/ src / bitmessageqt / settings.py
settings.py
  1  """
  2  This module setting file is for settings
  3  """
  4  import configparser
  5  import os
  6  import sys
  7  import tempfile
  8  
  9  import six
 10  from PyQt4 import QtCore, QtGui
 11  
 12  import debug
 13  import defaults
 14  import namecoin
 15  import openclpow
 16  import paths
 17  import queues
 18  import state
 19  from . import widgets
 20  from bmconfigparser import config as config_obj
 21  from helper_sql import sqlExecute, sqlStoredProcedure
 22  from helper_startup import start_proxyconfig
 23  from network import connectionpool, knownnodes
 24  from network.announcethread import AnnounceThread
 25  from network.asyncore_pollchoose import set_rates
 26  from tr import _translate
 27  
 28  
 29  def getSOCKSProxyType(config):
 30      """Get user socksproxytype setting from *config*"""
 31      try:
 32          result = configparser.SafeConfigParser.get(
 33              config, 'bitmessagesettings', 'socksproxytype')
 34      except (configparser.NoSectionError, configparser.NoOptionError):
 35          return None
 36      else:
 37          if result.lower() in ('', 'none', 'false'):
 38              result = None
 39      return result
 40  
 41  
 42  class SettingsDialog(QtGui.QDialog):
 43      """The "Settings" dialog"""
 44      # pylint: disable=too-many-instance-attributes
 45      def __init__(self, parent=None, firstrun=False):
 46          super(SettingsDialog, self).__init__(parent)
 47          widgets.load('settings.ui', self)
 48  
 49          self.app = QtGui.QApplication.instance()
 50          self.parent = parent
 51          self.firstrun = firstrun
 52          self.config = config_obj
 53          self.net_restart_needed = False
 54          self.font_setting = None
 55          self.timer = QtCore.QTimer()
 56  
 57          if self.config.safeGetBoolean('bitmessagesettings', 'dontconnect'):
 58              self.firstrun = False
 59          try:
 60              import pkg_resources
 61          except ImportError:
 62              pass
 63          else:
 64              # Append proxy types defined in plugins
 65              # FIXME: this should be a function in mod:`plugin`
 66              for ep in pkg_resources.iter_entry_points(
 67                      'bitmessage.proxyconfig'):
 68                  try:
 69                      ep.load()
 70                  except Exception:  # it should add only functional plugins
 71                      # many possible exceptions, which are don't matter
 72                      pass
 73                  else:
 74                      self.comboBoxProxyType.addItem(ep.name)
 75  
 76          self.lineEditMaxOutboundConnections.setValidator(
 77              QtGui.QIntValidator(0, 8, self.lineEditMaxOutboundConnections))
 78  
 79          self.adjust_from_config(self.config)
 80          if firstrun:
 81              # switch to "Network Settings" tab if user selected
 82              # "Let me configure special network settings first" on first run
 83              self.tabWidgetSettings.setCurrentIndex(
 84                  self.tabWidgetSettings.indexOf(self.tabNetworkSettings)
 85              )
 86          QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self))
 87  
 88      def adjust_from_config(self, config):
 89          """Adjust all widgets state according to config settings"""
 90          # pylint: disable=too-many-branches,too-many-statements
 91  
 92          current_style = self.app.get_windowstyle()
 93          for i, sk in enumerate(QtGui.QStyleFactory.keys()):
 94              self.comboBoxStyle.addItem(sk)
 95              if sk == current_style:
 96                  self.comboBoxStyle.setCurrentIndex(i)
 97  
 98          self.save_font_setting(self.app.font())
 99  
100          if not self.parent.tray.isSystemTrayAvailable():
101              self.groupBoxTray.setEnabled(False)
102              self.groupBoxTray.setTitle(_translate(
103                  "MainWindow", "Tray (not available in your system)"))
104              for setting in (
105                      'minimizetotray', 'trayonclose', 'startintray'):
106                  config.set('bitmessagesettings', setting, 'false')
107          else:
108              self.checkBoxMinimizeToTray.setChecked(
109                  config.getboolean('bitmessagesettings', 'minimizetotray'))
110              self.checkBoxTrayOnClose.setChecked(
111                  config.safeGetBoolean('bitmessagesettings', 'trayonclose'))
112              self.checkBoxStartInTray.setChecked(
113                  config.getboolean('bitmessagesettings', 'startintray'))
114  
115          self.checkBoxHideTrayConnectionNotifications.setChecked(
116              config.getboolean(
117                  'bitmessagesettings', 'hidetrayconnectionnotifications'))
118          self.checkBoxShowTrayNotifications.setChecked(
119              config.getboolean('bitmessagesettings', 'showtraynotifications'))
120  
121          self.checkBoxStartOnLogon.setChecked(
122              config.getboolean('bitmessagesettings', 'startonlogon'))
123  
124          self.checkBoxWillinglySendToMobile.setChecked(
125              config.safeGetBoolean(
126                  'bitmessagesettings', 'willinglysendtomobile'))
127          self.checkBoxUseIdenticons.setChecked(
128              config.safeGetBoolean('bitmessagesettings', 'useidenticons'))
129          self.checkBoxReplyBelow.setChecked(
130              config.safeGetBoolean('bitmessagesettings', 'replybelow'))
131  
132          if state.appdata == paths.lookupExeFolder():
133              self.checkBoxPortableMode.setChecked(True)
134          else:
135              try:
136                  tempfile.NamedTemporaryFile(
137                      dir=paths.lookupExeFolder(), delete=True
138                  ).close()  # should autodelete
139              except Exception:
140                  self.checkBoxPortableMode.setDisabled(True)
141  
142          if 'darwin' in sys.platform:
143              self.checkBoxMinimizeToTray.setDisabled(True)
144              self.checkBoxMinimizeToTray.setText(_translate(
145                  "MainWindow",
146                  "Minimize-to-tray not yet supported on your OS."))
147              self.checkBoxShowTrayNotifications.setDisabled(True)
148              self.checkBoxShowTrayNotifications.setText(_translate(
149                  "MainWindow",
150                  "Tray notifications not yet supported on your OS."))
151  
152          if not sys.platform.startswith('win') and not self.parent.desktop:
153              self.checkBoxStartOnLogon.setDisabled(True)
154              self.checkBoxStartOnLogon.setText(_translate(
155                  "MainWindow", "Start-on-login not yet supported on your OS."))
156  
157          # On the Network settings tab:
158          self.lineEditTCPPort.setText(str(
159              config.get('bitmessagesettings', 'port')))
160          self.checkBoxUPnP.setChecked(
161              config.safeGetBoolean('bitmessagesettings', 'upnp'))
162          self.checkBoxUDP.setChecked(
163              config.safeGetBoolean('bitmessagesettings', 'udp'))
164          self.checkBoxAuthentication.setChecked(
165              config.getboolean('bitmessagesettings', 'socksauthentication'))
166          self.checkBoxSocksListen.setChecked(
167              config.getboolean('bitmessagesettings', 'sockslisten'))
168          self.checkBoxOnionOnly.setChecked(
169              config.safeGetBoolean('bitmessagesettings', 'onionservicesonly'))
170  
171          self._proxy_type = getSOCKSProxyType(config)
172          self.comboBoxProxyType.setCurrentIndex(
173              0 if not self._proxy_type
174              else self.comboBoxProxyType.findText(self._proxy_type))
175          self.comboBoxProxyTypeChanged(self.comboBoxProxyType.currentIndex())
176  
177          if self._proxy_type:
178              for node, info in six.iteritems(
179                  knownnodes.knownNodes.get(
180                      min(connectionpool.pool.streams), [])
181              ):
182                  if (
183                      node.host.endswith('.onion') and len(node.host) > 22
184                      and not info.get('self')
185                  ):
186                      break
187              else:
188                  if self.checkBoxOnionOnly.isChecked():
189                      self.checkBoxOnionOnly.setText(
190                          self.checkBoxOnionOnly.text() + ", " + _translate(
191                              "MainWindow", "may cause connection problems!"))
192                      self.checkBoxOnionOnly.setStyleSheet(
193                          "QCheckBox { color : red; }")
194                  else:
195                      self.checkBoxOnionOnly.setEnabled(False)
196  
197          self.lineEditSocksHostname.setText(
198              config.get('bitmessagesettings', 'sockshostname'))
199          self.lineEditSocksPort.setText(str(
200              config.get('bitmessagesettings', 'socksport')))
201          self.lineEditSocksUsername.setText(
202              config.get('bitmessagesettings', 'socksusername'))
203          self.lineEditSocksPassword.setText(
204              config.get('bitmessagesettings', 'sockspassword'))
205  
206          self.lineEditMaxDownloadRate.setText(str(
207              config.get('bitmessagesettings', 'maxdownloadrate')))
208          self.lineEditMaxUploadRate.setText(str(
209              config.get('bitmessagesettings', 'maxuploadrate')))
210          self.lineEditMaxOutboundConnections.setText(str(
211              config.get('bitmessagesettings', 'maxoutboundconnections')))
212  
213          # Demanded difficulty tab
214          self.lineEditTotalDifficulty.setText(str((float(
215              config.getint(
216                  'bitmessagesettings', 'defaultnoncetrialsperbyte')
217          ) / defaults.networkDefaultProofOfWorkNonceTrialsPerByte)))
218          self.lineEditSmallMessageDifficulty.setText(str((float(
219              config.getint(
220                  'bitmessagesettings', 'defaultpayloadlengthextrabytes')
221          ) / defaults.networkDefaultPayloadLengthExtraBytes)))
222  
223          # Max acceptable difficulty tab
224          self.lineEditMaxAcceptableTotalDifficulty.setText(str((float(
225              config.getint(
226                  'bitmessagesettings', 'maxacceptablenoncetrialsperbyte')
227          ) / defaults.networkDefaultProofOfWorkNonceTrialsPerByte)))
228          self.lineEditMaxAcceptableSmallMessageDifficulty.setText(str((float(
229              config.getint(
230                  'bitmessagesettings', 'maxacceptablepayloadlengthextrabytes')
231          ) / defaults.networkDefaultPayloadLengthExtraBytes)))
232  
233          # OpenCL
234          self.comboBoxOpenCL.setEnabled(openclpow.openclAvailable())
235          self.comboBoxOpenCL.clear()
236          self.comboBoxOpenCL.addItem("None")
237          self.comboBoxOpenCL.addItems(openclpow.vendors)
238          self.comboBoxOpenCL.setCurrentIndex(0)
239          for i in range(self.comboBoxOpenCL.count()):
240              if self.comboBoxOpenCL.itemText(i) == config.safeGet(
241                      'bitmessagesettings', 'opencl'):
242                  self.comboBoxOpenCL.setCurrentIndex(i)
243                  break
244  
245          # Namecoin integration tab
246          nmctype = config.get('bitmessagesettings', 'namecoinrpctype')
247          self.lineEditNamecoinHost.setText(
248              config.get('bitmessagesettings', 'namecoinrpchost'))
249          self.lineEditNamecoinPort.setText(str(
250              config.get('bitmessagesettings', 'namecoinrpcport')))
251          self.lineEditNamecoinUser.setText(
252              config.get('bitmessagesettings', 'namecoinrpcuser'))
253          self.lineEditNamecoinPassword.setText(
254              config.get('bitmessagesettings', 'namecoinrpcpassword'))
255  
256          if nmctype == "namecoind":
257              self.radioButtonNamecoinNamecoind.setChecked(True)
258          elif nmctype == "nmcontrol":
259              self.radioButtonNamecoinNmcontrol.setChecked(True)
260              self.lineEditNamecoinUser.setEnabled(False)
261              self.labelNamecoinUser.setEnabled(False)
262              self.lineEditNamecoinPassword.setEnabled(False)
263              self.labelNamecoinPassword.setEnabled(False)
264          else:
265              assert False
266  
267          # Message Resend tab
268          self.lineEditDays.setText(str(
269              config.get('bitmessagesettings', 'stopresendingafterxdays')))
270          self.lineEditMonths.setText(str(
271              config.get('bitmessagesettings', 'stopresendingafterxmonths')))
272  
273      def comboBoxProxyTypeChanged(self, comboBoxIndex):
274          """A callback for currentIndexChanged event of comboBoxProxyType"""
275          if comboBoxIndex == 0:
276              self.lineEditSocksHostname.setEnabled(False)
277              self.lineEditSocksPort.setEnabled(False)
278              self.lineEditSocksUsername.setEnabled(False)
279              self.lineEditSocksPassword.setEnabled(False)
280              self.checkBoxAuthentication.setEnabled(False)
281              self.checkBoxSocksListen.setEnabled(False)
282              self.checkBoxOnionOnly.setEnabled(False)
283          else:
284              self.lineEditSocksHostname.setEnabled(True)
285              self.lineEditSocksPort.setEnabled(True)
286              self.checkBoxAuthentication.setEnabled(True)
287              self.checkBoxSocksListen.setEnabled(True)
288              self.checkBoxOnionOnly.setEnabled(True)
289              if self.checkBoxAuthentication.isChecked():
290                  self.lineEditSocksUsername.setEnabled(True)
291                  self.lineEditSocksPassword.setEnabled(True)
292  
293      def getNamecoinType(self):
294          """
295          Check status of namecoin integration radio buttons
296          and translate it to a string as in the options.
297          """
298          if self.radioButtonNamecoinNamecoind.isChecked():
299              return "namecoind"
300          if self.radioButtonNamecoinNmcontrol.isChecked():
301              return "nmcontrol"
302          assert False
303  
304      # Namecoin connection type was changed.
305      def namecoinTypeChanged(self, checked):  # pylint: disable=unused-argument
306          """A callback for toggled event of radioButtonNamecoinNamecoind"""
307          nmctype = self.getNamecoinType()
308          assert nmctype == "namecoind" or nmctype == "nmcontrol"
309  
310          isNamecoind = (nmctype == "namecoind")
311          self.lineEditNamecoinUser.setEnabled(isNamecoind)
312          self.labelNamecoinUser.setEnabled(isNamecoind)
313          self.lineEditNamecoinPassword.setEnabled(isNamecoind)
314          self.labelNamecoinPassword.setEnabled(isNamecoind)
315  
316          if isNamecoind:
317              self.lineEditNamecoinPort.setText(defaults.namecoinDefaultRpcPort)
318          else:
319              self.lineEditNamecoinPort.setText("9000")
320  
321      def click_pushButtonNamecoinTest(self):
322          """Test the namecoin settings specified in the settings dialog."""
323          self.labelNamecoinTestResult.setText(
324              _translate("MainWindow", "Testing..."))
325          nc = namecoin.namecoinConnection({
326              'type': self.getNamecoinType(),
327              'host': str(self.lineEditNamecoinHost.text().toUtf8()),
328              'port': str(self.lineEditNamecoinPort.text().toUtf8()),
329              'user': str(self.lineEditNamecoinUser.text().toUtf8()),
330              'password': str(self.lineEditNamecoinPassword.text().toUtf8())
331          })
332          status, text = nc.test()
333          self.labelNamecoinTestResult.setText(text)
334          if status == 'success':
335              self.parent.namecoin = nc
336  
337      def save_font_setting(self, font):
338          """Save user font setting and set the buttonFont text"""
339          font_setting = (font.family(), font.pointSize())
340          self.buttonFont.setText('{} {}'.format(*font_setting))
341          self.font_setting = '{},{}'.format(*font_setting)
342  
343      def choose_font(self):
344          """Show the font selection dialog"""
345          font, valid = QtGui.QFontDialog.getFont()
346          if valid:
347              self.save_font_setting(font)
348  
349      def accept(self):
350          """A callback for accepted event of buttonBox (OK button pressed)"""
351          # pylint: disable=too-many-branches,too-many-statements
352          super(SettingsDialog, self).accept()
353          if self.firstrun:
354              self.config.remove_option('bitmessagesettings', 'dontconnect')
355          self.config.set('bitmessagesettings', 'startonlogon', str(
356              self.checkBoxStartOnLogon.isChecked()))
357          self.config.set('bitmessagesettings', 'minimizetotray', str(
358              self.checkBoxMinimizeToTray.isChecked()))
359          self.config.set('bitmessagesettings', 'trayonclose', str(
360              self.checkBoxTrayOnClose.isChecked()))
361          self.config.set(
362              'bitmessagesettings', 'hidetrayconnectionnotifications',
363              str(self.checkBoxHideTrayConnectionNotifications.isChecked()))
364          self.config.set('bitmessagesettings', 'showtraynotifications', str(
365              self.checkBoxShowTrayNotifications.isChecked()))
366          self.config.set('bitmessagesettings', 'startintray', str(
367              self.checkBoxStartInTray.isChecked()))
368          self.config.set('bitmessagesettings', 'willinglysendtomobile', str(
369              self.checkBoxWillinglySendToMobile.isChecked()))
370          self.config.set('bitmessagesettings', 'useidenticons', str(
371              self.checkBoxUseIdenticons.isChecked()))
372          self.config.set('bitmessagesettings', 'replybelow', str(
373              self.checkBoxReplyBelow.isChecked()))
374  
375          window_style = str(self.comboBoxStyle.currentText())
376          if self.app.get_windowstyle() != window_style or self.config.safeGet(
377              'bitmessagesettings', 'font'
378          ) != self.font_setting:
379              self.config.set('bitmessagesettings', 'windowstyle', window_style)
380              self.config.set('bitmessagesettings', 'font', self.font_setting)
381              queues.UISignalQueue.put((
382                  'updateStatusBar', (
383                      _translate(
384                          "MainWindow",
385                          "You need to restart the application to apply"
386                          " the window style or default font."), 1)
387              ))
388  
389          lang = str(self.languageComboBox.itemData(
390              self.languageComboBox.currentIndex()).toString())
391          self.config.set('bitmessagesettings', 'userlocale', lang)
392          self.parent.change_translation()
393  
394          if int(self.config.get('bitmessagesettings', 'port')) != int(
395                  self.lineEditTCPPort.text()):
396              self.config.set(
397                  'bitmessagesettings', 'port', str(self.lineEditTCPPort.text()))
398              if not self.config.safeGetBoolean(
399                      'bitmessagesettings', 'dontconnect'):
400                  self.net_restart_needed = True
401  
402          if self.checkBoxUPnP.isChecked() != self.config.safeGetBoolean(
403                  'bitmessagesettings', 'upnp'):
404              self.config.set(
405                  'bitmessagesettings', 'upnp',
406                  str(self.checkBoxUPnP.isChecked()))
407              if self.checkBoxUPnP.isChecked():
408                  import upnp
409                  upnpThread = upnp.uPnPThread()
410                  upnpThread.start()
411  
412          udp_enabled = self.checkBoxUDP.isChecked()
413          if udp_enabled != self.config.safeGetBoolean(
414                  'bitmessagesettings', 'udp'):
415              self.config.set('bitmessagesettings', 'udp', str(udp_enabled))
416              if udp_enabled:
417                  announceThread = AnnounceThread()
418                  announceThread.daemon = True
419                  announceThread.start()
420              else:
421                  try:
422                      state.announceThread.stopThread()
423                  except AttributeError:
424                      pass
425  
426          proxytype_index = self.comboBoxProxyType.currentIndex()
427          if proxytype_index == 0:
428              if self._proxy_type and state.statusIconColor != 'red':
429                  self.net_restart_needed = True
430          elif state.statusIconColor == 'red' and self.config.safeGetBoolean(
431                  'bitmessagesettings', 'dontconnect'):
432              self.net_restart_needed = False
433          elif self.comboBoxProxyType.currentText() != self._proxy_type:
434              self.net_restart_needed = True
435              self.parent.statusbar.clearMessage()
436  
437          self.config.set(
438              'bitmessagesettings', 'socksproxytype',
439              'none' if self.comboBoxProxyType.currentIndex() == 0
440              else str(self.comboBoxProxyType.currentText())
441          )
442          if proxytype_index > 2:  # last literal proxytype in ui
443              start_proxyconfig()
444  
445          self.config.set('bitmessagesettings', 'socksauthentication', str(
446              self.checkBoxAuthentication.isChecked()))
447          self.config.set('bitmessagesettings', 'sockshostname', str(
448              self.lineEditSocksHostname.text()))
449          self.config.set('bitmessagesettings', 'socksport', str(
450              self.lineEditSocksPort.text()))
451          self.config.set('bitmessagesettings', 'socksusername', str(
452              self.lineEditSocksUsername.text()))
453          self.config.set('bitmessagesettings', 'sockspassword', str(
454              self.lineEditSocksPassword.text()))
455          self.config.set('bitmessagesettings', 'sockslisten', str(
456              self.checkBoxSocksListen.isChecked()))
457          if (
458              self.checkBoxOnionOnly.isChecked()
459              and not self.config.safeGetBoolean(
460                  'bitmessagesettings', 'onionservicesonly')
461          ):
462              self.net_restart_needed = True
463          self.config.set('bitmessagesettings', 'onionservicesonly', str(
464              self.checkBoxOnionOnly.isChecked()))
465          try:
466              # Rounding to integers just for aesthetics
467              self.config.set('bitmessagesettings', 'maxdownloadrate', str(
468                  int(float(self.lineEditMaxDownloadRate.text()))))
469              self.config.set('bitmessagesettings', 'maxuploadrate', str(
470                  int(float(self.lineEditMaxUploadRate.text()))))
471          except ValueError:
472              QtGui.QMessageBox.about(
473                  self, _translate("MainWindow", "Number needed"),
474                  _translate(
475                      "MainWindow",
476                      "Your maximum download and upload rate must be numbers."
477                      " Ignoring what you typed.")
478              )
479          else:
480              set_rates(
481                  self.config.safeGetInt('bitmessagesettings', 'maxdownloadrate'),
482                  self.config.safeGetInt('bitmessagesettings', 'maxuploadrate'))
483  
484          self.config.set('bitmessagesettings', 'maxoutboundconnections', str(
485              int(float(self.lineEditMaxOutboundConnections.text()))))
486  
487          self.config.set(
488              'bitmessagesettings', 'namecoinrpctype', self.getNamecoinType())
489          self.config.set('bitmessagesettings', 'namecoinrpchost', str(
490              self.lineEditNamecoinHost.text()))
491          self.config.set('bitmessagesettings', 'namecoinrpcport', str(
492              self.lineEditNamecoinPort.text()))
493          self.config.set('bitmessagesettings', 'namecoinrpcuser', str(
494              self.lineEditNamecoinUser.text()))
495          self.config.set('bitmessagesettings', 'namecoinrpcpassword', str(
496              self.lineEditNamecoinPassword.text()))
497          self.parent.resetNamecoinConnection()
498  
499          # Demanded difficulty tab
500          if float(self.lineEditTotalDifficulty.text()) >= 1:
501              self.config.set(
502                  'bitmessagesettings', 'defaultnoncetrialsperbyte',
503                  str(int(
504                      float(self.lineEditTotalDifficulty.text())
505                      * defaults.networkDefaultProofOfWorkNonceTrialsPerByte)))
506          if float(self.lineEditSmallMessageDifficulty.text()) >= 1:
507              self.config.set(
508                  'bitmessagesettings', 'defaultpayloadlengthextrabytes',
509                  str(int(
510                      float(self.lineEditSmallMessageDifficulty.text())
511                      * defaults.networkDefaultPayloadLengthExtraBytes)))
512  
513          if self.comboBoxOpenCL.currentText().toUtf8() != self.config.safeGet(
514                  'bitmessagesettings', 'opencl'):
515              self.config.set(
516                  'bitmessagesettings', 'opencl',
517                  str(self.comboBoxOpenCL.currentText()))
518              queues.workerQueue.put(('resetPoW', ''))
519  
520          acceptableDifficultyChanged = False
521  
522          if (
523              float(self.lineEditMaxAcceptableTotalDifficulty.text()) >= 1
524              or float(self.lineEditMaxAcceptableTotalDifficulty.text()) == 0
525          ):
526              if self.config.get(
527                      'bitmessagesettings', 'maxacceptablenoncetrialsperbyte'
528              ) != str(int(
529                  float(self.lineEditMaxAcceptableTotalDifficulty.text())
530                      * defaults.networkDefaultProofOfWorkNonceTrialsPerByte)):
531                  # the user changed the max acceptable total difficulty
532                  acceptableDifficultyChanged = True
533                  self.config.set(
534                      'bitmessagesettings', 'maxacceptablenoncetrialsperbyte',
535                      str(int(
536                          float(self.lineEditMaxAcceptableTotalDifficulty.text())
537                          * defaults.networkDefaultProofOfWorkNonceTrialsPerByte))
538                  )
539          if (
540              float(self.lineEditMaxAcceptableSmallMessageDifficulty.text()) >= 1
541              or float(self.lineEditMaxAcceptableSmallMessageDifficulty.text()) == 0
542          ):
543              if self.config.get(
544                      'bitmessagesettings', 'maxacceptablepayloadlengthextrabytes'
545              ) != str(int(
546                  float(self.lineEditMaxAcceptableSmallMessageDifficulty.text())
547                      * defaults.networkDefaultPayloadLengthExtraBytes)):
548                  # the user changed the max acceptable small message difficulty
549                  acceptableDifficultyChanged = True
550                  self.config.set(
551                      'bitmessagesettings', 'maxacceptablepayloadlengthextrabytes',
552                      str(int(
553                          float(self.lineEditMaxAcceptableSmallMessageDifficulty.text())
554                          * defaults.networkDefaultPayloadLengthExtraBytes))
555                  )
556          if acceptableDifficultyChanged:
557              # It might now be possible to send msgs which were previously
558              # marked as toodifficult. Let us change them to 'msgqueued'.
559              # The singleWorker will try to send them and will again mark
560              # them as toodifficult if the receiver's required difficulty
561              # is still higher than we are willing to do.
562              sqlExecute(
563                  "UPDATE sent SET status='msgqueued'"
564                  " WHERE status='toodifficult'")
565              queues.workerQueue.put(('sendmessage', ''))
566  
567          stopResendingDefaults = False
568  
569          # UI setting to stop trying to send messages after X days/months
570          # I'm open to changing this UI to something else if someone has a better idea.
571          if self.lineEditDays.text() == '' and self.lineEditMonths.text() == '':
572              # We need to handle this special case. Bitmessage has its
573              # default behavior. The input is blank/blank
574              self.config.set('bitmessagesettings', 'stopresendingafterxdays', '')
575              self.config.set('bitmessagesettings', 'stopresendingafterxmonths', '')
576              state.maximumLengthOfTimeToBotherResendingMessages = float('inf')
577              stopResendingDefaults = True
578  
579          try:
580              days = float(self.lineEditDays.text())
581          except ValueError:
582              self.lineEditDays.setText("0")
583              days = 0.0
584          try:
585              months = float(self.lineEditMonths.text())
586          except ValueError:
587              self.lineEditMonths.setText("0")
588              months = 0.0
589  
590          if days >= 0 and months >= 0 and not stopResendingDefaults:
591              state.maximumLengthOfTimeToBotherResendingMessages = \
592                  days * 24 * 60 * 60 + months * 60 * 60 * 24 * 365 / 12
593              if state.maximumLengthOfTimeToBotherResendingMessages < 432000:
594                  # If the time period is less than 5 hours, we give
595                  # zero values to all fields. No message will be sent again.
596                  QtGui.QMessageBox.about(
597                      self,
598                      _translate("MainWindow", "Will not resend ever"),
599                      _translate(
600                          "MainWindow",
601                          "Note that the time limit you entered is less"
602                          " than the amount of time Bitmessage waits for"
603                          " the first resend attempt therefore your"
604                          " messages will never be resent.")
605                  )
606                  self.config.set(
607                      'bitmessagesettings', 'stopresendingafterxdays', '0')
608                  self.config.set(
609                      'bitmessagesettings', 'stopresendingafterxmonths', '0')
610                  state.maximumLengthOfTimeToBotherResendingMessages = 0.0
611              else:
612                  self.config.set(
613                      'bitmessagesettings', 'stopresendingafterxdays', str(days))
614                  self.config.set(
615                      'bitmessagesettings', 'stopresendingafterxmonths',
616                      str(months))
617  
618          self.config.save()
619  
620          if self.net_restart_needed:
621              self.net_restart_needed = False
622              self.config.setTemp('bitmessagesettings', 'dontconnect', 'true')
623              self.timer.singleShot(
624                  5000, lambda:
625                  self.config.setTemp(
626                      'bitmessagesettings', 'dontconnect', 'false')
627              )
628  
629          self.parent.updateStartOnLogon()
630  
631          if (
632              state.appdata != paths.lookupExeFolder()
633              and self.checkBoxPortableMode.isChecked()
634          ):
635              # If we are NOT using portable mode now but the user selected
636              # that we should...
637              # Write the keys.dat file to disk in the new location
638              sqlStoredProcedure('movemessagstoprog')
639              with open(paths.lookupExeFolder() + 'keys.dat', 'wb') as configfile:
640                  self.config.write(configfile)
641              # Write the knownnodes.dat file to disk in the new location
642              knownnodes.saveKnownNodes(paths.lookupExeFolder())
643              os.remove(state.appdata + 'keys.dat')
644              os.remove(state.appdata + 'knownnodes.dat')
645              previousAppdataLocation = state.appdata
646              state.appdata = paths.lookupExeFolder()
647              debug.resetLogging()
648              try:
649                  os.remove(previousAppdataLocation + 'debug.log')
650                  os.remove(previousAppdataLocation + 'debug.log.1')
651              except Exception:
652                  pass
653  
654          if (
655              state.appdata == paths.lookupExeFolder()
656              and not self.checkBoxPortableMode.isChecked()
657          ):
658              # If we ARE using portable mode now but the user selected
659              # that we shouldn't...
660              state.appdata = paths.lookupAppdataFolder()
661              if not os.path.exists(state.appdata):
662                  os.makedirs(state.appdata)
663              sqlStoredProcedure('movemessagstoappdata')
664              # Write the keys.dat file to disk in the new location
665              self.config.save()
666              # Write the knownnodes.dat file to disk in the new location
667              knownnodes.saveKnownNodes(state.appdata)
668              os.remove(paths.lookupExeFolder() + 'keys.dat')
669              os.remove(paths.lookupExeFolder() + 'knownnodes.dat')
670              debug.resetLogging()
671              try:
672                  os.remove(paths.lookupExeFolder() + 'debug.log')
673                  os.remove(paths.lookupExeFolder() + 'debug.log.1')
674              except Exception:
675                  pass