Merge branch 'v0.6' of https://github.com/surbhicis/PyBitmessage into UiChanges

This commit is contained in:
surbhi 2019-09-30 19:08:14 +05:30
commit a3bbcdd795
No known key found for this signature in database
GPG Key ID: 88928762974D3618
43 changed files with 1876 additions and 2585 deletions

View File

@ -6,6 +6,7 @@ addons:
packages: packages:
- build-essential - build-essential
- libcap-dev - libcap-dev
- tor
install: install:
- pip install -r requirements.txt - pip install -r requirements.txt
- ln -s src pybitmessage # tests environment - ln -s src pybitmessage # tests environment

View File

@ -1,3 +1,4 @@
python_prctl python_prctl
psutil psutil
pycrypto pycrypto
stem

View File

@ -70,7 +70,6 @@ if __name__ == "__main__":
'pybitmessage.network', 'pybitmessage.network',
'pybitmessage.plugins', 'pybitmessage.plugins',
'pybitmessage.pyelliptic', 'pybitmessage.pyelliptic',
'pybitmessage.socks',
'pybitmessage.storage' 'pybitmessage.storage'
] ]

View File

@ -1,3 +1,8 @@
"""
src/bitmessagecurses/__init__.py
================================
"""
# Copyright (c) 2014 Luke Montalvo <lukemontalvo@gmail.com> # Copyright (c) 2014 Luke Montalvo <lukemontalvo@gmail.com>
# This file adds a alternative commandline interface, feel free to critique and fork # This file adds a alternative commandline interface, feel free to critique and fork
# #
@ -7,34 +12,29 @@
# * python2-pythondialog # * python2-pythondialog
# * dialog # * dialog
import ConfigParser
import curses
import os import os
import sys import sys
import StringIO
from textwrap import *
import time import time
from time import strftime, localtime from textwrap import fill
from threading import Timer from threading import Timer
import curses from addresses import addBMIfNotPresent, decodeAddress
import dialog
from dialog import Dialog
from helper_sql import *
from helper_ackPayload import genAckPayload
from addresses import *
import ConfigParser
from bmconfigparser import BMConfigParser from bmconfigparser import BMConfigParser
from dialog import Dialog
from helper_ackPayload import genAckPayload
from helper_sql import sqlExecute, sqlQuery
from inventory import Inventory from inventory import Inventory
import l10n import l10n
import network.stats
from pyelliptic.openssl import OpenSSL from pyelliptic.openssl import OpenSSL
import queues import queues
import shared import shared
import shutdown import shutdown
import network.stats
quit = False quit = False # pylint: disable=redefined-builtin
menutab = 1 menutab = 1
menu = ["Inbox", "Send", "Sent", "Your Identities", "Subscriptions", "Address Book", "Blacklist", "Network Status"] menu = ["Inbox", "Send", "Sent", "Your Identities", "Subscriptions", "Address Book", "Blacklist", "Network Status"]
naptime = 100 naptime = 100
@ -60,156 +60,189 @@ bwtype = "black"
BROADCAST_STR = "[Broadcast subscribers]" BROADCAST_STR = "[Broadcast subscribers]"
class printLog:
class printLog: # pylint: disable=no-self-use, no-init, old-style-class
"""Printing logs"""
def write(self, output): def write(self, output):
# pylint: disable=global-statement
global log global log
log += output log += output
def flush(self): def flush(self):
pass pass
class errLog:
class errLog: # pylint: disable=no-self-use, no-init, old-style-class
"""Error logs"""
def write(self, output): def write(self, output):
# pylint: disable=global-statement
global log global log
log += "!"+output log += "!" + output
def flush(self): def flush(self):
pass pass
printlog = printLog() printlog = printLog()
errlog = errLog() errlog = errLog()
def cpair(a): def cpair(a):
"""Color pairs"""
r = curses.color_pair(a) r = curses.color_pair(a)
if r not in range(1, curses.COLOR_PAIRS-1): if r not in range(1, curses.COLOR_PAIRS - 1):
r = curses.color_pair(0) r = curses.color_pair(0)
return r return r
def ascii(s): def ascii(s):
"""ASCII values"""
r = "" r = ""
for c in s: for c in s:
if ord(c) in range(128): if ord(c) in range(128):
r += c r += c
return r return r
def drawmenu(stdscr): def drawmenu(stdscr):
"""Creating menu's"""
menustr = " " menustr = " "
for i in range(0, len(menu)): for i, _ in enumerate(menu):
if menutab == i+1: if menutab == i + 1:
menustr = menustr[:-1] menustr = menustr[:-1]
menustr += "[" menustr += "["
menustr += str(i+1)+menu[i] menustr += str(i + 1) + menu[i]
if menutab == i+1: if menutab == i + 1:
menustr += "] " menustr += "] "
elif i != len(menu)-1: elif i != len(menu) - 1:
menustr += " " menustr += " "
stdscr.addstr(2, 5, menustr, curses.A_UNDERLINE) stdscr.addstr(2, 5, menustr, curses.A_UNDERLINE)
def set_background_title(d, title): def set_background_title(d, title):
"""Setting background title"""
try: try:
d.set_background_title(title) d.set_background_title(title)
except: except:
d.add_persistent_args(("--backtitle", title)) d.add_persistent_args(("--backtitle", title))
def scrollbox(d, text, height=None, width=None): def scrollbox(d, text, height=None, width=None):
"""Setting scroll box"""
try: try:
d.scrollbox(text, height, width, exit_label = "Continue") d.scrollbox(text, height, width, exit_label="Continue")
except: except:
d.msgbox(text, height or 0, width or 0, ok_label = "Continue") d.msgbox(text, height or 0, width or 0, ok_label="Continue")
def resetlookups(): def resetlookups():
global inventorydata """Reset the Inventory Lookups"""
global inventorydata # pylint: disable=global-statement
inventorydata = Inventory().numberOfInventoryLookupsPerformed inventorydata = Inventory().numberOfInventoryLookupsPerformed
Inventory().numberOfInventoryLookupsPerformed = 0 Inventory().numberOfInventoryLookupsPerformed = 0
Timer(1, resetlookups, ()).start() Timer(1, resetlookups, ()).start()
def drawtab(stdscr):
if menutab in range(1, len(menu)+1):
if menutab == 1: # Inbox def drawtab(stdscr): # pylint: disable=too-many-branches, too-many-statements
"""Method for drawing different tabs"""
if menutab in range(1, len(menu) + 1):
if menutab == 1: # Inbox
stdscr.addstr(3, 5, "To", curses.A_BOLD) stdscr.addstr(3, 5, "To", curses.A_BOLD)
stdscr.addstr(3, 40, "From", curses.A_BOLD) stdscr.addstr(3, 40, "From", curses.A_BOLD)
stdscr.addstr(3, 80, "Subject", curses.A_BOLD) stdscr.addstr(3, 80, "Subject", curses.A_BOLD)
stdscr.addstr(3, 120, "Time Received", curses.A_BOLD) stdscr.addstr(3, 120, "Time Received", curses.A_BOLD)
stdscr.hline(4, 5, '-', 121) stdscr.hline(4, 5, '-', 121)
for i, item in enumerate(inbox[max(min(len(inbox)-curses.LINES+6, inboxcur-5), 0):]): for i, item in enumerate(inbox[max(min(len(inbox) - curses.LINES + 6, inboxcur - 5), 0):]):
if 6+i < curses.LINES: if 6 + i < curses.LINES:
a = 0 a = 0
if i == inboxcur - max(min(len(inbox)-curses.LINES+6, inboxcur-5), 0): # Highlight current address if i == inboxcur - max(min(len(inbox) - curses.LINES + 6, inboxcur - 5), 0):
# Highlight current address
a = a | curses.A_REVERSE a = a | curses.A_REVERSE
if item[7] == False: # If not read, highlight if item[7] is False: # If not read, highlight
a = a | curses.A_BOLD a = a | curses.A_BOLD
stdscr.addstr(5+i, 5, item[1][:34], a) stdscr.addstr(5 + i, 5, item[1][:34], a)
stdscr.addstr(5+i, 40, item[3][:39], a) stdscr.addstr(5 + i, 40, item[3][:39], a)
stdscr.addstr(5+i, 80, item[5][:39], a) stdscr.addstr(5 + i, 80, item[5][:39], a)
stdscr.addstr(5+i, 120, item[6][:39], a) stdscr.addstr(5 + i, 120, item[6][:39], a)
elif menutab == 3: # Sent elif menutab == 3: # Sent
stdscr.addstr(3, 5, "To", curses.A_BOLD) stdscr.addstr(3, 5, "To", curses.A_BOLD)
stdscr.addstr(3, 40, "From", curses.A_BOLD) stdscr.addstr(3, 40, "From", curses.A_BOLD)
stdscr.addstr(3, 80, "Subject", curses.A_BOLD) stdscr.addstr(3, 80, "Subject", curses.A_BOLD)
stdscr.addstr(3, 120, "Status", curses.A_BOLD) stdscr.addstr(3, 120, "Status", curses.A_BOLD)
stdscr.hline(4, 5, '-', 121) stdscr.hline(4, 5, '-', 121)
for i, item in enumerate(sentbox[max(min(len(sentbox)-curses.LINES+6, sentcur-5), 0):]): for i, item in enumerate(sentbox[max(min(len(sentbox) - curses.LINES + 6, sentcur - 5), 0):]):
if 6+i < curses.LINES: if 6 + i < curses.LINES:
a = 0 a = 0
if i == sentcur - max(min(len(sentbox)-curses.LINES+6, sentcur-5), 0): # Highlight current address if i == sentcur - max(min(len(sentbox) - curses.LINES + 6, sentcur - 5), 0):
# Highlight current address
a = a | curses.A_REVERSE a = a | curses.A_REVERSE
stdscr.addstr(5+i, 5, item[0][:34], a) stdscr.addstr(5 + i, 5, item[0][:34], a)
stdscr.addstr(5+i, 40, item[2][:39], a) stdscr.addstr(5 + i, 40, item[2][:39], a)
stdscr.addstr(5+i, 80, item[4][:39], a) stdscr.addstr(5 + i, 80, item[4][:39], a)
stdscr.addstr(5+i, 120, item[5][:39], a) stdscr.addstr(5 + i, 120, item[5][:39], a)
elif menutab == 2 or menutab == 4: # Send or Identities elif menutab == 2 or menutab == 4: # Send or Identities
stdscr.addstr(3, 5, "Label", curses.A_BOLD) stdscr.addstr(3, 5, "Label", curses.A_BOLD)
stdscr.addstr(3, 40, "Address", curses.A_BOLD) stdscr.addstr(3, 40, "Address", curses.A_BOLD)
stdscr.addstr(3, 80, "Stream", curses.A_BOLD) stdscr.addstr(3, 80, "Stream", curses.A_BOLD)
stdscr.hline(4, 5, '-', 81) stdscr.hline(4, 5, '-', 81)
for i, item in enumerate(addresses[max(min(len(addresses)-curses.LINES+6, addrcur-5), 0):]): for i, item in enumerate(addresses[max(min(len(addresses) - curses.LINES + 6, addrcur - 5), 0):]):
if 6+i < curses.LINES: if 6 + i < curses.LINES:
a = 0 a = 0
if i == addrcur - max(min(len(addresses)-curses.LINES+6, addrcur-5), 0): # Highlight current address if i == addrcur - max(min(len(addresses) - curses.LINES + 6, addrcur - 5), 0):
# Highlight current address
a = a | curses.A_REVERSE a = a | curses.A_REVERSE
if item[1] == True and item[3] not in [8,9]: # Embolden enabled, non-special addresses if item[1] and item[3] not in [8, 9]: # Embolden enabled, non-special addresses
a = a | curses.A_BOLD a = a | curses.A_BOLD
stdscr.addstr(5+i, 5, item[0][:34], a) stdscr.addstr(5 + i, 5, item[0][:34], a)
stdscr.addstr(5+i, 40, item[2][:39], cpair(item[3]) | a) stdscr.addstr(5 + i, 40, item[2][:39], cpair(item[3]) | a)
stdscr.addstr(5+i, 80, str(1)[:39], a) stdscr.addstr(5 + i, 80, str(1)[:39], a)
elif menutab == 5: # Subscriptions elif menutab == 5: # Subscriptions
stdscr.addstr(3, 5, "Label", curses.A_BOLD) stdscr.addstr(3, 5, "Label", curses.A_BOLD)
stdscr.addstr(3, 80, "Address", curses.A_BOLD) stdscr.addstr(3, 80, "Address", curses.A_BOLD)
stdscr.addstr(3, 120, "Enabled", curses.A_BOLD) stdscr.addstr(3, 120, "Enabled", curses.A_BOLD)
stdscr.hline(4, 5, '-', 121) stdscr.hline(4, 5, '-', 121)
for i, item in enumerate(subscriptions[max(min(len(subscriptions)-curses.LINES+6, subcur-5), 0):]): for i, item in enumerate(subscriptions[max(min(len(subscriptions) - curses.LINES + 6, subcur - 5), 0):]):
if 6+i < curses.LINES: if 6 + i < curses.LINES:
a = 0 a = 0
if i == subcur - max(min(len(subscriptions)-curses.LINES+6, subcur-5), 0): # Highlight current address if i == subcur - max(min(len(subscriptions) - curses.LINES + 6, subcur - 5), 0):
# Highlight current address
a = a | curses.A_REVERSE a = a | curses.A_REVERSE
if item[2] == True: # Embolden enabled subscriptions if item[2]: # Embolden enabled subscriptions
a = a | curses.A_BOLD a = a | curses.A_BOLD
stdscr.addstr(5+i, 5, item[0][:74], a) stdscr.addstr(5 + i, 5, item[0][:74], a)
stdscr.addstr(5+i, 80, item[1][:39], a) stdscr.addstr(5 + i, 80, item[1][:39], a)
stdscr.addstr(5+i, 120, str(item[2]), a) stdscr.addstr(5 + i, 120, str(item[2]), a)
elif menutab == 6: # Address book elif menutab == 6: # Address book
stdscr.addstr(3, 5, "Label", curses.A_BOLD) stdscr.addstr(3, 5, "Label", curses.A_BOLD)
stdscr.addstr(3, 40, "Address", curses.A_BOLD) stdscr.addstr(3, 40, "Address", curses.A_BOLD)
stdscr.hline(4, 5, '-', 41) stdscr.hline(4, 5, '-', 41)
for i, item in enumerate(addrbook[max(min(len(addrbook)-curses.LINES+6, abookcur-5), 0):]): for i, item in enumerate(addrbook[max(min(len(addrbook) - curses.LINES + 6, abookcur - 5), 0):]):
if 6+i < curses.LINES: if 6 + i < curses.LINES:
a = 0 a = 0
if i == abookcur - max(min(len(addrbook)-curses.LINES+6, abookcur-5), 0): # Highlight current address if i == abookcur - max(min(len(addrbook) - curses.LINES + 6, abookcur - 5), 0):
# Highlight current address
a = a | curses.A_REVERSE a = a | curses.A_REVERSE
stdscr.addstr(5+i, 5, item[0][:34], a) stdscr.addstr(5 + i, 5, item[0][:34], a)
stdscr.addstr(5+i, 40, item[1][:39], a) stdscr.addstr(5 + i, 40, item[1][:39], a)
elif menutab == 7: # Blacklist elif menutab == 7: # Blacklist
stdscr.addstr(3, 5, "Type: "+bwtype) stdscr.addstr(3, 5, "Type: " + bwtype)
stdscr.addstr(4, 5, "Label", curses.A_BOLD) stdscr.addstr(4, 5, "Label", curses.A_BOLD)
stdscr.addstr(4, 80, "Address", curses.A_BOLD) stdscr.addstr(4, 80, "Address", curses.A_BOLD)
stdscr.addstr(4, 120, "Enabled", curses.A_BOLD) stdscr.addstr(4, 120, "Enabled", curses.A_BOLD)
stdscr.hline(5, 5, '-', 121) stdscr.hline(5, 5, '-', 121)
for i, item in enumerate(blacklist[max(min(len(blacklist)-curses.LINES+6, blackcur-5), 0):]): for i, item in enumerate(blacklist[max(min(len(blacklist) - curses.LINES + 6, blackcur - 5), 0):]):
if 7+i < curses.LINES: if 7 + i < curses.LINES:
a = 0 a = 0
if i == blackcur - max(min(len(blacklist)-curses.LINES+6, blackcur-5), 0): # Highlight current address if i == blackcur - max(min(len(blacklist) - curses.LINES + 6, blackcur - 5), 0):
# Highlight current address
a = a | curses.A_REVERSE a = a | curses.A_REVERSE
if item[2] == True: # Embolden enabled subscriptions if item[2]: # Embolden enabled subscriptions
a = a | curses.A_BOLD a = a | curses.A_BOLD
stdscr.addstr(6+i, 5, item[0][:74], a) stdscr.addstr(6 + i, 5, item[0][:74], a)
stdscr.addstr(6+i, 80, item[1][:39], a) stdscr.addstr(6 + i, 80, item[1][:39], a)
stdscr.addstr(6+i, 120, str(item[2]), a) stdscr.addstr(6 + i, 120, str(item[2]), a)
elif menutab == 8: # Network status elif menutab == 8: # Network status
# Connection data # Connection data
connected_hosts = network.stats.connectedHostsList() connected_hosts = network.stats.connectedHostsList()
stdscr.addstr( stdscr.addstr(
@ -228,19 +261,22 @@ def drawtab(stdscr):
for i, item in enumerate(streamcount): for i, item in enumerate(streamcount):
if i < 4: if i < 4:
if i == 0: if i == 0:
stdscr.addstr(8+i, 6, "?") stdscr.addstr(8 + i, 6, "?")
else: else:
stdscr.addstr(8+i, 6, str(i)) stdscr.addstr(8 + i, 6, str(i))
stdscr.addstr(8+i, 18, str(item).ljust(2)) stdscr.addstr(8 + i, 18, str(item).ljust(2))
# Uptime and processing data # Uptime and processing data
stdscr.addstr(6, 35, "Since startup on "+l10n.formatTimestamp(startuptime, False)) stdscr.addstr(6, 35, "Since startup on " + l10n.formatTimestamp(startuptime, False))
stdscr.addstr(7, 40, "Processed "+str(shared.numberOfMessagesProcessed).ljust(4)+" person-to-person messages.") stdscr.addstr(7, 40, "Processed " + str(
stdscr.addstr(8, 40, "Processed "+str(shared.numberOfBroadcastsProcessed).ljust(4)+" broadcast messages.") shared.numberOfMessagesProcessed).ljust(4) + " person-to-person messages.")
stdscr.addstr(9, 40, "Processed "+str(shared.numberOfPubkeysProcessed).ljust(4)+" public keys.") stdscr.addstr(8, 40, "Processed " + str(
shared.numberOfBroadcastsProcessed).ljust(4) + " broadcast messages.")
stdscr.addstr(9, 40, "Processed " + str(
shared.numberOfPubkeysProcessed).ljust(4) + " public keys.")
# Inventory data # Inventory data
stdscr.addstr(11, 35, "Inventory lookups per second: "+str(inventorydata).ljust(3)) stdscr.addstr(11, 35, "Inventory lookups per second: " + str(inventorydata).ljust(3))
# Log # Log
stdscr.addstr(13, 6, "Log", curses.A_BOLD) stdscr.addstr(13, 6, "Log", curses.A_BOLD)
@ -248,28 +284,37 @@ def drawtab(stdscr):
if n > 0: if n > 0:
l = log.split('\n') l = log.split('\n')
if n > 512: if n > 512:
del l[:(n-256)] del l[:(n - 256)]
logpad.erase() logpad.erase()
n = len(l) n = len(l)
for i, item in enumerate(l): for i, item in enumerate(l):
a = 0 a = 0
if len(item) > 0 and item[0] == '!': if item and item[0] == '!':
a = curses.color_pair(1) a = curses.color_pair(1)
item = item[1:] item = item[1:]
logpad.addstr(i, 0, item, a) logpad.addstr(i, 0, item, a)
logpad.refresh(n-curses.LINES+2, 0, 14, 6, curses.LINES-2, curses.COLS-7) logpad.refresh(n - curses.LINES + 2, 0, 14, 6, curses.LINES - 2, curses.COLS - 7)
stdscr.refresh() stdscr.refresh()
def redraw(stdscr): def redraw(stdscr):
"""Redraw menu"""
stdscr.erase() stdscr.erase()
stdscr.border() stdscr.border()
drawmenu(stdscr) drawmenu(stdscr)
stdscr.refresh() stdscr.refresh()
def dialogreset(stdscr): def dialogreset(stdscr):
"""Resetting dialogue"""
stdscr.clear() stdscr.clear()
stdscr.keypad(1) stdscr.keypad(1)
curses.curs_set(0) curses.curs_set(0)
# pylint: disable=too-many-branches, too-many-statements
def handlech(c, stdscr): def handlech(c, stdscr):
# pylint: disable=redefined-outer-name, too-many-nested-blocks, too-many-locals, global-statement
if c != curses.ERR: if c != curses.ERR:
global inboxcur, addrcur, sentcur, subcur, abookcur, blackcur global inboxcur, addrcur, sentcur, subcur, abookcur, blackcur
if c in range(256): if c in range(256):
@ -284,17 +329,27 @@ def handlech(c, stdscr):
d = Dialog(dialog="dialog") d = Dialog(dialog="dialog")
if menutab == 1: if menutab == 1:
set_background_title(d, "Inbox Message Dialog Box") set_background_title(d, "Inbox Message Dialog Box")
r, t = d.menu("Do what with \""+inbox[inboxcur][5]+"\" from \""+inbox[inboxcur][3]+"\"?", r, t = d.menu(
choices=[("1", "View message"), "Do what with \"" + inbox[inboxcur][5] + "\" from \"" + inbox[inboxcur][3] + "\"?",
choices=[
("1", "View message"),
("2", "Mark message as unread"), ("2", "Mark message as unread"),
("3", "Reply"), ("3", "Reply"),
("4", "Add sender to Address Book"), ("4", "Add sender to Address Book"),
("5", "Save message as text file"), ("5", "Save message as text file"),
("6", "Move to trash")]) ("6", "Move to trash")])
if r == d.DIALOG_OK: if r == d.DIALOG_OK:
if t == "1": # View if t == "1": # View
set_background_title(d, "\""+inbox[inboxcur][5]+"\" from \""+inbox[inboxcur][3]+"\" to \""+inbox[inboxcur][1]+"\"") set_background_title(
data = "" d,
"\"" +
inbox[inboxcur][5] +
"\" from \"" +
inbox[inboxcur][3] +
"\" to \"" +
inbox[inboxcur][1] +
"\"")
data = "" # pyint: disable=redefined-outer-name
ret = sqlQuery("SELECT message FROM inbox WHERE msgid=?", inbox[inboxcur][0]) ret = sqlQuery("SELECT message FROM inbox WHERE msgid=?", inbox[inboxcur][0])
if ret != []: if ret != []:
for row in ret: for row in ret:
@ -302,16 +357,16 @@ def handlech(c, stdscr):
data = shared.fixPotentiallyInvalidUTF8Data(data) data = shared.fixPotentiallyInvalidUTF8Data(data)
msg = "" msg = ""
for i, item in enumerate(data.split("\n")): for i, item in enumerate(data.split("\n")):
msg += fill(item, replace_whitespace=False)+"\n" msg += fill(item, replace_whitespace=False) + "\n"
scrollbox(d, unicode(ascii(msg)), 30, 80) scrollbox(d, unicode(ascii(msg)), 30, 80)
sqlExecute("UPDATE inbox SET read=1 WHERE msgid=?", inbox[inboxcur][0]) sqlExecute("UPDATE inbox SET read=1 WHERE msgid=?", inbox[inboxcur][0])
inbox[inboxcur][7] = 1 inbox[inboxcur][7] = 1
else: else:
scrollbox(d, unicode("Could not fetch message.")) scrollbox(d, unicode("Could not fetch message."))
elif t == "2": # Mark unread elif t == "2": # Mark unread
sqlExecute("UPDATE inbox SET read=0 WHERE msgid=?", inbox[inboxcur][0]) sqlExecute("UPDATE inbox SET read=0 WHERE msgid=?", inbox[inboxcur][0])
inbox[inboxcur][7] = 0 inbox[inboxcur][7] = 0
elif t == "3": # Reply elif t == "3": # Reply
curses.curs_set(1) curses.curs_set(1)
m = inbox[inboxcur] m = inbox[inboxcur]
fromaddr = m[4] fromaddr = m[4]
@ -320,8 +375,10 @@ def handlech(c, stdscr):
if fromaddr == item[2] and item[3] != 0: if fromaddr == item[2] and item[3] != 0:
ischan = True ischan = True
break break
if not addresses[i][1]: if not addresses[i][1]: # pylint: disable=undefined-loop-variable
scrollbox(d, unicode("Sending address disabled, please either enable it or choose a different address.")) scrollbox(d, unicode(
"Sending address disabled, please either enable it"
"or choose a different address."))
return return
toaddr = m[2] toaddr = m[2]
if ischan: if ischan:
@ -329,7 +386,7 @@ def handlech(c, stdscr):
subject = m[5] subject = m[5]
if not m[5][:4] == "Re: ": if not m[5][:4] == "Re: ":
subject = "Re: "+m[5] subject = "Re: " + m[5]
body = "" body = ""
ret = sqlQuery("SELECT message FROM inbox WHERE msgid=?", m[0]) ret = sqlQuery("SELECT message FROM inbox WHERE msgid=?", m[0])
if ret != []: if ret != []:
@ -339,10 +396,10 @@ def handlech(c, stdscr):
sendMessage(fromaddr, toaddr, ischan, subject, body, True) sendMessage(fromaddr, toaddr, ischan, subject, body, True)
dialogreset(stdscr) dialogreset(stdscr)
elif t == "4": # Add to Address Book elif t == "4": # Add to Address Book
addr = inbox[inboxcur][4] addr = inbox[inboxcur][4]
if addr not in [item[1] for i,item in enumerate(addrbook)]: if addr not in [item[1] for i, item in enumerate(addrbook)]:
r, t = d.inputbox("Label for address \""+addr+"\"") r, t = d.inputbox("Label for address \"" + addr + "\"")
if r == d.DIALOG_OK: if r == d.DIALOG_OK:
label = t label = t
sqlExecute("INSERT INTO addressbook VALUES (?,?)", label, addr) sqlExecute("INSERT INTO addressbook VALUES (?,?)", label, addr)
@ -352,61 +409,85 @@ def handlech(c, stdscr):
addrbook.reverse() addrbook.reverse()
else: else:
scrollbox(d, unicode("The selected address is already in the Address Book.")) scrollbox(d, unicode("The selected address is already in the Address Book."))
elif t == "5": # Save message elif t == "5": # Save message
set_background_title(d, "Save \""+inbox[inboxcur][5]+"\" as text file") set_background_title(d, "Save \"" + inbox[inboxcur][5] + "\" as text file")
r, t = d.inputbox("Filename", init=inbox[inboxcur][5]+".txt") r, t = d.inputbox("Filename", init=inbox[inboxcur][5] + ".txt")
if r == d.DIALOG_OK: if r == d.DIALOG_OK:
msg = "" msg = ""
ret = sqlQuery("SELECT message FROM inbox WHERE msgid=?", inbox[inboxcur][0]) ret = sqlQuery("SELECT message FROM inbox WHERE msgid=?", inbox[inboxcur][0])
if ret != []: if ret != []:
for row in ret: for row in ret:
msg, = row msg, = row
fh = open(t, "a") # Open in append mode just in case fh = open(t, "a") # Open in append mode just in case
fh.write(msg) fh.write(msg)
fh.close() fh.close()
else: else:
scrollbox(d, unicode("Could not fetch message.")) scrollbox(d, unicode("Could not fetch message."))
elif t == "6": # Move to trash elif t == "6": # Move to trash
sqlExecute("UPDATE inbox SET folder='trash' WHERE msgid=?", inbox[inboxcur][0]) sqlExecute("UPDATE inbox SET folder='trash' WHERE msgid=?", inbox[inboxcur][0])
del inbox[inboxcur] del inbox[inboxcur]
scrollbox(d, unicode("Message moved to trash. There is no interface to view your trash, \nbut the message is still on disk if you are desperate to recover it.")) scrollbox(d, unicode(
"Message moved to trash. There is no interface to view your trash,"
" \nbut the message is still on disk if you are desperate to recover it."))
elif menutab == 2: elif menutab == 2:
a = "" a = ""
if addresses[addrcur][3] != 0: # if current address is a chan if addresses[addrcur][3] != 0: # if current address is a chan
a = addresses[addrcur][2] a = addresses[addrcur][2]
sendMessage(addresses[addrcur][2], a) sendMessage(addresses[addrcur][2], a)
elif menutab == 3: elif menutab == 3:
set_background_title(d, "Sent Messages Dialog Box") set_background_title(d, "Sent Messages Dialog Box")
r, t = d.menu("Do what with \""+sentbox[sentcur][4]+"\" to \""+sentbox[sentcur][0]+"\"?", r, t = d.menu(
choices=[("1", "View message"), "Do what with \"" + sentbox[sentcur][4] + "\" to \"" + sentbox[sentcur][0] + "\"?",
choices=[
("1", "View message"),
("2", "Move to trash")]) ("2", "Move to trash")])
if r == d.DIALOG_OK: if r == d.DIALOG_OK:
if t == "1": # View if t == "1": # View
set_background_title(d, "\""+sentbox[sentcur][4]+"\" from \""+sentbox[sentcur][3]+"\" to \""+sentbox[sentcur][1]+"\"") set_background_title(
d,
"\"" +
sentbox[sentcur][4] +
"\" from \"" +
sentbox[sentcur][3] +
"\" to \"" +
sentbox[sentcur][1] +
"\"")
data = "" data = ""
ret = sqlQuery("SELECT message FROM sent WHERE subject=? AND ackdata=?", sentbox[sentcur][4], sentbox[sentcur][6]) ret = sqlQuery(
"SELECT message FROM sent WHERE subject=? AND ackdata=?",
sentbox[sentcur][4],
sentbox[sentcur][6])
if ret != []: if ret != []:
for row in ret: for row in ret:
data, = row data, = row
data = shared.fixPotentiallyInvalidUTF8Data(data) data = shared.fixPotentiallyInvalidUTF8Data(data)
msg = "" msg = ""
for i, item in enumerate(data.split("\n")): for i, item in enumerate(data.split("\n")):
msg += fill(item, replace_whitespace=False)+"\n" msg += fill(item, replace_whitespace=False) + "\n"
scrollbox(d, unicode(ascii(msg)), 30, 80) scrollbox(d, unicode(ascii(msg)), 30, 80)
else: else:
scrollbox(d, unicode("Could not fetch message.")) scrollbox(d, unicode("Could not fetch message."))
elif t == "2": # Move to trash elif t == "2": # Move to trash
sqlExecute("UPDATE sent SET folder='trash' WHERE subject=? AND ackdata=?", sentbox[sentcur][4], sentbox[sentcur][6]) sqlExecute(
"UPDATE sent SET folder='trash' WHERE subject=? AND ackdata=?",
sentbox[sentcur][4],
sentbox[sentcur][6])
del sentbox[sentcur] del sentbox[sentcur]
scrollbox(d, unicode("Message moved to trash. There is no interface to view your trash, \nbut the message is still on disk if you are desperate to recover it.")) scrollbox(d, unicode(
"Message moved to trash. There is no interface to view your trash"
" \nbut the message is still on disk if you are desperate to recover it."))
elif menutab == 4: elif menutab == 4:
set_background_title(d, "Your Identities Dialog Box") set_background_title(d, "Your Identities Dialog Box")
if len(addresses) <= addrcur: if len(addresses) <= addrcur:
r, t = d.menu("Do what with addresses?", r, t = d.menu(
choices=[("1", "Create new address")]) "Do what with addresses?",
choices=[
("1", "Create new address")])
else: else:
r, t = d.menu("Do what with \""+addresses[addrcur][0]+"\" : \""+addresses[addrcur][2]+"\"?", r, t = d.menu(
choices=[("1", "Create new address"), "Do what with \"" + addresses[addrcur][0] + "\" : \"" + addresses[addrcur][2] + "\"?",
choices=[
("1", "Create new address"),
("2", "Send a message from this address"), ("2", "Send a message from this address"),
("3", "Rename"), ("3", "Rename"),
("4", "Enable"), ("4", "Enable"),
@ -414,31 +495,41 @@ def handlech(c, stdscr):
("6", "Delete"), ("6", "Delete"),
("7", "Special address behavior")]) ("7", "Special address behavior")])
if r == d.DIALOG_OK: if r == d.DIALOG_OK:
if t == "1": # Create new address if t == "1": # Create new address
set_background_title(d, "Create new address") set_background_title(d, "Create new address")
scrollbox(d, unicode("Here you may generate as many addresses as you like.\n" scrollbox(
"Indeed, creating and abandoning addresses is encouraged.\n" d, unicode(
"Deterministic addresses have several pros and cons:\n" "Here you may generate as many addresses as you like.\n"
"\nPros:\n" "Indeed, creating and abandoning addresses is encouraged.\n"
" * You can recreate your addresses on any computer from memory\n" "Deterministic addresses have several pros and cons:\n"
" * You need not worry about backing up your keys.dat file as long as you \n can remember your passphrase\n" "\nPros:\n"
"Cons:\n" " * You can recreate your addresses on any computer from memory\n"
" * You must remember (or write down) your passphrase in order to recreate \n your keys if they are lost\n" " * You need not worry about backing up your keys.dat file as long as you"
" * You must also remember the address version and stream numbers\n" " \n can remember your passphrase\n"
" * If you choose a weak passphrase someone may be able to brute-force it \n and then send and receive messages as you")) "Cons:\n"
r, t = d.menu("Choose an address generation technique", " * You must remember (or write down) your passphrase in order to recreate"
choices=[("1", "Use a random number generator"), " \n your keys if they are lost\n"
" * You must also remember the address version and stream numbers\n"
" * If you choose a weak passphrase someone may be able to brute-force it"
" \n and then send and receive messages as you"))
r, t = d.menu(
"Choose an address generation technique",
choices=[
("1", "Use a random number generator"),
("2", "Use a passphrase")]) ("2", "Use a passphrase")])
if r == d.DIALOG_OK: if r == d.DIALOG_OK:
if t == "1": if t == "1":
set_background_title(d, "Randomly generate address") set_background_title(d, "Randomly generate address")
r, t = d.inputbox("Label (not shown to anyone except you)") r, t = d.inputbox("Label (not shown to anyone except you)")
label = "" label = ""
if r == d.DIALOG_OK and len(t) > 0: if r == d.DIALOG_OK and t:
label = t label = t
r, t = d.menu("Choose a stream", r, t = d.menu(
choices=[("1", "Use the most available stream"),("", "(Best if this is the first of many addresses you will create)"), "Choose a stream",
("2", "Use the same stream as an existing address"),("", "(Saves you some bandwidth and processing power)")]) choices=[("1", "Use the most available stream"),
("", "(Best if this is the first of many addresses you will create)"),
("2", "Use the same stream as an existing address"),
("", "(Saves you some bandwidth and processing power)")])
if r == d.DIALOG_OK: if r == d.DIALOG_OK:
if t == "1": if t == "1":
stream = 1 stream = 1
@ -450,42 +541,69 @@ def handlech(c, stdscr):
if r == d.DIALOG_OK: if r == d.DIALOG_OK:
stream = decodeAddress(addrs[int(t)][1])[2] stream = decodeAddress(addrs[int(t)][1])[2]
shorten = False shorten = False
r, t = d.checklist("Miscellaneous options", r, t = d.checklist(
choices=[("1", "Spend time shortening the address", 1 if shorten else 0)]) "Miscellaneous options",
choices=[(
"1",
"Spend time shortening the address",
1 if shorten else 0)])
if r == d.DIALOG_OK and "1" in t: if r == d.DIALOG_OK and "1" in t:
shorten = True shorten = True
queues.addressGeneratorQueue.put(("createRandomAddress", 4, stream, label, 1, "", shorten)) queues.addressGeneratorQueue.put((
"createRandomAddress",
4,
stream,
label,
1,
"",
shorten))
elif t == "2": elif t == "2":
set_background_title(d, "Make deterministic addresses") set_background_title(d, "Make deterministic addresses")
r, t = d.passwordform("Enter passphrase", r, t = d.passwordform(
[("Passphrase", 1, 1, "", 2, 1, 64, 128), "Enter passphrase",
("Confirm passphrase", 3, 1, "", 4, 1, 64, 128)], [
("Passphrase", 1, 1, "", 2, 1, 64, 128),
("Confirm passphrase", 3, 1, "", 4, 1, 64, 128)],
form_height=4, insecure=True) form_height=4, insecure=True)
if r == d.DIALOG_OK: if r == d.DIALOG_OK:
if t[0] == t[1]: if t[0] == t[1]:
passphrase = t[0] passphrase = t[0]
r, t = d.rangebox("Number of addresses to generate", r, t = d.rangebox(
width=48, min=1, max=99, init=8) "Number of addresses to generate",
width=48,
min=1,
max=99,
init=8)
if r == d.DIALOG_OK: if r == d.DIALOG_OK:
number = t number = t
stream = 1 stream = 1
shorten = False shorten = False
r, t = d.checklist("Miscellaneous options", r, t = d.checklist(
choices=[("1", "Spend time shortening the address", 1 if shorten else 0)]) "Miscellaneous options",
choices=[(
"1",
"Spend time shortening the address",
1 if shorten else 0)])
if r == d.DIALOG_OK and "1" in t: if r == d.DIALOG_OK and "1" in t:
shorten = True shorten = True
scrollbox(d, unicode("In addition to your passphrase, be sure to remember the following numbers:\n" scrollbox(
"\n * Address version number: "+str(4)+"\n" d, unicode(
" * Stream number: "+str(stream))) "In addition to your passphrase, be sure to remember the"
queues.addressGeneratorQueue.put(('createDeterministicAddresses', 4, stream, "unused deterministic address", number, str(passphrase), shorten)) " following numbers:\n"
"\n * Address version number: " + str(4) + "\n"
" * Stream number: " + str(stream)))
queues.addressGeneratorQueue.put(
('createDeterministicAddresses', 4, stream,
"unused deterministic address", number,
str(passphrase), shorten))
else: else:
scrollbox(d, unicode("Passphrases do not match")) scrollbox(d, unicode("Passphrases do not match"))
elif t == "2": # Send a message elif t == "2": # Send a message
a = "" a = ""
if addresses[addrcur][3] != 0: # if current address is a chan if addresses[addrcur][3] != 0: # if current address is a chan
a = addresses[addrcur][2] a = addresses[addrcur][2]
sendMessage(addresses[addrcur][2], a) sendMessage(addresses[addrcur][2], a)
elif t == "3": # Rename address label elif t == "3": # Rename address label
a = addresses[addrcur][2] a = addresses[addrcur][2]
label = addresses[addrcur][0] label = addresses[addrcur][0]
r, t = d.inputbox("New address label", init=label) r, t = d.inputbox("New address label", init=label)
@ -495,72 +613,79 @@ def handlech(c, stdscr):
# Write config # Write config
BMConfigParser().save() BMConfigParser().save()
addresses[addrcur][0] = label addresses[addrcur][0] = label
elif t == "4": # Enable address elif t == "4": # Enable address
a = addresses[addrcur][2] a = addresses[addrcur][2]
BMConfigParser().set(a, "enabled", "true") # Set config BMConfigParser().set(a, "enabled", "true") # Set config
# Write config # Write config
BMConfigParser().save() BMConfigParser().save()
# Change color # Change color
if BMConfigParser().safeGetBoolean(a, 'chan'): if BMConfigParser().safeGetBoolean(a, 'chan'):
addresses[addrcur][3] = 9 # orange addresses[addrcur][3] = 9 # orange
elif BMConfigParser().safeGetBoolean(a, 'mailinglist'): elif BMConfigParser().safeGetBoolean(a, 'mailinglist'):
addresses[addrcur][3] = 5 # magenta addresses[addrcur][3] = 5 # magenta
else: else:
addresses[addrcur][3] = 0 # black addresses[addrcur][3] = 0 # black
addresses[addrcur][1] = True addresses[addrcur][1] = True
shared.reloadMyAddressHashes() # Reload address hashes shared.reloadMyAddressHashes() # Reload address hashes
elif t == "5": # Disable address elif t == "5": # Disable address
a = addresses[addrcur][2] a = addresses[addrcur][2]
BMConfigParser().set(a, "enabled", "false") # Set config BMConfigParser().set(a, "enabled", "false") # Set config
addresses[addrcur][3] = 8 # Set color to gray addresses[addrcur][3] = 8 # Set color to gray
# Write config # Write config
BMConfigParser().save() BMConfigParser().save()
addresses[addrcur][1] = False addresses[addrcur][1] = False
shared.reloadMyAddressHashes() # Reload address hashes shared.reloadMyAddressHashes() # Reload address hashes
elif t == "6": # Delete address elif t == "6": # Delete address
r, t = d.inputbox("Type in \"I want to delete this address\"", width=50) r, t = d.inputbox("Type in \"I want to delete this address\"", width=50)
if r == d.DIALOG_OK and t == "I want to delete this address": if r == d.DIALOG_OK and t == "I want to delete this address":
BMConfigParser().remove_section(addresses[addrcur][2]) BMConfigParser().remove_section(addresses[addrcur][2])
BMConfigParser().save() BMConfigParser().save()
del addresses[addrcur] del addresses[addrcur]
elif t == "7": # Special address behavior elif t == "7": # Special address behavior
a = addresses[addrcur][2] a = addresses[addrcur][2]
set_background_title(d, "Special address behavior") set_background_title(d, "Special address behavior")
if BMConfigParser().safeGetBoolean(a, "chan"): if BMConfigParser().safeGetBoolean(a, "chan"):
scrollbox(d, unicode("This is a chan address. You cannot use it as a pseudo-mailing list.")) scrollbox(d, unicode(
"This is a chan address. You cannot use it as a pseudo-mailing list."))
else: else:
m = BMConfigParser().safeGetBoolean(a, "mailinglist") m = BMConfigParser().safeGetBoolean(a, "mailinglist")
r, t = d.radiolist("Select address behavior", r, t = d.radiolist(
choices=[("1", "Behave as a normal address", not m), "Select address behavior",
choices=[
("1", "Behave as a normal address", not m),
("2", "Behave as a pseudo-mailing-list address", m)]) ("2", "Behave as a pseudo-mailing-list address", m)])
if r == d.DIALOG_OK: if r == d.DIALOG_OK:
if t == "1" and m == True: if t == "1" and m:
BMConfigParser().set(a, "mailinglist", "false") BMConfigParser().set(a, "mailinglist", "false")
if addresses[addrcur][1]: if addresses[addrcur][1]:
addresses[addrcur][3] = 0 # Set color to black addresses[addrcur][3] = 0 # Set color to black
else: else:
addresses[addrcur][3] = 8 # Set color to gray addresses[addrcur][3] = 8 # Set color to gray
elif t == "2" and m == False: elif t == "2" and m is False:
try: try:
mn = BMConfigParser().get(a, "mailinglistname") mn = BMConfigParser().get(a, "mailinglistname")
except ConfigParser.NoOptionError: except ConfigParser.NoOptionError:
mn = "" mn = ""
r, t = d.inputbox("Mailing list name", init=mn) r, t = d.inputbox("Mailing list name", init=mn)
if r == d.DIALOG_OK: if r == d.DIALOG_OK:
mn = t mn = t
BMConfigParser().set(a, "mailinglist", "true") BMConfigParser().set(a, "mailinglist", "true")
BMConfigParser().set(a, "mailinglistname", mn) BMConfigParser().set(a, "mailinglistname", mn)
addresses[addrcur][3] = 6 # Set color to magenta addresses[addrcur][3] = 6 # Set color to magenta
# Write config # Write config
BMConfigParser().save() BMConfigParser().save()
elif menutab == 5: elif menutab == 5:
set_background_title(d, "Subscriptions Dialog Box") set_background_title(d, "Subscriptions Dialog Box")
if len(subscriptions) <= subcur: if len(subscriptions) <= subcur:
r, t = d.menu("Do what with subscription to \""+subscriptions[subcur][0]+"\"?", r, t = d.menu(
choices=[("1", "Add new subscription")]) "Do what with subscription to \"" + subscriptions[subcur][0] + "\"?",
choices=[
("1", "Add new subscription")])
else: else:
r, t = d.menu("Do what with subscription to \""+subscriptions[subcur][0]+"\"?", r, t = d.menu(
choices=[("1", "Add new subscription"), "Do what with subscription to \"" + subscriptions[subcur][0] + "\"?",
choices=[
("1", "Add new subscription"),
("2", "Delete this subscription"), ("2", "Delete this subscription"),
("3", "Enable"), ("3", "Enable"),
("4", "Disable")]) ("4", "Disable")])
@ -581,27 +706,39 @@ def handlech(c, stdscr):
sqlExecute("INSERT INTO subscriptions VALUES (?,?,?)", label, addr, True) sqlExecute("INSERT INTO subscriptions VALUES (?,?,?)", label, addr, True)
shared.reloadBroadcastSendersForWhichImWatching() shared.reloadBroadcastSendersForWhichImWatching()
elif t == "2": elif t == "2":
r, t = d.inpuxbox("Type in \"I want to delete this subscription\"") r, t = d.inputbox("Type in \"I want to delete this subscription\"")
if r == d.DIALOG_OK and t == "I want to delete this subscription": if r == d.DIALOG_OK and t == "I want to delete this subscription":
sqlExecute("DELETE FROM subscriptions WHERE label=? AND address=?", subscriptions[subcur][0], subscriptions[subcur][1]) sqlExecute(
shared.reloadBroadcastSendersForWhichImWatching() "DELETE FROM subscriptions WHERE label=? AND address=?",
del subscriptions[subcur] subscriptions[subcur][0],
subscriptions[subcur][1])
shared.reloadBroadcastSendersForWhichImWatching()
del subscriptions[subcur]
elif t == "3": elif t == "3":
sqlExecute("UPDATE subscriptions SET enabled=1 WHERE label=? AND address=?", subscriptions[subcur][0], subscriptions[subcur][1]) sqlExecute(
"UPDATE subscriptions SET enabled=1 WHERE label=? AND address=?",
subscriptions[subcur][0],
subscriptions[subcur][1])
shared.reloadBroadcastSendersForWhichImWatching() shared.reloadBroadcastSendersForWhichImWatching()
subscriptions[subcur][2] = True subscriptions[subcur][2] = True
elif t == "4": elif t == "4":
sqlExecute("UPDATE subscriptions SET enabled=0 WHERE label=? AND address=?", subscriptions[subcur][0], subscriptions[subcur][1]) sqlExecute(
"UPDATE subscriptions SET enabled=0 WHERE label=? AND address=?",
subscriptions[subcur][0],
subscriptions[subcur][1])
shared.reloadBroadcastSendersForWhichImWatching() shared.reloadBroadcastSendersForWhichImWatching()
subscriptions[subcur][2] = False subscriptions[subcur][2] = False
elif menutab == 6: elif menutab == 6:
set_background_title(d, "Address Book Dialog Box") set_background_title(d, "Address Book Dialog Box")
if len(addrbook) <= abookcur: if len(addrbook) <= abookcur:
r, t = d.menu("Do what with addressbook?", r, t = d.menu(
"Do what with addressbook?",
choices=[("3", "Add new address to Address Book")]) choices=[("3", "Add new address to Address Book")])
else: else:
r, t = d.menu("Do what with \""+addrbook[abookcur][0]+"\" : \""+addrbook[abookcur][1]+"\"", r, t = d.menu(
choices=[("1", "Send a message to this address"), "Do what with \"" + addrbook[abookcur][0] + "\" : \"" + addrbook[abookcur][1] + "\"",
choices=[
("1", "Send a message to this address"),
("2", "Subscribe to this address"), ("2", "Subscribe to this address"),
("3", "Add new address to Address Book"), ("3", "Add new address to Address Book"),
("4", "Delete this address")]) ("4", "Delete this address")])
@ -623,8 +760,8 @@ def handlech(c, stdscr):
r, t = d.inputbox("Input new address") r, t = d.inputbox("Input new address")
if r == d.DIALOG_OK: if r == d.DIALOG_OK:
addr = t addr = t
if addr not in [item[1] for i,item in enumerate(addrbook)]: if addr not in [item[1] for i, item in enumerate(addrbook)]:
r, t = d.inputbox("Label for address \""+addr+"\"") r, t = d.inputbox("Label for address \"" + addr + "\"")
if r == d.DIALOG_OK: if r == d.DIALOG_OK:
sqlExecute("INSERT INTO addressbook VALUES (?,?)", t, addr) sqlExecute("INSERT INTO addressbook VALUES (?,?)", t, addr)
# Prepend entry # Prepend entry
@ -636,25 +773,39 @@ def handlech(c, stdscr):
elif t == "4": elif t == "4":
r, t = d.inputbox("Type in \"I want to delete this Address Book entry\"") r, t = d.inputbox("Type in \"I want to delete this Address Book entry\"")
if r == d.DIALOG_OK and t == "I want to delete this Address Book entry": if r == d.DIALOG_OK and t == "I want to delete this Address Book entry":
sqlExecute("DELETE FROM addressbook WHERE label=? AND address=?", addrbook[abookcur][0], addrbook[abookcur][1]) sqlExecute(
"DELETE FROM addressbook WHERE label=? AND address=?",
addrbook[abookcur][0],
addrbook[abookcur][1])
del addrbook[abookcur] del addrbook[abookcur]
elif menutab == 7: elif menutab == 7:
set_background_title(d, "Blacklist Dialog Box") set_background_title(d, "Blacklist Dialog Box")
r, t = d.menu("Do what with \""+blacklist[blackcur][0]+"\" : \""+blacklist[blackcur][1]+"\"?", r, t = d.menu(
choices=[("1", "Delete"), "Do what with \"" + blacklist[blackcur][0] + "\" : \"" + blacklist[blackcur][1] + "\"?",
choices=[
("1", "Delete"),
("2", "Enable"), ("2", "Enable"),
("3", "Disable")]) ("3", "Disable")])
if r == d.DIALOG_OK: if r == d.DIALOG_OK:
if t == "1": if t == "1":
r, t = d.inputbox("Type in \"I want to delete this Blacklist entry\"") r, t = d.inputbox("Type in \"I want to delete this Blacklist entry\"")
if r == d.DIALOG_OK and t == "I want to delete this Blacklist entry": if r == d.DIALOG_OK and t == "I want to delete this Blacklist entry":
sqlExecute("DELETE FROM blacklist WHERE label=? AND address=?", blacklist[blackcur][0], blacklist[blackcur][1]) sqlExecute(
"DELETE FROM blacklist WHERE label=? AND address=?",
blacklist[blackcur][0],
blacklist[blackcur][1])
del blacklist[blackcur] del blacklist[blackcur]
elif t == "2": elif t == "2":
sqlExecute("UPDATE blacklist SET enabled=1 WHERE label=? AND address=?", blacklist[blackcur][0], blacklist[blackcur][1]) sqlExecute(
"UPDATE blacklist SET enabled=1 WHERE label=? AND address=?",
blacklist[blackcur][0],
blacklist[blackcur][1])
blacklist[blackcur][2] = True blacklist[blackcur][2] = True
elif t== "3": elif t == "3":
sqlExecute("UPDATE blacklist SET enabled=0 WHERE label=? AND address=?", blacklist[blackcur][0], blacklist[blackcur][1]) sqlExecute(
"UPDATE blacklist SET enabled=0 WHERE label=? AND address=?",
blacklist[blackcur][0],
blacklist[blackcur][1])
blacklist[blackcur][2] = False blacklist[blackcur][2] = False
dialogreset(stdscr) dialogreset(stdscr)
else: else:
@ -672,17 +823,17 @@ def handlech(c, stdscr):
if menutab == 7 and blackcur > 0: if menutab == 7 and blackcur > 0:
blackcur -= 1 blackcur -= 1
elif c == curses.KEY_DOWN: elif c == curses.KEY_DOWN:
if menutab == 1 and inboxcur < len(inbox)-1: if menutab == 1 and inboxcur < len(inbox) - 1:
inboxcur += 1 inboxcur += 1
if (menutab == 2 or menutab == 4) and addrcur < len(addresses)-1: if (menutab == 2 or menutab == 4) and addrcur < len(addresses) - 1:
addrcur += 1 addrcur += 1
if menutab == 3 and sentcur < len(sentbox)-1: if menutab == 3 and sentcur < len(sentbox) - 1:
sentcur += 1 sentcur += 1
if menutab == 5 and subcur < len(subscriptions)-1: if menutab == 5 and subcur < len(subscriptions) - 1:
subcur += 1 subcur += 1
if menutab == 6 and abookcur < len(addrbook)-1: if menutab == 6 and abookcur < len(addrbook) - 1:
abookcur += 1 abookcur += 1
if menutab == 7 and blackcur < len(blacklist)-1: if menutab == 7 and blackcur < len(blacklist) - 1:
blackcur += 1 blackcur += 1
elif c == curses.KEY_HOME: elif c == curses.KEY_HOME:
if menutab == 1: if menutab == 1:
@ -699,38 +850,47 @@ def handlech(c, stdscr):
blackcur = 0 blackcur = 0
elif c == curses.KEY_END: elif c == curses.KEY_END:
if menutab == 1: if menutab == 1:
inboxcur = len(inbox)-1 inboxcur = len(inbox) - 1
if menutab == 2 or menutab == 4: if menutab == 2 or menutab == 4:
addrcur = len(addresses)-1 addrcur = len(addresses) - 1
if menutab == 3: if menutab == 3:
sentcur = len(sentbox)-1 sentcur = len(sentbox) - 1
if menutab == 5: if menutab == 5:
subcur = len(subscriptions)-1 subcur = len(subscriptions) - 1
if menutab == 6: if menutab == 6:
abookcur = len(addrbook)-1 abookcur = len(addrbook) - 1
if menutab == 7: if menutab == 7:
blackcur = len(blackcur)-1 blackcur = len(blackcur) - 1
redraw(stdscr) redraw(stdscr)
# pylint: disable=too-many-locals, too-many-arguments
def sendMessage(sender="", recv="", broadcast=None, subject="", body="", reply=False): def sendMessage(sender="", recv="", broadcast=None, subject="", body="", reply=False):
"""Method for message sending"""
if sender == "": if sender == "":
return return
d = Dialog(dialog="dialog") d = Dialog(dialog="dialog")
set_background_title(d, "Send a message") set_background_title(d, "Send a message")
if recv == "": if recv == "":
r, t = d.inputbox("Recipient address (Cancel to load from the Address Book or leave blank to broadcast)", 10, 60) r, t = d.inputbox(
"Recipient address (Cancel to load from the Address Book or leave blank to broadcast)",
10,
60)
if r != d.DIALOG_OK: if r != d.DIALOG_OK:
global menutab global menutab # pylint: disable=global-statement
menutab = 6 menutab = 6
return return
recv = t recv = t
if broadcast == None and sender != recv: if broadcast is None and sender != recv:
r, t = d.radiolist("How to send the message?", r, t = d.radiolist(
choices=[("1", "Send to one or more specific people", 1), "How to send the message?",
choices=[
("1", "Send to one or more specific people", 1),
("2", "Broadcast to everyone who is subscribed to your address", 0)]) ("2", "Broadcast to everyone who is subscribed to your address", 0)])
if r != d.DIALOG_OK: if r != d.DIALOG_OK:
return return
broadcast = False broadcast = False
if t == "2": # Broadcast if t == "2": # Broadcast
broadcast = True broadcast = True
if subject == "" or reply: if subject == "" or reply:
r, t = d.inputbox("Message subject", width=60, init=subject) r, t = d.inputbox("Message subject", width=60, init=subject)
@ -748,9 +908,10 @@ def sendMessage(sender="", recv="", broadcast=None, subject="", body="", reply=F
recvlist = [] recvlist = []
for i, item in enumerate(recv.replace(",", ";").split(";")): for i, item in enumerate(recv.replace(",", ";").split(";")):
recvlist.append(item.strip()) recvlist.append(item.strip())
list(set(recvlist)) # Remove exact duplicates list(set(recvlist)) # Remove exact duplicates
for addr in recvlist: for addr in recvlist:
if addr != "": if addr != "":
# pylint: disable=redefined-outer-name
status, version, stream, ripe = decodeAddress(addr) status, version, stream, ripe = decodeAddress(addr)
if status != "success": if status != "success":
set_background_title(d, "Recipient address error") set_background_title(d, "Recipient address error")
@ -762,13 +923,17 @@ def sendMessage(sender="", recv="", broadcast=None, subject="", body="", reply=F
elif status == "invalidcharacters": elif status == "invalidcharacters":
err += "The address contains invalid characters." err += "The address contains invalid characters."
elif status == "versiontoohigh": elif status == "versiontoohigh":
err += "The address version is too high. Either you need to upgrade your Bitmessage software or your acquaintance is doing something clever." err += ("The address version is too high. Either you need to upgrade your Bitmessage software"
" or your acquaintance is doing something clever.")
elif status == "ripetooshort": elif status == "ripetooshort":
err += "Some data encoded in the address is too short. There might be something wrong with the software of your acquaintance." err += ("Some data encoded in the address is too short. There might be something wrong with"
" the software of your acquaintance.")
elif status == "ripetoolong": elif status == "ripetoolong":
err += "Some data encoded in the address is too long. There might be something wrong with the software of your acquaintance." err += ("Some data encoded in the address is too long. There might be something wrong with"
" the software of your acquaintance.")
elif status == "varintmalformed": elif status == "varintmalformed":
err += "Some data encoded in the address is malformed. There might be something wrong with the software of your acquaintance." err += ("Some data encoded in the address is malformed. There might be something wrong with"
" the software of your acquaintance.")
else: else:
err += "It is unknown what is wrong with the address." err += "It is unknown what is wrong with the address."
scrollbox(d, unicode(err)) scrollbox(d, unicode(err))
@ -776,17 +941,24 @@ def sendMessage(sender="", recv="", broadcast=None, subject="", body="", reply=F
addr = addBMIfNotPresent(addr) addr = addBMIfNotPresent(addr)
if version > 4 or version <= 1: if version > 4 or version <= 1:
set_background_title(d, "Recipient address error") set_background_title(d, "Recipient address error")
scrollbox(d, unicode("Could not understand version number " + version + "of address" + addr + ".")) scrollbox(d, unicode(
"Could not understand version number " +
version +
"of address" +
addr +
"."))
continue continue
if stream > 1 or stream == 0: if stream > 1 or stream == 0:
set_background_title(d, "Recipient address error") set_background_title(d, "Recipient address error")
scrollbox(d, unicode("Bitmessage currently only supports stream numbers of 1, unlike as requested for address " + addr + ".")) scrollbox(d, unicode(
"Bitmessage currently only supports stream numbers of 1,"
"unlike as requested for address " + addr + "."))
continue continue
if not network.stats.connectedHostsList(): if not network.stats.connectedHostsList():
set_background_title(d, "Not connected warning") set_background_title(d, "Not connected warning")
scrollbox(d, unicode("Because you are not currently connected to the network, ")) scrollbox(d, unicode("Because you are not currently connected to the network, "))
stealthLevel = BMConfigParser().safeGetInt('bitmessagesettings', 'ackstealthlevel') stealthLevel = BMConfigParser().safeGetInt('bitmessagesettings', 'ackstealthlevel')
ackdata = genAckPayload(streamNumber, stealthLevel) ackdata = genAckPayload(decodeAddress(addr)[2], stealthLevel)
sqlExecute( sqlExecute(
"INSERT INTO sent VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", "INSERT INTO sent VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
"", "",
@ -796,22 +968,22 @@ def sendMessage(sender="", recv="", broadcast=None, subject="", body="", reply=F
subject, subject,
body, body,
ackdata, ackdata,
int(time.time()), # sentTime (this will never change) int(time.time()), # sentTime (this will never change)
int(time.time()), # lastActionTime int(time.time()), # lastActionTime
0, # sleepTill time. This will get set when the POW gets done. 0, # sleepTill time. This will get set when the POW gets done.
"msgqueued", "msgqueued",
0, # retryNumber 0, # retryNumber
"sent", "sent",
2, # encodingType 2, # encodingType
BMConfigParser().getint('bitmessagesettings', 'ttl')) BMConfigParser().getint('bitmessagesettings', 'ttl'))
queues.workerQueue.put(("sendmessage", addr)) queues.workerQueue.put(("sendmessage", addr))
else: # Broadcast else: # Broadcast
if recv == "": if recv == "":
set_background_title(d, "Empty sender error") set_background_title(d, "Empty sender error")
scrollbox(d, unicode("You must specify an address to send the message from.")) scrollbox(d, unicode("You must specify an address to send the message from."))
else: else:
# dummy ackdata, no need for stealth # dummy ackdata, no need for stealth
ackdata = genAckPayload(streamNumber, 0) ackdata = genAckPayload(decodeAddress(addr)[2], 0)
recv = BROADCAST_STR recv = BROADCAST_STR
ripe = "" ripe = ""
sqlExecute( sqlExecute(
@ -823,19 +995,22 @@ def sendMessage(sender="", recv="", broadcast=None, subject="", body="", reply=F
subject, subject,
body, body,
ackdata, ackdata,
int(time.time()), # sentTime (this will never change) int(time.time()), # sentTime (this will never change)
int(time.time()), # lastActionTime int(time.time()), # lastActionTime
0, # sleepTill time. This will get set when the POW gets done. 0, # sleepTill time. This will get set when the POW gets done.
"broadcastqueued", "broadcastqueued",
0, # retryNumber 0, # retryNumber
"sent", # folder "sent", # folder
2, # encodingType 2, # encodingType
BMConfigParser().getint('bitmessagesettings', 'ttl')) BMConfigParser().getint('bitmessagesettings', 'ttl'))
queues.workerQueue.put(('sendbroadcast', '')) queues.workerQueue.put(('sendbroadcast', ''))
# pylint: disable=redefined-outer-name, too-many-locals
def loadInbox(): def loadInbox():
"""Load the list of messages"""
sys.stdout = sys.__stdout__ sys.stdout = sys.__stdout__
print("Loading inbox messages...") print "Loading inbox messages..."
sys.stdout = printlog sys.stdout = printlog
where = "toaddress || fromaddress || subject || message" where = "toaddress || fromaddress || subject || message"
@ -864,12 +1039,12 @@ def loadInbox():
fromlabel = "" fromlabel = ""
if BMConfigParser().has_section(fromaddr): if BMConfigParser().has_section(fromaddr):
fromlabel = BMConfigParser().get(fromaddr, "label") fromlabel = BMConfigParser().get(fromaddr, "label")
if fromlabel == "": # Check Address Book if fromlabel == "": # Check Address Book
qr = sqlQuery("SELECT label FROM addressbook WHERE address=?", fromaddr) qr = sqlQuery("SELECT label FROM addressbook WHERE address=?", fromaddr)
if qr != []: if qr != []:
for r in qr: for r in qr:
fromlabel, = r fromlabel, = r
if fromlabel == "": # Check Subscriptions if fromlabel == "": # Check Subscriptions
qr = sqlQuery("SELECT label FROM subscriptions WHERE address=?", fromaddr) qr = sqlQuery("SELECT label FROM subscriptions WHERE address=?", fromaddr)
if qr != []: if qr != []:
for r in qr: for r in qr:
@ -879,12 +1054,15 @@ def loadInbox():
fromlabel = shared.fixPotentiallyInvalidUTF8Data(fromlabel) fromlabel = shared.fixPotentiallyInvalidUTF8Data(fromlabel)
# Load into array # Load into array
inbox.append([msgid, tolabel, toaddr, fromlabel, fromaddr, subject, inbox.append([msgid, tolabel, toaddr, fromlabel, fromaddr, subject, l10n.formatTimestamp(
l10n.formatTimestamp(received, False), read]) received, False), read])
inbox.reverse() inbox.reverse()
def loadSent(): def loadSent():
"""Load the messages that sent"""
sys.stdout = sys.__stdout__ sys.stdout = sys.__stdout__
print("Loading sent messages...") print "Loading sent messages..."
sys.stdout = printlog sys.stdout = printlog
where = "toaddress || fromaddress || subject || message" where = "toaddress || fromaddress || subject || message"
@ -930,20 +1108,20 @@ def loadSent():
statstr = "Message queued" statstr = "Message queued"
elif status == "msgsent": elif status == "msgsent":
t = l10n.formatTimestamp(lastactiontime, False) t = l10n.formatTimestamp(lastactiontime, False)
statstr = "Message sent at "+t+".Waiting for acknowledgement." statstr = "Message sent at " + t + ".Waiting for acknowledgement."
elif status == "msgsentnoackexpected": elif status == "msgsentnoackexpected":
t = l10n.formatTimestamp(lastactiontime, False) t = l10n.formatTimestamp(lastactiontime, False)
statstr = "Message sent at "+t+"." statstr = "Message sent at " + t + "."
elif status == "doingmsgpow": elif status == "doingmsgpow":
statstr = "The proof of work required to send the message has been queued." statstr = "The proof of work required to send the message has been queued."
elif status == "ackreceived": elif status == "ackreceived":
t = l10n.formatTimestamp(lastactiontime, False) t = l10n.formatTimestamp(lastactiontime, False)
statstr = "Acknowledgment of the message received at "+t+"." statstr = "Acknowledgment of the message received at " + t + "."
elif status == "broadcastqueued": elif status == "broadcastqueued":
statstr = "Broadcast queued." statstr = "Broadcast queued."
elif status == "broadcastsent": elif status == "broadcastsent":
t = l10n.formatTimestamp(lastactiontime, False) t = l10n.formatTimestamp(lastactiontime, False)
statstr = "Broadcast sent at "+t+"." statstr = "Broadcast sent at " + t + "."
elif status == "forcepow": elif status == "forcepow":
statstr = "Forced difficulty override. Message will start sending soon." statstr = "Forced difficulty override. Message will start sending soon."
elif status == "badkey": elif status == "badkey":
@ -952,15 +1130,25 @@ def loadSent():
statstr = "Error: The work demanded by the recipient is more difficult than you are willing to do." statstr = "Error: The work demanded by the recipient is more difficult than you are willing to do."
else: else:
t = l10n.formatTimestamp(lastactiontime, False) t = l10n.formatTimestamp(lastactiontime, False)
statstr = "Unknown status "+status+" at "+t+"." statstr = "Unknown status " + status + " at " + t + "."
# Load into array # Load into array
sentbox.append([tolabel, toaddr, fromlabel, fromaddr, subject, statstr, ackdata, sentbox.append([
tolabel,
toaddr,
fromlabel,
fromaddr,
subject,
statstr,
ackdata,
l10n.formatTimestamp(lastactiontime, False)]) l10n.formatTimestamp(lastactiontime, False)])
sentbox.reverse() sentbox.reverse()
def loadAddrBook(): def loadAddrBook():
"""Load address book"""
sys.stdout = sys.__stdout__ sys.stdout = sys.__stdout__
print("Loading address book...") print "Loading address book..."
sys.stdout = printlog sys.stdout = printlog
ret = sqlQuery("SELECT label, address FROM addressbook") ret = sqlQuery("SELECT label, address FROM addressbook")
@ -969,14 +1157,20 @@ def loadAddrBook():
label = shared.fixPotentiallyInvalidUTF8Data(label) label = shared.fixPotentiallyInvalidUTF8Data(label)
addrbook.append([label, addr]) addrbook.append([label, addr])
addrbook.reverse() addrbook.reverse()
def loadSubscriptions(): def loadSubscriptions():
"""Load subscription functionality"""
ret = sqlQuery("SELECT label, address, enabled FROM subscriptions") ret = sqlQuery("SELECT label, address, enabled FROM subscriptions")
for row in ret: for row in ret:
label, address, enabled = row label, address, enabled = row
subscriptions.append([label, address, enabled]) subscriptions.append([label, address, enabled])
subscriptions.reverse() subscriptions.reverse()
def loadBlackWhiteList(): def loadBlackWhiteList():
global bwtype """load black/white list"""
global bwtype # pylint: disable=global-statement
bwtype = BMConfigParser().get("bitmessagesettings", "blackwhitelist") bwtype = BMConfigParser().get("bitmessagesettings", "blackwhitelist")
if bwtype == "black": if bwtype == "black":
ret = sqlQuery("SELECT label, address, enabled FROM blacklist") ret = sqlQuery("SELECT label, address, enabled FROM blacklist")
@ -987,9 +1181,10 @@ def loadBlackWhiteList():
blacklist.append([label, address, enabled]) blacklist.append([label, address, enabled])
blacklist.reverse() blacklist.reverse()
def runwrapper(): def runwrapper():
sys.stdout = printlog sys.stdout = printlog
#sys.stderr = errlog # sys.stderr = errlog
# Load messages from database # Load messages from database
loadInbox() loadInbox()
@ -1000,7 +1195,7 @@ def runwrapper():
stdscr = curses.initscr() stdscr = curses.initscr()
global logpad global logpad # pylint: disable=global-statement
logpad = curses.newpad(1024, curses.COLS) logpad = curses.newpad(1024, curses.COLS)
stdscr.nodelay(0) stdscr.nodelay(0)
@ -1010,27 +1205,28 @@ def runwrapper():
curses.wrapper(run) curses.wrapper(run)
doShutdown() doShutdown()
def run(stdscr): def run(stdscr):
# Schedule inventory lookup data # Schedule inventory lookup data
resetlookups() resetlookups()
# Init color pairs # Init color pairs
if curses.has_colors(): if curses.has_colors():
curses.init_pair(1, curses.COLOR_RED, curses.COLOR_BLACK) # red curses.init_pair(1, curses.COLOR_RED, curses.COLOR_BLACK) # red
curses.init_pair(2, curses.COLOR_GREEN, curses.COLOR_BLACK) # green curses.init_pair(2, curses.COLOR_GREEN, curses.COLOR_BLACK) # green
curses.init_pair(3, curses.COLOR_YELLOW, curses.COLOR_BLACK) # yellow curses.init_pair(3, curses.COLOR_YELLOW, curses.COLOR_BLACK) # yellow
curses.init_pair(4, curses.COLOR_BLUE, curses.COLOR_BLACK) # blue curses.init_pair(4, curses.COLOR_BLUE, curses.COLOR_BLACK) # blue
curses.init_pair(5, curses.COLOR_MAGENTA, curses.COLOR_BLACK) # magenta curses.init_pair(5, curses.COLOR_MAGENTA, curses.COLOR_BLACK) # magenta
curses.init_pair(6, curses.COLOR_CYAN, curses.COLOR_BLACK) # cyan curses.init_pair(6, curses.COLOR_CYAN, curses.COLOR_BLACK) # cyan
curses.init_pair(7, curses.COLOR_WHITE, curses.COLOR_BLACK) # white curses.init_pair(7, curses.COLOR_WHITE, curses.COLOR_BLACK) # white
if curses.can_change_color(): if curses.can_change_color():
curses.init_color(8, 500, 500, 500) # gray curses.init_color(8, 500, 500, 500) # gray
curses.init_pair(8, 8, 0) curses.init_pair(8, 8, 0)
curses.init_color(9, 844, 465, 0) # orange curses.init_color(9, 844, 465, 0) # orange
curses.init_pair(9, 9, 0) curses.init_pair(9, 9, 0)
else: else:
curses.init_pair(8, curses.COLOR_WHITE, curses.COLOR_BLACK) # grayish curses.init_pair(8, curses.COLOR_WHITE, curses.COLOR_BLACK) # grayish
curses.init_pair(9, curses.COLOR_YELLOW, curses.COLOR_BLACK) # orangish curses.init_pair(9, curses.COLOR_YELLOW, curses.COLOR_BLACK) # orangish
# Init list of address in 'Your Identities' tab # Init list of address in 'Your Identities' tab
configSections = BMConfigParser().addresses() configSections = BMConfigParser().addresses()
@ -1039,27 +1235,29 @@ def run(stdscr):
addresses.append([BMConfigParser().get(addressInKeysFile, "label"), isEnabled, addressInKeysFile]) addresses.append([BMConfigParser().get(addressInKeysFile, "label"), isEnabled, addressInKeysFile])
# Set address color # Set address color
if not isEnabled: if not isEnabled:
addresses[len(addresses)-1].append(8) # gray addresses[len(addresses) - 1].append(8) # gray
elif BMConfigParser().safeGetBoolean(addressInKeysFile, 'chan'): elif BMConfigParser().safeGetBoolean(addressInKeysFile, 'chan'):
addresses[len(addresses)-1].append(9) # orange addresses[len(addresses) - 1].append(9) # orange
elif BMConfigParser().safeGetBoolean(addressInKeysFile, 'mailinglist'): elif BMConfigParser().safeGetBoolean(addressInKeysFile, 'mailinglist'):
addresses[len(addresses)-1].append(5) # magenta addresses[len(addresses) - 1].append(5) # magenta
else: else:
addresses[len(addresses)-1].append(0) # black addresses[len(addresses) - 1].append(0) # black
addresses.reverse() addresses.reverse()
stdscr.clear() stdscr.clear()
redraw(stdscr) redraw(stdscr)
while quit == False: while quit is False:
drawtab(stdscr) drawtab(stdscr)
handlech(stdscr.getch(), stdscr) handlech(stdscr.getch(), stdscr)
def doShutdown(): def doShutdown():
"""Shutting the app down"""
sys.stdout = sys.__stdout__ sys.stdout = sys.__stdout__
print("Shutting down...") print "Shutting down..."
sys.stdout = printlog sys.stdout = printlog
shutdown.doCleanShutdown() shutdown.doCleanShutdown()
sys.stdout = sys.__stdout__ sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__ sys.stderr = sys.__stderr__
os._exit(0) os._exit(0) # pylint: disable=protected-access

View File

@ -194,7 +194,8 @@ class Main:
from plugins.plugin import get_plugin from plugins.plugin import get_plugin
try: try:
proxyconfig_start = time.time() proxyconfig_start = time.time()
get_plugin('proxyconfig', name=proxy_type)(config) if not get_plugin('proxyconfig', name=proxy_type)(config):
raise TypeError
except TypeError: except TypeError:
logger.error( logger.error(
'Failed to run proxy config plugin %s', 'Failed to run proxy config plugin %s',
@ -418,7 +419,7 @@ class Main:
self.stop() self.stop()
elif not state.enableGUI: elif not state.enableGUI:
from tests import core as test_core # pylint: disable=relative-import from tests import core as test_core # pylint: disable=relative-import
test_core_result = test_core.run() test_core_result = test_core.run(self)
state.enableGUI = True state.enableGUI = True
self.stop() self.stop()
test_core.cleanup() test_core.cleanup()

View File

@ -23,7 +23,6 @@ from addresses import decodeAddress, addBMIfNotPresent
import shared import shared
from bitmessageui import Ui_MainWindow from bitmessageui import Ui_MainWindow
from bmconfigparser import BMConfigParser from bmconfigparser import BMConfigParser
import defaults
import namecoin import namecoin
from messageview import MessageView from messageview import MessageView
from migrationwizard import Ui_MigrationWizard from migrationwizard import Ui_MigrationWizard
@ -31,15 +30,12 @@ from foldertree import (
AccountMixin, Ui_FolderWidget, Ui_AddressWidget, Ui_SubscriptionWidget, AccountMixin, Ui_FolderWidget, Ui_AddressWidget, Ui_SubscriptionWidget,
MessageList_AddressWidget, MessageList_SubjectWidget, MessageList_AddressWidget, MessageList_SubjectWidget,
Ui_AddressBookWidgetItemLabel, Ui_AddressBookWidgetItemAddress) Ui_AddressBookWidgetItemLabel, Ui_AddressBookWidgetItemAddress)
from settings import Ui_settingsDialog
import settingsmixin import settingsmixin
import support import support
import debug
from helper_ackPayload import genAckPayload from helper_ackPayload import genAckPayload
from helper_sql import sqlQuery, sqlExecute, sqlExecuteChunked, sqlStoredProcedure from helper_sql import sqlQuery, sqlExecute, sqlExecuteChunked, sqlStoredProcedure
import helper_search import helper_search
import l10n import l10n
import openclpow
from utils import str_broadcast_subscribers, avatarize from utils import str_broadcast_subscribers, avatarize
from account import ( from account import (
getSortedAccounts, getSortedSubscriptions, accountClass, BMAccount, getSortedAccounts, getSortedSubscriptions, accountClass, BMAccount,
@ -47,16 +43,15 @@ from account import (
import dialogs import dialogs
from network.stats import pendingDownload, pendingUpload from network.stats import pendingDownload, pendingUpload
from uisignaler import UISignaler from uisignaler import UISignaler
import knownnodes
import paths import paths
from proofofwork import getPowType from proofofwork import getPowType
import queues import queues
import shutdown import shutdown
import state import state
from statusbar import BMStatusBar from statusbar import BMStatusBar
from network.asyncore_pollchoose import set_rates
import sound import sound
# This is needed for tray icon
import bitmessage_icons_rc # noqa:F401 pylint: disable=unused-import
try: try:
from plugins.plugin import get_plugin, get_plugins from plugins.plugin import get_plugin, get_plugins
@ -64,49 +59,6 @@ except ImportError:
get_plugins = False get_plugins = False
def change_translation(newlocale):
global qmytranslator, qsystranslator
try:
if not qmytranslator.isEmpty():
QtGui.QApplication.removeTranslator(qmytranslator)
except:
pass
try:
if not qsystranslator.isEmpty():
QtGui.QApplication.removeTranslator(qsystranslator)
except:
pass
qmytranslator = QtCore.QTranslator()
translationpath = os.path.join (paths.codePath(), 'translations', 'bitmessage_' + newlocale)
qmytranslator.load(translationpath)
QtGui.QApplication.installTranslator(qmytranslator)
qsystranslator = QtCore.QTranslator()
if paths.frozen:
translationpath = os.path.join (paths.codePath(), 'translations', 'qt_' + newlocale)
else:
translationpath = os.path.join (str(QtCore.QLibraryInfo.location(QtCore.QLibraryInfo.TranslationsPath)), 'qt_' + newlocale)
qsystranslator.load(translationpath)
QtGui.QApplication.installTranslator(qsystranslator)
lang = locale.normalize(l10n.getTranslationLanguage())
langs = [lang.split(".")[0] + "." + l10n.encoding, lang.split(".")[0] + "." + 'UTF-8', lang]
if 'win32' in sys.platform or 'win64' in sys.platform:
langs = [l10n.getWindowsLocale(lang)]
for lang in langs:
try:
l10n.setlocale(locale.LC_ALL, lang)
if 'win32' not in sys.platform and 'win64' not in sys.platform:
l10n.encoding = locale.nl_langinfo(locale.CODESET)
else:
l10n.encoding = locale.getlocale()[1]
logger.info("Successfully set locale to %s", lang)
break
except:
logger.error("Failed to set locale to %s", lang, exc_info=True)
# TODO: rewrite # TODO: rewrite
def powQueueSize(): def powQueueSize():
"""Returns the size of queues.workerQueue including current unfinished work""" """Returns the size of queues.workerQueue including current unfinished work"""
@ -122,9 +74,6 @@ def powQueueSize():
class MyForm(settingsmixin.SMainWindow): class MyForm(settingsmixin.SMainWindow):
# the last time that a message arrival sound was played
lastSoundTime = datetime.now() - timedelta(days=1)
# the maximum frequency of message sounds in seconds # the maximum frequency of message sounds in seconds
maxSoundFrequencySec = 60 maxSoundFrequencySec = 60
@ -132,6 +81,58 @@ class MyForm(settingsmixin.SMainWindow):
REPLY_TYPE_CHAN = 1 REPLY_TYPE_CHAN = 1
REPLY_TYPE_UPD = 2 REPLY_TYPE_UPD = 2
def change_translation(self, newlocale=None):
"""Change translation language for the application"""
if newlocale is None:
newlocale = l10n.getTranslationLanguage()
try:
if not self.qmytranslator.isEmpty():
QtGui.QApplication.removeTranslator(self.qmytranslator)
except:
pass
try:
if not self.qsystranslator.isEmpty():
QtGui.QApplication.removeTranslator(self.qsystranslator)
except:
pass
self.qmytranslator = QtCore.QTranslator()
translationpath = os.path.join(
paths.codePath(), 'translations', 'bitmessage_' + newlocale)
self.qmytranslator.load(translationpath)
QtGui.QApplication.installTranslator(self.qmytranslator)
self.qsystranslator = QtCore.QTranslator()
if paths.frozen:
translationpath = os.path.join(
paths.codePath(), 'translations', 'qt_' + newlocale)
else:
translationpath = os.path.join(
str(QtCore.QLibraryInfo.location(
QtCore.QLibraryInfo.TranslationsPath)), 'qt_' + newlocale)
self.qsystranslator.load(translationpath)
QtGui.QApplication.installTranslator(self.qsystranslator)
lang = locale.normalize(l10n.getTranslationLanguage())
langs = [
lang.split(".")[0] + "." + l10n.encoding,
lang.split(".")[0] + "." + 'UTF-8',
lang
]
if 'win32' in sys.platform or 'win64' in sys.platform:
langs = [l10n.getWindowsLocale(lang)]
for lang in langs:
try:
l10n.setlocale(locale.LC_ALL, lang)
if 'win32' not in sys.platform and 'win64' not in sys.platform:
l10n.encoding = locale.nl_langinfo(locale.CODESET)
else:
l10n.encoding = locale.getlocale()[1]
logger.info("Successfully set locale to %s", lang)
break
except:
logger.error("Failed to set locale to %s", lang, exc_info=True)
def init_file_menu(self): def init_file_menu(self):
QtCore.QObject.connect(self.ui.actionExit, QtCore.SIGNAL( QtCore.QObject.connect(self.ui.actionExit, QtCore.SIGNAL(
"triggered()"), self.quit) "triggered()"), self.quit)
@ -605,6 +606,13 @@ class MyForm(settingsmixin.SMainWindow):
self.ui = Ui_MainWindow() self.ui = Ui_MainWindow()
self.ui.setupUi(self) self.ui.setupUi(self)
self.qmytranslator = self.qsystranslator = None
self.indicatorUpdate = None
self.actionStatus = None
# the last time that a message arrival sound was played
self.lastSoundTime = datetime.now() - timedelta(days=1)
# Ask the user if we may delete their old version 1 addresses if they # Ask the user if we may delete their old version 1 addresses if they
# have any. # have any.
for addressInKeysFile in getSortedAccounts(): for addressInKeysFile in getSortedAccounts():
@ -620,22 +628,9 @@ class MyForm(settingsmixin.SMainWindow):
BMConfigParser().remove_section(addressInKeysFile) BMConfigParser().remove_section(addressInKeysFile)
BMConfigParser().save() BMConfigParser().save()
# Configure Bitmessage to start on startup (or remove the self.updateStartOnLogon()
# configuration) based on the setting in the keys.dat file
if 'win32' in sys.platform or 'win64' in sys.platform: self.change_translation()
# Auto-startup for Windows
RUN_PATH = "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run"
self.settings = QtCore.QSettings(RUN_PATH, QtCore.QSettings.NativeFormat)
self.settings.remove(
"PyBitmessage") # In case the user moves the program and the registry entry is no longer valid, this will delete the old registry entry.
if BMConfigParser().getboolean('bitmessagesettings', 'startonlogon'):
self.settings.setValue("PyBitmessage", sys.argv[0])
elif 'darwin' in sys.platform:
# startup for mac
pass
elif 'linux' in sys.platform:
# startup for linux
pass
# e.g. for editing labels # e.g. for editing labels
self.recurDepth = 0 self.recurDepth = 0
@ -786,6 +781,9 @@ class MyForm(settingsmixin.SMainWindow):
self.ui.treeWidgetSubscriptions.keyPressEvent = self.treeWidgetKeyPressEvent self.ui.treeWidgetSubscriptions.keyPressEvent = self.treeWidgetKeyPressEvent
self.ui.treeWidgetChans.keyPressEvent = self.treeWidgetKeyPressEvent self.ui.treeWidgetChans.keyPressEvent = self.treeWidgetKeyPressEvent
# Key press in addressbook
self.ui.tableWidgetAddressBook.keyPressEvent = self.addressbookKeyPressEvent
# Key press in messagelist # Key press in messagelist
self.ui.tableWidgetInbox.keyPressEvent = self.tableWidgetKeyPressEvent self.ui.tableWidgetInbox.keyPressEvent = self.tableWidgetKeyPressEvent
self.ui.tableWidgetInboxSubscriptions.keyPressEvent = self.tableWidgetKeyPressEvent self.ui.tableWidgetInboxSubscriptions.keyPressEvent = self.tableWidgetKeyPressEvent
@ -828,6 +826,28 @@ class MyForm(settingsmixin.SMainWindow):
finally: finally:
self._contact_selected = None self._contact_selected = None
def updateStartOnLogon(self):
# Configure Bitmessage to start on startup (or remove the
# configuration) based on the setting in the keys.dat file
if 'win32' in sys.platform or 'win64' in sys.platform:
# Auto-startup for Windows
RUN_PATH = "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run"
self.settings = QtCore.QSettings(
RUN_PATH, QtCore.QSettings.NativeFormat)
# In case the user moves the program and the registry entry is
# no longer valid, this will delete the old registry entry.
self.settings.remove("PyBitmessage")
if BMConfigParser().getboolean(
'bitmessagesettings', 'startonlogon'
):
self.settings.setValue("PyBitmessage", sys.argv[0])
elif 'darwin' in sys.platform:
# startup for mac
pass
elif 'linux' in sys.platform:
# startup for linux
pass
def updateTTL(self, sliderPosition): def updateTTL(self, sliderPosition):
TTL = int(sliderPosition ** 3.199 + 3600) TTL = int(sliderPosition ** 3.199 + 3600)
self.updateHumanFriendlyTTLDescription(TTL) self.updateHumanFriendlyTTLDescription(TTL)
@ -1433,6 +1453,15 @@ class MyForm(settingsmixin.SMainWindow):
def treeWidgetKeyPressEvent(self, event): def treeWidgetKeyPressEvent(self, event):
return self.handleKeyPress(event, self.getCurrentTreeWidget()) return self.handleKeyPress(event, self.getCurrentTreeWidget())
# addressbook
def addressbookKeyPressEvent(self, event):
"""Handle keypress event in addressbook widget"""
if event.key() == QtCore.Qt.Key_Delete:
self.on_action_AddressBookDelete()
else:
return QtGui.QTableWidget.keyPressEvent(
self.ui.tableWidgetAddressBook, event)
# inbox / sent # inbox / sent
def tableWidgetKeyPressEvent(self, event): def tableWidgetKeyPressEvent(self, event):
return self.handleKeyPress(event, self.getCurrentMessagelist()) return self.handleKeyPress(event, self.getCurrentMessagelist())
@ -1441,11 +1470,12 @@ class MyForm(settingsmixin.SMainWindow):
def textEditKeyPressEvent(self, event): def textEditKeyPressEvent(self, event):
return self.handleKeyPress(event, self.getCurrentMessageTextedit()) return self.handleKeyPress(event, self.getCurrentMessageTextedit())
def handleKeyPress(self, event, focus = None): def handleKeyPress(self, event, focus=None):
"""This method handles keypress events for all widgets on MyForm"""
messagelist = self.getCurrentMessagelist() messagelist = self.getCurrentMessagelist()
folder = self.getCurrentFolder() folder = self.getCurrentFolder()
if event.key() == QtCore.Qt.Key_Delete: if event.key() == QtCore.Qt.Key_Delete:
if isinstance (focus, MessageView) or isinstance(focus, QtGui.QTableWidget): if isinstance(focus, MessageView) or isinstance(focus, QtGui.QTableWidget):
if folder == "sent": if folder == "sent":
self.on_action_SentTrash() self.on_action_SentTrash()
else: else:
@ -1481,17 +1511,17 @@ class MyForm(settingsmixin.SMainWindow):
self.ui.lineEditTo.setFocus() self.ui.lineEditTo.setFocus()
event.ignore() event.ignore()
elif event.key() == QtCore.Qt.Key_F: elif event.key() == QtCore.Qt.Key_F:
searchline = self.getCurrentSearchLine(retObj = True) searchline = self.getCurrentSearchLine(retObj=True)
if searchline: if searchline:
searchline.setFocus() searchline.setFocus()
event.ignore() event.ignore()
if not event.isAccepted(): if not event.isAccepted():
return return
if isinstance (focus, MessageView): if isinstance(focus, MessageView):
return MessageView.keyPressEvent(focus, event) return MessageView.keyPressEvent(focus, event)
elif isinstance (focus, QtGui.QTableWidget): elif isinstance(focus, QtGui.QTableWidget):
return QtGui.QTableWidget.keyPressEvent(focus, event) return QtGui.QTableWidget.keyPressEvent(focus, event)
elif isinstance (focus, QtGui.QTreeWidget): elif isinstance(focus, QtGui.QTreeWidget):
return QtGui.QTreeWidget.keyPressEvent(focus, event) return QtGui.QTreeWidget.keyPressEvent(focus, event)
# menu button 'manage keys' # menu button 'manage keys'
@ -1622,7 +1652,6 @@ class MyForm(settingsmixin.SMainWindow):
# The window state has just been changed to # The window state has just been changed to
# Normal/Maximised/FullScreen # Normal/Maximised/FullScreen
pass pass
# QtGui.QWidget.changeEvent(self, event)
def __icon_activated(self, reason): def __icon_activated(self, reason):
if reason == QtGui.QSystemTrayIcon.Trigger: if reason == QtGui.QSystemTrayIcon.Trigger:
@ -2434,225 +2463,7 @@ class MyForm(settingsmixin.SMainWindow):
dialogs.AboutDialog(self).exec_() dialogs.AboutDialog(self).exec_()
def click_actionSettings(self): def click_actionSettings(self):
self.settingsDialogInstance = settingsDialog(self) dialogs.SettingsDialog(self, firstrun=self._firstrun).exec_()
if self._firstrun:
self.settingsDialogInstance.ui.tabWidgetSettings.setCurrentIndex(1)
if self.settingsDialogInstance.exec_():
if self._firstrun:
BMConfigParser().remove_option(
'bitmessagesettings', 'dontconnect')
BMConfigParser().set('bitmessagesettings', 'startonlogon', str(
self.settingsDialogInstance.ui.checkBoxStartOnLogon.isChecked()))
BMConfigParser().set('bitmessagesettings', 'minimizetotray', str(
self.settingsDialogInstance.ui.checkBoxMinimizeToTray.isChecked()))
BMConfigParser().set('bitmessagesettings', 'trayonclose', str(
self.settingsDialogInstance.ui.checkBoxTrayOnClose.isChecked()))
BMConfigParser().set('bitmessagesettings', 'hidetrayconnectionnotifications', str(
self.settingsDialogInstance.ui.checkBoxHideTrayConnectionNotifications.isChecked()))
BMConfigParser().set('bitmessagesettings', 'showtraynotifications', str(
self.settingsDialogInstance.ui.checkBoxShowTrayNotifications.isChecked()))
BMConfigParser().set('bitmessagesettings', 'startintray', str(
self.settingsDialogInstance.ui.checkBoxStartInTray.isChecked()))
BMConfigParser().set('bitmessagesettings', 'willinglysendtomobile', str(
self.settingsDialogInstance.ui.checkBoxWillinglySendToMobile.isChecked()))
BMConfigParser().set('bitmessagesettings', 'useidenticons', str(
self.settingsDialogInstance.ui.checkBoxUseIdenticons.isChecked()))
BMConfigParser().set('bitmessagesettings', 'replybelow', str(
self.settingsDialogInstance.ui.checkBoxReplyBelow.isChecked()))
lang = str(self.settingsDialogInstance.ui.languageComboBox.itemData(self.settingsDialogInstance.ui.languageComboBox.currentIndex()).toString())
BMConfigParser().set('bitmessagesettings', 'userlocale', lang)
change_translation(l10n.getTranslationLanguage())
if int(BMConfigParser().get('bitmessagesettings', 'port')) != int(self.settingsDialogInstance.ui.lineEditTCPPort.text()):
if not BMConfigParser().safeGetBoolean('bitmessagesettings', 'dontconnect'):
QtGui.QMessageBox.about(self, _translate("MainWindow", "Restart"), _translate(
"MainWindow", "You must restart Bitmessage for the port number change to take effect."))
BMConfigParser().set('bitmessagesettings', 'port', str(
self.settingsDialogInstance.ui.lineEditTCPPort.text()))
if self.settingsDialogInstance.ui.checkBoxUPnP.isChecked() != BMConfigParser().safeGetBoolean('bitmessagesettings', 'upnp'):
BMConfigParser().set('bitmessagesettings', 'upnp', str(self.settingsDialogInstance.ui.checkBoxUPnP.isChecked()))
if self.settingsDialogInstance.ui.checkBoxUPnP.isChecked():
import upnp
upnpThread = upnp.uPnPThread()
upnpThread.start()
#print 'self.settingsDialogInstance.ui.comboBoxProxyType.currentText()', self.settingsDialogInstance.ui.comboBoxProxyType.currentText()
#print 'self.settingsDialogInstance.ui.comboBoxProxyType.currentText())[0:5]', self.settingsDialogInstance.ui.comboBoxProxyType.currentText()[0:5]
if BMConfigParser().get('bitmessagesettings', 'socksproxytype') == 'none' and self.settingsDialogInstance.ui.comboBoxProxyType.currentText()[0:5] == 'SOCKS':
if shared.statusIconColor != 'red':
QtGui.QMessageBox.about(self, _translate("MainWindow", "Restart"), _translate(
"MainWindow", "Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any)."))
if BMConfigParser().get('bitmessagesettings', 'socksproxytype')[0:5] == 'SOCKS' and self.settingsDialogInstance.ui.comboBoxProxyType.currentText()[0:5] != 'SOCKS':
self.statusbar.clearMessage()
state.resetNetworkProtocolAvailability() # just in case we changed something in the network connectivity
if self.settingsDialogInstance.ui.comboBoxProxyType.currentText()[0:5] == 'SOCKS':
BMConfigParser().set('bitmessagesettings', 'socksproxytype', str(
self.settingsDialogInstance.ui.comboBoxProxyType.currentText()))
else:
BMConfigParser().set('bitmessagesettings', 'socksproxytype', 'none')
BMConfigParser().set('bitmessagesettings', 'socksauthentication', str(
self.settingsDialogInstance.ui.checkBoxAuthentication.isChecked()))
BMConfigParser().set('bitmessagesettings', 'sockshostname', str(
self.settingsDialogInstance.ui.lineEditSocksHostname.text()))
BMConfigParser().set('bitmessagesettings', 'socksport', str(
self.settingsDialogInstance.ui.lineEditSocksPort.text()))
BMConfigParser().set('bitmessagesettings', 'socksusername', str(
self.settingsDialogInstance.ui.lineEditSocksUsername.text()))
BMConfigParser().set('bitmessagesettings', 'sockspassword', str(
self.settingsDialogInstance.ui.lineEditSocksPassword.text()))
BMConfigParser().set('bitmessagesettings', 'sockslisten', str(
self.settingsDialogInstance.ui.checkBoxSocksListen.isChecked()))
try:
# Rounding to integers just for aesthetics
BMConfigParser().set('bitmessagesettings', 'maxdownloadrate', str(
int(float(self.settingsDialogInstance.ui.lineEditMaxDownloadRate.text()))))
BMConfigParser().set('bitmessagesettings', 'maxuploadrate', str(
int(float(self.settingsDialogInstance.ui.lineEditMaxUploadRate.text()))))
except ValueError:
QtGui.QMessageBox.about(self, _translate("MainWindow", "Number needed"), _translate(
"MainWindow", "Your maximum download and upload rate must be numbers. Ignoring what you typed."))
else:
set_rates(BMConfigParser().safeGetInt("bitmessagesettings", "maxdownloadrate"),
BMConfigParser().safeGetInt("bitmessagesettings", "maxuploadrate"))
BMConfigParser().set('bitmessagesettings', 'maxoutboundconnections', str(
int(float(self.settingsDialogInstance.ui.lineEditMaxOutboundConnections.text()))))
BMConfigParser().set('bitmessagesettings', 'namecoinrpctype',
self.settingsDialogInstance.getNamecoinType())
BMConfigParser().set('bitmessagesettings', 'namecoinrpchost', str(
self.settingsDialogInstance.ui.lineEditNamecoinHost.text()))
BMConfigParser().set('bitmessagesettings', 'namecoinrpcport', str(
self.settingsDialogInstance.ui.lineEditNamecoinPort.text()))
BMConfigParser().set('bitmessagesettings', 'namecoinrpcuser', str(
self.settingsDialogInstance.ui.lineEditNamecoinUser.text()))
BMConfigParser().set('bitmessagesettings', 'namecoinrpcpassword', str(
self.settingsDialogInstance.ui.lineEditNamecoinPassword.text()))
self.resetNamecoinConnection()
# Demanded difficulty tab
if float(self.settingsDialogInstance.ui.lineEditTotalDifficulty.text()) >= 1:
BMConfigParser().set('bitmessagesettings', 'defaultnoncetrialsperbyte', str(int(float(
self.settingsDialogInstance.ui.lineEditTotalDifficulty.text()) * defaults.networkDefaultProofOfWorkNonceTrialsPerByte)))
if float(self.settingsDialogInstance.ui.lineEditSmallMessageDifficulty.text()) >= 1:
BMConfigParser().set('bitmessagesettings', 'defaultpayloadlengthextrabytes', str(int(float(
self.settingsDialogInstance.ui.lineEditSmallMessageDifficulty.text()) * defaults.networkDefaultPayloadLengthExtraBytes)))
if self.settingsDialogInstance.ui.comboBoxOpenCL.currentText().toUtf8() != BMConfigParser().safeGet("bitmessagesettings", "opencl"):
BMConfigParser().set('bitmessagesettings', 'opencl', str(self.settingsDialogInstance.ui.comboBoxOpenCL.currentText()))
queues.workerQueue.put(('resetPoW', ''))
acceptableDifficultyChanged = False
if float(self.settingsDialogInstance.ui.lineEditMaxAcceptableTotalDifficulty.text()) >= 1 or float(self.settingsDialogInstance.ui.lineEditMaxAcceptableTotalDifficulty.text()) == 0:
if BMConfigParser().get('bitmessagesettings','maxacceptablenoncetrialsperbyte') != str(int(float(
self.settingsDialogInstance.ui.lineEditMaxAcceptableTotalDifficulty.text()) * defaults.networkDefaultProofOfWorkNonceTrialsPerByte)):
# the user changed the max acceptable total difficulty
acceptableDifficultyChanged = True
BMConfigParser().set('bitmessagesettings', 'maxacceptablenoncetrialsperbyte', str(int(float(
self.settingsDialogInstance.ui.lineEditMaxAcceptableTotalDifficulty.text()) * defaults.networkDefaultProofOfWorkNonceTrialsPerByte)))
if float(self.settingsDialogInstance.ui.lineEditMaxAcceptableSmallMessageDifficulty.text()) >= 1 or float(self.settingsDialogInstance.ui.lineEditMaxAcceptableSmallMessageDifficulty.text()) == 0:
if BMConfigParser().get('bitmessagesettings','maxacceptablepayloadlengthextrabytes') != str(int(float(
self.settingsDialogInstance.ui.lineEditMaxAcceptableSmallMessageDifficulty.text()) * defaults.networkDefaultPayloadLengthExtraBytes)):
# the user changed the max acceptable small message difficulty
acceptableDifficultyChanged = True
BMConfigParser().set('bitmessagesettings', 'maxacceptablepayloadlengthextrabytes', str(int(float(
self.settingsDialogInstance.ui.lineEditMaxAcceptableSmallMessageDifficulty.text()) * defaults.networkDefaultPayloadLengthExtraBytes)))
if acceptableDifficultyChanged:
# It might now be possible to send msgs which were previously marked as toodifficult.
# Let us change them to 'msgqueued'. The singleWorker will try to send them and will again
# mark them as toodifficult if the receiver's required difficulty is still higher than
# we are willing to do.
sqlExecute('''UPDATE sent SET status='msgqueued' WHERE status='toodifficult' ''')
queues.workerQueue.put(('sendmessage', ''))
#start:UI setting to stop trying to send messages after X days/months
# I'm open to changing this UI to something else if someone has a better idea.
if ((self.settingsDialogInstance.ui.lineEditDays.text()=='') and (self.settingsDialogInstance.ui.lineEditMonths.text()=='')):#We need to handle this special case. Bitmessage has its default behavior. The input is blank/blank
BMConfigParser().set('bitmessagesettings', 'stopresendingafterxdays', '')
BMConfigParser().set('bitmessagesettings', 'stopresendingafterxmonths', '')
shared.maximumLengthOfTimeToBotherResendingMessages = float('inf')
try:
float(self.settingsDialogInstance.ui.lineEditDays.text())
lineEditDaysIsValidFloat = True
except:
lineEditDaysIsValidFloat = False
try:
float(self.settingsDialogInstance.ui.lineEditMonths.text())
lineEditMonthsIsValidFloat = True
except:
lineEditMonthsIsValidFloat = False
if lineEditDaysIsValidFloat and not lineEditMonthsIsValidFloat:
self.settingsDialogInstance.ui.lineEditMonths.setText("0")
if lineEditMonthsIsValidFloat and not lineEditDaysIsValidFloat:
self.settingsDialogInstance.ui.lineEditDays.setText("0")
if lineEditDaysIsValidFloat or lineEditMonthsIsValidFloat:
if (float(self.settingsDialogInstance.ui.lineEditDays.text()) >=0 and float(self.settingsDialogInstance.ui.lineEditMonths.text()) >=0):
shared.maximumLengthOfTimeToBotherResendingMessages = (float(str(self.settingsDialogInstance.ui.lineEditDays.text())) * 24 * 60 * 60) + (float(str(self.settingsDialogInstance.ui.lineEditMonths.text())) * (60 * 60 * 24 *365)/12)
if shared.maximumLengthOfTimeToBotherResendingMessages < 432000: # If the time period is less than 5 hours, we give zero values to all fields. No message will be sent again.
QtGui.QMessageBox.about(self, _translate("MainWindow", "Will not resend ever"), _translate(
"MainWindow", "Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent."))
BMConfigParser().set('bitmessagesettings', 'stopresendingafterxdays', '0')
BMConfigParser().set('bitmessagesettings', 'stopresendingafterxmonths', '0')
shared.maximumLengthOfTimeToBotherResendingMessages = 0
else:
BMConfigParser().set('bitmessagesettings', 'stopresendingafterxdays', str(float(
self.settingsDialogInstance.ui.lineEditDays.text())))
BMConfigParser().set('bitmessagesettings', 'stopresendingafterxmonths', str(float(
self.settingsDialogInstance.ui.lineEditMonths.text())))
BMConfigParser().save()
if 'win32' in sys.platform or 'win64' in sys.platform:
# Auto-startup for Windows
RUN_PATH = "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run"
self.settings = QtCore.QSettings(RUN_PATH, QtCore.QSettings.NativeFormat)
if BMConfigParser().getboolean('bitmessagesettings', 'startonlogon'):
self.settings.setValue("PyBitmessage", sys.argv[0])
else:
self.settings.remove("PyBitmessage")
elif 'darwin' in sys.platform:
# startup for mac
pass
elif 'linux' in sys.platform:
# startup for linux
pass
if state.appdata != paths.lookupExeFolder() and self.settingsDialogInstance.ui.checkBoxPortableMode.isChecked(): # If we are NOT using portable mode now but the user selected that we should...
# Write the keys.dat file to disk in the new location
sqlStoredProcedure('movemessagstoprog')
with open(paths.lookupExeFolder() + 'keys.dat', 'wb') as configfile:
BMConfigParser().write(configfile)
# Write the knownnodes.dat file to disk in the new location
knownnodes.saveKnownNodes(paths.lookupExeFolder())
os.remove(state.appdata + 'keys.dat')
os.remove(state.appdata + 'knownnodes.dat')
previousAppdataLocation = state.appdata
state.appdata = paths.lookupExeFolder()
debug.resetLogging()
try:
os.remove(previousAppdataLocation + 'debug.log')
os.remove(previousAppdataLocation + 'debug.log.1')
except:
pass
if state.appdata == paths.lookupExeFolder() and not self.settingsDialogInstance.ui.checkBoxPortableMode.isChecked(): # If we ARE using portable mode now but the user selected that we shouldn't...
state.appdata = paths.lookupAppdataFolder()
if not os.path.exists(state.appdata):
os.makedirs(state.appdata)
sqlStoredProcedure('movemessagstoappdata')
# Write the keys.dat file to disk in the new location
BMConfigParser().save()
# Write the knownnodes.dat file to disk in the new location
knownnodes.saveKnownNodes(state.appdata)
os.remove(paths.lookupExeFolder() + 'keys.dat')
os.remove(paths.lookupExeFolder() + 'knownnodes.dat')
debug.resetLogging()
try:
os.remove(paths.lookupExeFolder() + 'debug.log')
os.remove(paths.lookupExeFolder() + 'debug.log.1')
except:
pass
def on_action_Send(self): def on_action_Send(self):
"""Send message to current selected address""" """Send message to current selected address"""
@ -3393,8 +3204,7 @@ class MyForm(settingsmixin.SMainWindow):
0].row() 0].row()
item = self.ui.tableWidgetAddressBook.item(currentRow, 0) item = self.ui.tableWidgetAddressBook.item(currentRow, 0)
sqlExecute( sqlExecute(
'DELETE FROM addressbook WHERE label=? AND address=?', 'DELETE FROM addressbook WHERE address=?', item.address)
item.label, item.address)
self.ui.tableWidgetAddressBook.removeRow(currentRow) self.ui.tableWidgetAddressBook.removeRow(currentRow)
self.rerenderMessagelistFromLabels() self.rerenderMessagelistFromLabels()
self.rerenderMessagelistToLabels() self.rerenderMessagelistToLabels()
@ -4253,237 +4063,6 @@ class MyForm(settingsmixin.SMainWindow):
obj.loadSettings() obj.loadSettings()
class settingsDialog(QtGui.QDialog):
def __init__(self, parent):
QtGui.QWidget.__init__(self, parent)
self.ui = Ui_settingsDialog()
self.ui.setupUi(self)
self.parent = parent
self.ui.checkBoxStartOnLogon.setChecked(
BMConfigParser().getboolean('bitmessagesettings', 'startonlogon'))
self.ui.checkBoxMinimizeToTray.setChecked(
BMConfigParser().getboolean('bitmessagesettings', 'minimizetotray'))
self.ui.checkBoxTrayOnClose.setChecked(
BMConfigParser().safeGetBoolean('bitmessagesettings', 'trayonclose'))
self.ui.checkBoxHideTrayConnectionNotifications.setChecked(
BMConfigParser().getboolean("bitmessagesettings", "hidetrayconnectionnotifications"))
self.ui.checkBoxShowTrayNotifications.setChecked(
BMConfigParser().getboolean('bitmessagesettings', 'showtraynotifications'))
self.ui.checkBoxStartInTray.setChecked(
BMConfigParser().getboolean('bitmessagesettings', 'startintray'))
self.ui.checkBoxWillinglySendToMobile.setChecked(
BMConfigParser().safeGetBoolean('bitmessagesettings', 'willinglysendtomobile'))
self.ui.checkBoxUseIdenticons.setChecked(
BMConfigParser().safeGetBoolean('bitmessagesettings', 'useidenticons'))
self.ui.checkBoxReplyBelow.setChecked(
BMConfigParser().safeGetBoolean('bitmessagesettings', 'replybelow'))
if state.appdata == paths.lookupExeFolder():
self.ui.checkBoxPortableMode.setChecked(True)
else:
try:
import tempfile
tempfile.NamedTemporaryFile(
dir=paths.lookupExeFolder(), delete=True
).close() # should autodelete
except:
self.ui.checkBoxPortableMode.setDisabled(True)
if 'darwin' in sys.platform:
self.ui.checkBoxStartOnLogon.setDisabled(True)
self.ui.checkBoxStartOnLogon.setText(_translate(
"MainWindow", "Start-on-login not yet supported on your OS."))
self.ui.checkBoxMinimizeToTray.setDisabled(True)
self.ui.checkBoxMinimizeToTray.setText(_translate(
"MainWindow", "Minimize-to-tray not yet supported on your OS."))
self.ui.checkBoxShowTrayNotifications.setDisabled(True)
self.ui.checkBoxShowTrayNotifications.setText(_translate(
"MainWindow", "Tray notifications not yet supported on your OS."))
elif 'linux' in sys.platform:
self.ui.checkBoxStartOnLogon.setDisabled(True)
self.ui.checkBoxStartOnLogon.setText(_translate(
"MainWindow", "Start-on-login not yet supported on your OS."))
# On the Network settings tab:
self.ui.lineEditTCPPort.setText(str(
BMConfigParser().get('bitmessagesettings', 'port')))
self.ui.checkBoxUPnP.setChecked(
BMConfigParser().safeGetBoolean('bitmessagesettings', 'upnp'))
self.ui.checkBoxAuthentication.setChecked(BMConfigParser().getboolean(
'bitmessagesettings', 'socksauthentication'))
self.ui.checkBoxSocksListen.setChecked(BMConfigParser().getboolean(
'bitmessagesettings', 'sockslisten'))
if str(BMConfigParser().get('bitmessagesettings', 'socksproxytype')) == 'none':
self.ui.comboBoxProxyType.setCurrentIndex(0)
self.ui.lineEditSocksHostname.setEnabled(False)
self.ui.lineEditSocksPort.setEnabled(False)
self.ui.lineEditSocksUsername.setEnabled(False)
self.ui.lineEditSocksPassword.setEnabled(False)
self.ui.checkBoxAuthentication.setEnabled(False)
self.ui.checkBoxSocksListen.setEnabled(False)
elif str(BMConfigParser().get('bitmessagesettings', 'socksproxytype')) == 'SOCKS4a':
self.ui.comboBoxProxyType.setCurrentIndex(1)
elif str(BMConfigParser().get('bitmessagesettings', 'socksproxytype')) == 'SOCKS5':
self.ui.comboBoxProxyType.setCurrentIndex(2)
self.ui.lineEditSocksHostname.setText(str(
BMConfigParser().get('bitmessagesettings', 'sockshostname')))
self.ui.lineEditSocksPort.setText(str(
BMConfigParser().get('bitmessagesettings', 'socksport')))
self.ui.lineEditSocksUsername.setText(str(
BMConfigParser().get('bitmessagesettings', 'socksusername')))
self.ui.lineEditSocksPassword.setText(str(
BMConfigParser().get('bitmessagesettings', 'sockspassword')))
QtCore.QObject.connect(self.ui.comboBoxProxyType, QtCore.SIGNAL(
"currentIndexChanged(int)"), self.comboBoxProxyTypeChanged)
self.ui.lineEditMaxDownloadRate.setText(str(
BMConfigParser().get('bitmessagesettings', 'maxdownloadrate')))
self.ui.lineEditMaxUploadRate.setText(str(
BMConfigParser().get('bitmessagesettings', 'maxuploadrate')))
self.ui.lineEditMaxOutboundConnections.setText(str(
BMConfigParser().get('bitmessagesettings', 'maxoutboundconnections')))
# Demanded difficulty tab
self.ui.lineEditTotalDifficulty.setText(str((float(BMConfigParser().getint(
'bitmessagesettings', 'defaultnoncetrialsperbyte')) / defaults.networkDefaultProofOfWorkNonceTrialsPerByte)))
self.ui.lineEditSmallMessageDifficulty.setText(str((float(BMConfigParser().getint(
'bitmessagesettings', 'defaultpayloadlengthextrabytes')) / defaults.networkDefaultPayloadLengthExtraBytes)))
# Max acceptable difficulty tab
self.ui.lineEditMaxAcceptableTotalDifficulty.setText(str((float(BMConfigParser().getint(
'bitmessagesettings', 'maxacceptablenoncetrialsperbyte')) / defaults.networkDefaultProofOfWorkNonceTrialsPerByte)))
self.ui.lineEditMaxAcceptableSmallMessageDifficulty.setText(str((float(BMConfigParser().getint(
'bitmessagesettings', 'maxacceptablepayloadlengthextrabytes')) / defaults.networkDefaultPayloadLengthExtraBytes)))
# OpenCL
if openclpow.openclAvailable():
self.ui.comboBoxOpenCL.setEnabled(True)
else:
self.ui.comboBoxOpenCL.setEnabled(False)
self.ui.comboBoxOpenCL.clear()
self.ui.comboBoxOpenCL.addItem("None")
self.ui.comboBoxOpenCL.addItems(openclpow.vendors)
self.ui.comboBoxOpenCL.setCurrentIndex(0)
for i in range(self.ui.comboBoxOpenCL.count()):
if self.ui.comboBoxOpenCL.itemText(i) == BMConfigParser().safeGet('bitmessagesettings', 'opencl'):
self.ui.comboBoxOpenCL.setCurrentIndex(i)
break
# Namecoin integration tab
nmctype = BMConfigParser().get('bitmessagesettings', 'namecoinrpctype')
self.ui.lineEditNamecoinHost.setText(str(
BMConfigParser().get('bitmessagesettings', 'namecoinrpchost')))
self.ui.lineEditNamecoinPort.setText(str(
BMConfigParser().get('bitmessagesettings', 'namecoinrpcport')))
self.ui.lineEditNamecoinUser.setText(str(
BMConfigParser().get('bitmessagesettings', 'namecoinrpcuser')))
self.ui.lineEditNamecoinPassword.setText(str(
BMConfigParser().get('bitmessagesettings', 'namecoinrpcpassword')))
if nmctype == "namecoind":
self.ui.radioButtonNamecoinNamecoind.setChecked(True)
elif nmctype == "nmcontrol":
self.ui.radioButtonNamecoinNmcontrol.setChecked(True)
self.ui.lineEditNamecoinUser.setEnabled(False)
self.ui.labelNamecoinUser.setEnabled(False)
self.ui.lineEditNamecoinPassword.setEnabled(False)
self.ui.labelNamecoinPassword.setEnabled(False)
else:
assert False
QtCore.QObject.connect(self.ui.radioButtonNamecoinNamecoind, QtCore.SIGNAL(
"toggled(bool)"), self.namecoinTypeChanged)
QtCore.QObject.connect(self.ui.radioButtonNamecoinNmcontrol, QtCore.SIGNAL(
"toggled(bool)"), self.namecoinTypeChanged)
QtCore.QObject.connect(self.ui.pushButtonNamecoinTest, QtCore.SIGNAL(
"clicked()"), self.click_pushButtonNamecoinTest)
#Message Resend tab
self.ui.lineEditDays.setText(str(
BMConfigParser().get('bitmessagesettings', 'stopresendingafterxdays')))
self.ui.lineEditMonths.setText(str(
BMConfigParser().get('bitmessagesettings', 'stopresendingafterxmonths')))
#'System' tab removed for now.
"""try:
maxCores = BMConfigParser().getint('bitmessagesettings', 'maxcores')
except:
maxCores = 99999
if maxCores <= 1:
self.ui.comboBoxMaxCores.setCurrentIndex(0)
elif maxCores == 2:
self.ui.comboBoxMaxCores.setCurrentIndex(1)
elif maxCores <= 4:
self.ui.comboBoxMaxCores.setCurrentIndex(2)
elif maxCores <= 8:
self.ui.comboBoxMaxCores.setCurrentIndex(3)
elif maxCores <= 16:
self.ui.comboBoxMaxCores.setCurrentIndex(4)
else:
self.ui.comboBoxMaxCores.setCurrentIndex(5)"""
QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self))
def comboBoxProxyTypeChanged(self, comboBoxIndex):
if comboBoxIndex == 0:
self.ui.lineEditSocksHostname.setEnabled(False)
self.ui.lineEditSocksPort.setEnabled(False)
self.ui.lineEditSocksUsername.setEnabled(False)
self.ui.lineEditSocksPassword.setEnabled(False)
self.ui.checkBoxAuthentication.setEnabled(False)
self.ui.checkBoxSocksListen.setEnabled(False)
elif comboBoxIndex == 1 or comboBoxIndex == 2:
self.ui.lineEditSocksHostname.setEnabled(True)
self.ui.lineEditSocksPort.setEnabled(True)
self.ui.checkBoxAuthentication.setEnabled(True)
self.ui.checkBoxSocksListen.setEnabled(True)
if self.ui.checkBoxAuthentication.isChecked():
self.ui.lineEditSocksUsername.setEnabled(True)
self.ui.lineEditSocksPassword.setEnabled(True)
# Check status of namecoin integration radio buttons and translate
# it to a string as in the options.
def getNamecoinType(self):
if self.ui.radioButtonNamecoinNamecoind.isChecked():
return "namecoind"
if self.ui.radioButtonNamecoinNmcontrol.isChecked():
return "nmcontrol"
assert False
# Namecoin connection type was changed.
def namecoinTypeChanged(self, checked):
nmctype = self.getNamecoinType()
assert nmctype == "namecoind" or nmctype == "nmcontrol"
isNamecoind = (nmctype == "namecoind")
self.ui.lineEditNamecoinUser.setEnabled(isNamecoind)
self.ui.labelNamecoinUser.setEnabled(isNamecoind)
self.ui.lineEditNamecoinPassword.setEnabled(isNamecoind)
self.ui.labelNamecoinPassword.setEnabled(isNamecoind)
if isNamecoind:
self.ui.lineEditNamecoinPort.setText(defaults.namecoinDefaultRpcPort)
else:
self.ui.lineEditNamecoinPort.setText("9000")
def click_pushButtonNamecoinTest(self):
"""Test the namecoin settings specified in the settings dialog."""
self.ui.labelNamecoinTestResult.setText(_translate(
"MainWindow", "Testing..."))
options = {}
options["type"] = self.getNamecoinType()
options["host"] = str(self.ui.lineEditNamecoinHost.text().toUtf8())
options["port"] = str(self.ui.lineEditNamecoinPort.text().toUtf8())
options["user"] = str(self.ui.lineEditNamecoinUser.text().toUtf8())
options["password"] = str(self.ui.lineEditNamecoinPassword.text().toUtf8())
nc = namecoin.namecoinConnection(options)
status, text = nc.test()
self.ui.labelNamecoinTestResult.setText(text)
if status == 'success':
self.parent.namecoin = nc
# In order for the time columns on the Inbox and Sent tabs to be sorted # In order for the time columns on the Inbox and Sent tabs to be sorted
# correctly (rather than alphabetically), we need to overload the < # correctly (rather than alphabetically), we need to overload the <
# operator and use this class instead of QTableWidgetItem. # operator and use this class instead of QTableWidgetItem.
@ -4558,7 +4137,6 @@ def init():
def run(): def run():
global myapp global myapp
app = init() app = init()
change_translation(l10n.getTranslationLanguage())
app.setStyleSheet("QStatusBar::item { border: 0px solid black }") app.setStyleSheet("QStatusBar::item { border: 0px solid black }")
myapp = MyForm() myapp = MyForm()

View File

@ -5,22 +5,25 @@ src/bitmessageqt/dialogs.py
from PyQt4 import QtGui from PyQt4 import QtGui
from version import softwareVersion
import paths import paths
import widgets import widgets
from address_dialogs import ( from address_dialogs import (
AddAddressDialog, EmailGatewayDialog, NewAddressDialog, NewSubscriptionDialog, RegenerateAddressesDialog, AddAddressDialog, EmailGatewayDialog, NewAddressDialog,
NewSubscriptionDialog, RegenerateAddressesDialog,
SpecialAddressBehaviorDialog SpecialAddressBehaviorDialog
) )
from newchandialog import NewChanDialog from newchandialog import NewChanDialog
from retranslateui import RetranslateMixin from retranslateui import RetranslateMixin
from settings import SettingsDialog
from tr import _translate from tr import _translate
from version import softwareVersion
__all__ = [ __all__ = [
"NewChanDialog", "AddAddressDialog", "NewAddressDialog", "NewChanDialog", "AddAddressDialog", "NewAddressDialog",
"NewSubscriptionDialog", "RegenerateAddressesDialog", "NewSubscriptionDialog", "RegenerateAddressesDialog",
"SpecialAddressBehaviorDialog", "EmailGatewayDialog" "SpecialAddressBehaviorDialog", "EmailGatewayDialog",
"SettingsDialog"
] ]

View File

@ -1,630 +1,546 @@
# -*- coding: utf-8 -*- import os
# pylint: disable=too-many-instance-attributes,too-many-locals,too-many-statements,attribute-defined-outside-init import sys
"""
src/bitmessageqt/settings.py
============================
Form implementation generated from reading ui file 'settings.ui'
Created: Thu Dec 25 23:21:20 2014
by: PyQt4 UI code generator 4.10.3
WARNING! All changes made in this file will be lost!
"""
from sys import platform
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
from . import bitmessage_icons_rc # pylint: disable=unused-import import debug
from .languagebox import LanguageBox import defaults
import knownnodes
try: import namecoin
_fromUtf8 = QtCore.QString.fromUtf8 import openclpow
except AttributeError: import paths
def _fromUtf8(s): import queues
return s import shared
import state
try: import tempfile
_encoding = QtGui.QApplication.UnicodeUTF8 import widgets
from bmconfigparser import BMConfigParser
def _translate(context, text, disambig): from helper_sql import sqlExecute, sqlStoredProcedure
return QtGui.QApplication.translate(context, text, disambig, _encoding) from network.asyncore_pollchoose import set_rates
except AttributeError: from tr import _translate
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig)
class Ui_settingsDialog(object): class SettingsDialog(QtGui.QDialog):
"""Encapsulate a UI settings dialog object""" """The "Settings" dialog"""
def __init__(self, parent=None, firstrun=False):
super(SettingsDialog, self).__init__(parent)
widgets.load('settings.ui', self)
def setupUi(self, settingsDialog): self.parent = parent
"""Set up the UI""" self.firstrun = firstrun
self.config = BMConfigParser()
self.net_restart_needed = False
self.timer = QtCore.QTimer()
settingsDialog.setObjectName(_fromUtf8("settingsDialog"))
settingsDialog.resize(521, 413)
self.gridLayout = QtGui.QGridLayout(settingsDialog)
self.gridLayout.setObjectName(_fromUtf8("gridLayout"))
self.buttonBox = QtGui.QDialogButtonBox(settingsDialog)
self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel | QtGui.QDialogButtonBox.Ok)
self.buttonBox.setObjectName(_fromUtf8("buttonBox"))
self.gridLayout.addWidget(self.buttonBox, 1, 0, 1, 1)
self.tabWidgetSettings = QtGui.QTabWidget(settingsDialog)
self.tabWidgetSettings.setObjectName(_fromUtf8("tabWidgetSettings"))
self.tabUserInterface = QtGui.QWidget()
self.tabUserInterface.setEnabled(True)
self.tabUserInterface.setObjectName(_fromUtf8("tabUserInterface"))
self.formLayout = QtGui.QFormLayout(self.tabUserInterface)
self.formLayout.setObjectName(_fromUtf8("formLayout"))
self.checkBoxStartOnLogon = QtGui.QCheckBox(self.tabUserInterface)
self.checkBoxStartOnLogon.setObjectName(_fromUtf8("checkBoxStartOnLogon"))
self.formLayout.setWidget(0, QtGui.QFormLayout.LabelRole, self.checkBoxStartOnLogon)
self.groupBoxTray = QtGui.QGroupBox(self.tabUserInterface)
self.groupBoxTray.setObjectName(_fromUtf8("groupBoxTray"))
self.formLayoutTray = QtGui.QFormLayout(self.groupBoxTray)
self.formLayoutTray.setObjectName(_fromUtf8("formLayoutTray"))
self.checkBoxStartInTray = QtGui.QCheckBox(self.groupBoxTray)
self.checkBoxStartInTray.setObjectName(_fromUtf8("checkBoxStartInTray"))
self.formLayoutTray.setWidget(0, QtGui.QFormLayout.SpanningRole, self.checkBoxStartInTray)
self.checkBoxMinimizeToTray = QtGui.QCheckBox(self.groupBoxTray)
self.checkBoxMinimizeToTray.setChecked(True)
self.checkBoxMinimizeToTray.setObjectName(_fromUtf8("checkBoxMinimizeToTray"))
self.formLayoutTray.setWidget(1, QtGui.QFormLayout.LabelRole, self.checkBoxMinimizeToTray)
self.checkBoxTrayOnClose = QtGui.QCheckBox(self.groupBoxTray)
self.checkBoxTrayOnClose.setChecked(True)
self.checkBoxTrayOnClose.setObjectName(_fromUtf8("checkBoxTrayOnClose"))
self.formLayoutTray.setWidget(2, QtGui.QFormLayout.LabelRole, self.checkBoxTrayOnClose)
self.formLayout.setWidget(1, QtGui.QFormLayout.SpanningRole, self.groupBoxTray)
self.checkBoxHideTrayConnectionNotifications = QtGui.QCheckBox(self.tabUserInterface)
self.checkBoxHideTrayConnectionNotifications.setChecked(False)
self.checkBoxHideTrayConnectionNotifications.setObjectName(
_fromUtf8("checkBoxHideTrayConnectionNotifications"))
self.formLayout.setWidget(2, QtGui.QFormLayout.LabelRole, self.checkBoxHideTrayConnectionNotifications)
self.checkBoxShowTrayNotifications = QtGui.QCheckBox(self.tabUserInterface)
self.checkBoxShowTrayNotifications.setObjectName(_fromUtf8("checkBoxShowTrayNotifications"))
self.formLayout.setWidget(3, QtGui.QFormLayout.LabelRole, self.checkBoxShowTrayNotifications)
self.checkBoxPortableMode = QtGui.QCheckBox(self.tabUserInterface)
self.checkBoxPortableMode.setObjectName(_fromUtf8("checkBoxPortableMode"))
self.formLayout.setWidget(4, QtGui.QFormLayout.LabelRole, self.checkBoxPortableMode)
self.PortableModeDescription = QtGui.QLabel(self.tabUserInterface)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.PortableModeDescription.sizePolicy().hasHeightForWidth())
self.PortableModeDescription.setSizePolicy(sizePolicy)
self.PortableModeDescription.setWordWrap(True)
self.PortableModeDescription.setObjectName(_fromUtf8("PortableModeDescription"))
self.formLayout.setWidget(5, QtGui.QFormLayout.SpanningRole, self.PortableModeDescription)
self.checkBoxWillinglySendToMobile = QtGui.QCheckBox(self.tabUserInterface)
self.checkBoxWillinglySendToMobile.setObjectName(_fromUtf8("checkBoxWillinglySendToMobile"))
self.formLayout.setWidget(6, QtGui.QFormLayout.SpanningRole, self.checkBoxWillinglySendToMobile)
self.checkBoxUseIdenticons = QtGui.QCheckBox(self.tabUserInterface)
self.checkBoxUseIdenticons.setObjectName(_fromUtf8("checkBoxUseIdenticons"))
self.formLayout.setWidget(7, QtGui.QFormLayout.LabelRole, self.checkBoxUseIdenticons)
self.checkBoxReplyBelow = QtGui.QCheckBox(self.tabUserInterface)
self.checkBoxReplyBelow.setObjectName(_fromUtf8("checkBoxReplyBelow"))
self.formLayout.setWidget(8, QtGui.QFormLayout.LabelRole, self.checkBoxReplyBelow)
self.groupBox = QtGui.QGroupBox(self.tabUserInterface)
self.groupBox.setObjectName(_fromUtf8("groupBox"))
self.formLayout_2 = QtGui.QFormLayout(self.groupBox)
self.formLayout_2.setObjectName(_fromUtf8("formLayout_2"))
self.languageComboBox = LanguageBox(self.groupBox)
self.languageComboBox.setMinimumSize(QtCore.QSize(100, 0))
self.languageComboBox.setObjectName(_fromUtf8("languageComboBox")) # pylint: disable=not-callable
self.formLayout_2.setWidget(0, QtGui.QFormLayout.LabelRole, self.languageComboBox)
self.formLayout.setWidget(9, QtGui.QFormLayout.FieldRole, self.groupBox)
self.tabWidgetSettings.addTab(self.tabUserInterface, _fromUtf8(""))
self.tabNetworkSettings = QtGui.QWidget()
self.tabNetworkSettings.setObjectName(_fromUtf8("tabNetworkSettings"))
self.gridLayout_4 = QtGui.QGridLayout(self.tabNetworkSettings)
self.gridLayout_4.setObjectName(_fromUtf8("gridLayout_4"))
self.groupBox1 = QtGui.QGroupBox(self.tabNetworkSettings)
self.groupBox1.setObjectName(_fromUtf8("groupBox1"))
self.gridLayout_3 = QtGui.QGridLayout(self.groupBox1)
self.gridLayout_3.setObjectName(_fromUtf8("gridLayout_3"))
self.label = QtGui.QLabel(self.groupBox1)
self.label.setObjectName(_fromUtf8("label"))
self.gridLayout_3.addWidget(self.label, 0, 0, 1, 1, QtCore.Qt.AlignRight)
self.lineEditTCPPort = QtGui.QLineEdit(self.groupBox1)
self.lineEditTCPPort.setMaximumSize(QtCore.QSize(70, 16777215))
self.lineEditTCPPort.setObjectName(_fromUtf8("lineEditTCPPort"))
self.gridLayout_3.addWidget(self.lineEditTCPPort, 0, 1, 1, 1, QtCore.Qt.AlignLeft)
self.labelUPnP = QtGui.QLabel(self.groupBox1)
self.labelUPnP.setObjectName(_fromUtf8("labelUPnP"))
self.gridLayout_3.addWidget(self.labelUPnP, 0, 2, 1, 1, QtCore.Qt.AlignRight)
self.checkBoxUPnP = QtGui.QCheckBox(self.groupBox1)
self.checkBoxUPnP.setObjectName(_fromUtf8("checkBoxUPnP"))
self.gridLayout_3.addWidget(self.checkBoxUPnP, 0, 3, 1, 1, QtCore.Qt.AlignLeft)
self.gridLayout_4.addWidget(self.groupBox1, 0, 0, 1, 1)
self.groupBox_3 = QtGui.QGroupBox(self.tabNetworkSettings)
self.groupBox_3.setObjectName(_fromUtf8("groupBox_3"))
self.gridLayout_9 = QtGui.QGridLayout(self.groupBox_3)
self.gridLayout_9.setObjectName(_fromUtf8("gridLayout_9"))
spacerItem1 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.gridLayout_9.addItem(spacerItem1, 0, 0, 2, 1)
self.label_24 = QtGui.QLabel(self.groupBox_3)
self.label_24.setObjectName(_fromUtf8("label_24"))
self.gridLayout_9.addWidget(self.label_24, 0, 1, 1, 1)
self.lineEditMaxDownloadRate = QtGui.QLineEdit(self.groupBox_3)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.lineEditMaxDownloadRate.sizePolicy().hasHeightForWidth())
self.lineEditMaxDownloadRate.setSizePolicy(sizePolicy)
self.lineEditMaxDownloadRate.setMaximumSize(QtCore.QSize(60, 16777215))
self.lineEditMaxDownloadRate.setObjectName(_fromUtf8("lineEditMaxDownloadRate"))
self.gridLayout_9.addWidget(self.lineEditMaxDownloadRate, 0, 2, 1, 1)
self.label_25 = QtGui.QLabel(self.groupBox_3)
self.label_25.setObjectName(_fromUtf8("label_25"))
self.gridLayout_9.addWidget(self.label_25, 1, 1, 1, 1)
self.lineEditMaxUploadRate = QtGui.QLineEdit(self.groupBox_3)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.lineEditMaxUploadRate.sizePolicy().hasHeightForWidth())
self.lineEditMaxUploadRate.setSizePolicy(sizePolicy)
self.lineEditMaxUploadRate.setMaximumSize(QtCore.QSize(60, 16777215))
self.lineEditMaxUploadRate.setObjectName(_fromUtf8("lineEditMaxUploadRate"))
self.gridLayout_9.addWidget(self.lineEditMaxUploadRate, 1, 2, 1, 1)
self.label_26 = QtGui.QLabel(self.groupBox_3)
self.label_26.setObjectName(_fromUtf8("label_26"))
self.gridLayout_9.addWidget(self.label_26, 2, 1, 1, 1)
self.lineEditMaxOutboundConnections = QtGui.QLineEdit(self.groupBox_3)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.lineEditMaxOutboundConnections.sizePolicy().hasHeightForWidth())
self.lineEditMaxOutboundConnections.setSizePolicy(sizePolicy)
self.lineEditMaxOutboundConnections.setMaximumSize(QtCore.QSize(60, 16777215))
self.lineEditMaxOutboundConnections.setObjectName(_fromUtf8("lineEditMaxOutboundConnections"))
self.lineEditMaxOutboundConnections.setValidator( self.lineEditMaxOutboundConnections.setValidator(
QtGui.QIntValidator(0, 8, self.lineEditMaxOutboundConnections)) QtGui.QIntValidator(0, 8, self.lineEditMaxOutboundConnections))
self.gridLayout_9.addWidget(self.lineEditMaxOutboundConnections, 2, 2, 1, 1)
self.gridLayout_4.addWidget(self.groupBox_3, 2, 0, 1, 1) self.adjust_from_config(self.config)
self.groupBox_2 = QtGui.QGroupBox(self.tabNetworkSettings) if firstrun:
self.groupBox_2.setObjectName(_fromUtf8("groupBox_2")) # switch to "Network Settings" tab if user selected
self.gridLayout_2 = QtGui.QGridLayout(self.groupBox_2) # "Let me configure special network settings first" on first run
self.gridLayout_2.setObjectName(_fromUtf8("gridLayout_2")) self.tabWidgetSettings.setCurrentIndex(
self.label_2 = QtGui.QLabel(self.groupBox_2) self.tabWidgetSettings.indexOf(self.tabNetworkSettings)
self.label_2.setObjectName(_fromUtf8("label_2")) )
self.gridLayout_2.addWidget(self.label_2, 0, 0, 1, 1) QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self))
self.label_3 = QtGui.QLabel(self.groupBox_2)
self.label_3.setObjectName(_fromUtf8("label_3")) def adjust_from_config(self, config):
self.gridLayout_2.addWidget(self.label_3, 1, 1, 1, 1) """Adjust all widgets state according to config settings"""
self.lineEditSocksHostname = QtGui.QLineEdit(self.groupBox_2) # pylint: disable=too-many-branches,too-many-statements
self.lineEditSocksHostname.setObjectName(_fromUtf8("lineEditSocksHostname")) self.checkBoxStartOnLogon.setChecked(
self.lineEditSocksHostname.setPlaceholderText(_fromUtf8("127.0.0.1")) config.getboolean('bitmessagesettings', 'startonlogon'))
self.gridLayout_2.addWidget(self.lineEditSocksHostname, 1, 2, 1, 2) self.checkBoxMinimizeToTray.setChecked(
self.label_4 = QtGui.QLabel(self.groupBox_2) config.getboolean('bitmessagesettings', 'minimizetotray'))
self.label_4.setObjectName(_fromUtf8("label_4")) self.checkBoxTrayOnClose.setChecked(
self.gridLayout_2.addWidget(self.label_4, 1, 4, 1, 1) config.safeGetBoolean('bitmessagesettings', 'trayonclose'))
self.lineEditSocksPort = QtGui.QLineEdit(self.groupBox_2) self.checkBoxHideTrayConnectionNotifications.setChecked(
self.lineEditSocksPort.setObjectName(_fromUtf8("lineEditSocksPort")) config.getboolean("bitmessagesettings", "hidetrayconnectionnotifications"))
if platform in ['darwin', 'win32', 'win64']: self.checkBoxShowTrayNotifications.setChecked(
self.lineEditSocksPort.setPlaceholderText(_fromUtf8("9150")) config.getboolean('bitmessagesettings', 'showtraynotifications'))
self.checkBoxStartInTray.setChecked(
config.getboolean('bitmessagesettings', 'startintray'))
self.checkBoxWillinglySendToMobile.setChecked(
config.safeGetBoolean('bitmessagesettings', 'willinglysendtomobile'))
self.checkBoxUseIdenticons.setChecked(
config.safeGetBoolean('bitmessagesettings', 'useidenticons'))
self.checkBoxReplyBelow.setChecked(
config.safeGetBoolean('bitmessagesettings', 'replybelow'))
if state.appdata == paths.lookupExeFolder():
self.checkBoxPortableMode.setChecked(True)
else: else:
self.lineEditSocksPort.setPlaceholderText(_fromUtf8("9050")) try:
self.gridLayout_2.addWidget(self.lineEditSocksPort, 1, 5, 1, 1) tempfile.NamedTemporaryFile(
self.checkBoxAuthentication = QtGui.QCheckBox(self.groupBox_2) dir=paths.lookupExeFolder(), delete=True
self.checkBoxAuthentication.setObjectName(_fromUtf8("checkBoxAuthentication")) ).close() # should autodelete
self.gridLayout_2.addWidget(self.checkBoxAuthentication, 2, 1, 1, 1) except:
self.label_5 = QtGui.QLabel(self.groupBox_2) self.checkBoxPortableMode.setDisabled(True)
self.label_5.setObjectName(_fromUtf8("label_5"))
self.gridLayout_2.addWidget(self.label_5, 2, 2, 1, 1)
self.lineEditSocksUsername = QtGui.QLineEdit(self.groupBox_2)
self.lineEditSocksUsername.setEnabled(False)
self.lineEditSocksUsername.setObjectName(_fromUtf8("lineEditSocksUsername"))
self.gridLayout_2.addWidget(self.lineEditSocksUsername, 2, 3, 1, 1)
self.label_6 = QtGui.QLabel(self.groupBox_2)
self.label_6.setObjectName(_fromUtf8("label_6"))
self.gridLayout_2.addWidget(self.label_6, 2, 4, 1, 1)
self.lineEditSocksPassword = QtGui.QLineEdit(self.groupBox_2)
self.lineEditSocksPassword.setEnabled(False)
self.lineEditSocksPassword.setInputMethodHints(
QtCore.Qt.ImhHiddenText | QtCore.Qt.ImhNoAutoUppercase | QtCore.Qt.ImhNoPredictiveText)
self.lineEditSocksPassword.setEchoMode(QtGui.QLineEdit.Password)
self.lineEditSocksPassword.setObjectName(_fromUtf8("lineEditSocksPassword"))
self.gridLayout_2.addWidget(self.lineEditSocksPassword, 2, 5, 1, 1)
self.checkBoxSocksListen = QtGui.QCheckBox(self.groupBox_2)
self.checkBoxSocksListen.setObjectName(_fromUtf8("checkBoxSocksListen"))
self.gridLayout_2.addWidget(self.checkBoxSocksListen, 3, 1, 1, 4)
self.comboBoxProxyType = QtGui.QComboBox(self.groupBox_2)
self.comboBoxProxyType.setObjectName(_fromUtf8("comboBoxProxyType")) # pylint: disable=not-callable
self.comboBoxProxyType.addItem(_fromUtf8(""))
self.comboBoxProxyType.addItem(_fromUtf8(""))
self.comboBoxProxyType.addItem(_fromUtf8(""))
self.gridLayout_2.addWidget(self.comboBoxProxyType, 0, 1, 1, 1)
self.gridLayout_4.addWidget(self.groupBox_2, 1, 0, 1, 1)
spacerItem2 = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
self.gridLayout_4.addItem(spacerItem2, 3, 0, 1, 1)
self.tabWidgetSettings.addTab(self.tabNetworkSettings, _fromUtf8(""))
self.tabDemandedDifficulty = QtGui.QWidget()
self.tabDemandedDifficulty.setObjectName(_fromUtf8("tabDemandedDifficulty"))
self.gridLayout_6 = QtGui.QGridLayout(self.tabDemandedDifficulty)
self.gridLayout_6.setObjectName(_fromUtf8("gridLayout_6"))
self.label_9 = QtGui.QLabel(self.tabDemandedDifficulty)
self.label_9.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter)
self.label_9.setObjectName(_fromUtf8("label_9"))
self.gridLayout_6.addWidget(self.label_9, 1, 1, 1, 1)
self.label_10 = QtGui.QLabel(self.tabDemandedDifficulty)
self.label_10.setWordWrap(True)
self.label_10.setObjectName(_fromUtf8("label_10"))
self.gridLayout_6.addWidget(self.label_10, 2, 0, 1, 3)
self.label_11 = QtGui.QLabel(self.tabDemandedDifficulty)
self.label_11.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter)
self.label_11.setObjectName(_fromUtf8("label_11"))
self.gridLayout_6.addWidget(self.label_11, 3, 1, 1, 1)
self.label_8 = QtGui.QLabel(self.tabDemandedDifficulty)
self.label_8.setWordWrap(True)
self.label_8.setObjectName(_fromUtf8("label_8"))
self.gridLayout_6.addWidget(self.label_8, 0, 0, 1, 3)
spacerItem3 = QtGui.QSpacerItem(203, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.gridLayout_6.addItem(spacerItem3, 1, 0, 1, 1)
self.label_12 = QtGui.QLabel(self.tabDemandedDifficulty)
self.label_12.setWordWrap(True)
self.label_12.setObjectName(_fromUtf8("label_12"))
self.gridLayout_6.addWidget(self.label_12, 4, 0, 1, 3)
self.lineEditSmallMessageDifficulty = QtGui.QLineEdit(self.tabDemandedDifficulty)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.lineEditSmallMessageDifficulty.sizePolicy().hasHeightForWidth())
self.lineEditSmallMessageDifficulty.setSizePolicy(sizePolicy)
self.lineEditSmallMessageDifficulty.setMaximumSize(QtCore.QSize(70, 16777215))
self.lineEditSmallMessageDifficulty.setObjectName(_fromUtf8("lineEditSmallMessageDifficulty"))
self.gridLayout_6.addWidget(self.lineEditSmallMessageDifficulty, 3, 2, 1, 1)
self.lineEditTotalDifficulty = QtGui.QLineEdit(self.tabDemandedDifficulty)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.lineEditTotalDifficulty.sizePolicy().hasHeightForWidth())
self.lineEditTotalDifficulty.setSizePolicy(sizePolicy)
self.lineEditTotalDifficulty.setMaximumSize(QtCore.QSize(70, 16777215))
self.lineEditTotalDifficulty.setObjectName(_fromUtf8("lineEditTotalDifficulty"))
self.gridLayout_6.addWidget(self.lineEditTotalDifficulty, 1, 2, 1, 1)
spacerItem4 = QtGui.QSpacerItem(203, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.gridLayout_6.addItem(spacerItem4, 3, 0, 1, 1)
spacerItem5 = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
self.gridLayout_6.addItem(spacerItem5, 5, 0, 1, 1)
self.tabWidgetSettings.addTab(self.tabDemandedDifficulty, _fromUtf8(""))
self.tabMaxAcceptableDifficulty = QtGui.QWidget()
self.tabMaxAcceptableDifficulty.setObjectName(_fromUtf8("tabMaxAcceptableDifficulty"))
self.gridLayout_7 = QtGui.QGridLayout(self.tabMaxAcceptableDifficulty)
self.gridLayout_7.setObjectName(_fromUtf8("gridLayout_7"))
self.label_15 = QtGui.QLabel(self.tabMaxAcceptableDifficulty)
self.label_15.setWordWrap(True)
self.label_15.setObjectName(_fromUtf8("label_15"))
self.gridLayout_7.addWidget(self.label_15, 0, 0, 1, 3)
spacerItem6 = QtGui.QSpacerItem(102, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.gridLayout_7.addItem(spacerItem6, 1, 0, 1, 1)
self.label_13 = QtGui.QLabel(self.tabMaxAcceptableDifficulty)
self.label_13.setLayoutDirection(QtCore.Qt.LeftToRight)
self.label_13.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter)
self.label_13.setObjectName(_fromUtf8("label_13"))
self.gridLayout_7.addWidget(self.label_13, 1, 1, 1, 1)
self.lineEditMaxAcceptableTotalDifficulty = QtGui.QLineEdit(self.tabMaxAcceptableDifficulty)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.lineEditMaxAcceptableTotalDifficulty.sizePolicy().hasHeightForWidth())
self.lineEditMaxAcceptableTotalDifficulty.setSizePolicy(sizePolicy)
self.lineEditMaxAcceptableTotalDifficulty.setMaximumSize(QtCore.QSize(70, 16777215))
self.lineEditMaxAcceptableTotalDifficulty.setObjectName(_fromUtf8("lineEditMaxAcceptableTotalDifficulty"))
self.gridLayout_7.addWidget(self.lineEditMaxAcceptableTotalDifficulty, 1, 2, 1, 1)
spacerItem7 = QtGui.QSpacerItem(102, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.gridLayout_7.addItem(spacerItem7, 2, 0, 1, 1)
self.label_14 = QtGui.QLabel(self.tabMaxAcceptableDifficulty)
self.label_14.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter)
self.label_14.setObjectName(_fromUtf8("label_14"))
self.gridLayout_7.addWidget(self.label_14, 2, 1, 1, 1)
self.lineEditMaxAcceptableSmallMessageDifficulty = QtGui.QLineEdit(self.tabMaxAcceptableDifficulty)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.lineEditMaxAcceptableSmallMessageDifficulty.sizePolicy().hasHeightForWidth())
self.lineEditMaxAcceptableSmallMessageDifficulty.setSizePolicy(sizePolicy)
self.lineEditMaxAcceptableSmallMessageDifficulty.setMaximumSize(QtCore.QSize(70, 16777215))
self.lineEditMaxAcceptableSmallMessageDifficulty.setObjectName(
_fromUtf8("lineEditMaxAcceptableSmallMessageDifficulty"))
self.gridLayout_7.addWidget(self.lineEditMaxAcceptableSmallMessageDifficulty, 2, 2, 1, 1)
spacerItem8 = QtGui.QSpacerItem(20, 147, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
self.gridLayout_7.addItem(spacerItem8, 3, 1, 1, 1)
self.labelOpenCL = QtGui.QLabel(self.tabMaxAcceptableDifficulty)
self.labelOpenCL.setObjectName(_fromUtf8("labelOpenCL"))
self.gridLayout_7.addWidget(self.labelOpenCL, 4, 0, 1, 1)
self.comboBoxOpenCL = QtGui.QComboBox(self.tabMaxAcceptableDifficulty)
self.comboBoxOpenCL.setObjectName = (_fromUtf8("comboBoxOpenCL"))
self.gridLayout_7.addWidget(self.comboBoxOpenCL, 4, 1, 1, 1)
self.tabWidgetSettings.addTab(self.tabMaxAcceptableDifficulty, _fromUtf8(""))
self.tabNamecoin = QtGui.QWidget()
self.tabNamecoin.setObjectName(_fromUtf8("tabNamecoin"))
self.gridLayout_8 = QtGui.QGridLayout(self.tabNamecoin)
self.gridLayout_8.setObjectName(_fromUtf8("gridLayout_8"))
spacerItem9 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.gridLayout_8.addItem(spacerItem9, 2, 0, 1, 1)
self.label_16 = QtGui.QLabel(self.tabNamecoin)
self.label_16.setWordWrap(True)
self.label_16.setObjectName(_fromUtf8("label_16"))
self.gridLayout_8.addWidget(self.label_16, 0, 0, 1, 3)
self.label_17 = QtGui.QLabel(self.tabNamecoin)
self.label_17.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter)
self.label_17.setObjectName(_fromUtf8("label_17"))
self.gridLayout_8.addWidget(self.label_17, 2, 1, 1, 1)
self.lineEditNamecoinHost = QtGui.QLineEdit(self.tabNamecoin)
self.lineEditNamecoinHost.setObjectName(_fromUtf8("lineEditNamecoinHost"))
self.gridLayout_8.addWidget(self.lineEditNamecoinHost, 2, 2, 1, 1)
spacerItem10 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.gridLayout_8.addItem(spacerItem10, 3, 0, 1, 1)
spacerItem11 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.gridLayout_8.addItem(spacerItem11, 4, 0, 1, 1)
self.label_18 = QtGui.QLabel(self.tabNamecoin)
self.label_18.setEnabled(True)
self.label_18.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter)
self.label_18.setObjectName(_fromUtf8("label_18"))
self.gridLayout_8.addWidget(self.label_18, 3, 1, 1, 1)
self.lineEditNamecoinPort = QtGui.QLineEdit(self.tabNamecoin)
self.lineEditNamecoinPort.setObjectName(_fromUtf8("lineEditNamecoinPort"))
self.gridLayout_8.addWidget(self.lineEditNamecoinPort, 3, 2, 1, 1)
spacerItem12 = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
self.gridLayout_8.addItem(spacerItem12, 8, 1, 1, 1)
self.labelNamecoinUser = QtGui.QLabel(self.tabNamecoin)
self.labelNamecoinUser.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter)
self.labelNamecoinUser.setObjectName(_fromUtf8("labelNamecoinUser"))
self.gridLayout_8.addWidget(self.labelNamecoinUser, 4, 1, 1, 1)
self.lineEditNamecoinUser = QtGui.QLineEdit(self.tabNamecoin)
self.lineEditNamecoinUser.setObjectName(_fromUtf8("lineEditNamecoinUser"))
self.gridLayout_8.addWidget(self.lineEditNamecoinUser, 4, 2, 1, 1)
spacerItem13 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.gridLayout_8.addItem(spacerItem13, 5, 0, 1, 1)
self.labelNamecoinPassword = QtGui.QLabel(self.tabNamecoin)
self.labelNamecoinPassword.setAlignment(
QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter)
self.labelNamecoinPassword.setObjectName(_fromUtf8("labelNamecoinPassword"))
self.gridLayout_8.addWidget(self.labelNamecoinPassword, 5, 1, 1, 1)
self.lineEditNamecoinPassword = QtGui.QLineEdit(self.tabNamecoin)
self.lineEditNamecoinPassword.setInputMethodHints(
QtCore.Qt.ImhHiddenText | QtCore.Qt.ImhNoAutoUppercase | QtCore.Qt.ImhNoPredictiveText)
self.lineEditNamecoinPassword.setEchoMode(QtGui.QLineEdit.Password)
self.lineEditNamecoinPassword.setObjectName(_fromUtf8("lineEditNamecoinPassword"))
self.gridLayout_8.addWidget(self.lineEditNamecoinPassword, 5, 2, 1, 1)
self.labelNamecoinTestResult = QtGui.QLabel(self.tabNamecoin)
self.labelNamecoinTestResult.setText(_fromUtf8(""))
self.labelNamecoinTestResult.setObjectName(_fromUtf8("labelNamecoinTestResult"))
self.gridLayout_8.addWidget(self.labelNamecoinTestResult, 7, 0, 1, 2)
self.pushButtonNamecoinTest = QtGui.QPushButton(self.tabNamecoin)
self.pushButtonNamecoinTest.setObjectName(_fromUtf8("pushButtonNamecoinTest"))
self.gridLayout_8.addWidget(self.pushButtonNamecoinTest, 7, 2, 1, 1)
self.horizontalLayout = QtGui.QHBoxLayout()
self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout"))
self.label_21 = QtGui.QLabel(self.tabNamecoin)
self.label_21.setObjectName(_fromUtf8("label_21"))
self.horizontalLayout.addWidget(self.label_21)
self.radioButtonNamecoinNamecoind = QtGui.QRadioButton(self.tabNamecoin)
self.radioButtonNamecoinNamecoind.setObjectName(_fromUtf8("radioButtonNamecoinNamecoind"))
self.horizontalLayout.addWidget(self.radioButtonNamecoinNamecoind)
self.radioButtonNamecoinNmcontrol = QtGui.QRadioButton(self.tabNamecoin)
self.radioButtonNamecoinNmcontrol.setObjectName(_fromUtf8("radioButtonNamecoinNmcontrol"))
self.horizontalLayout.addWidget(self.radioButtonNamecoinNmcontrol)
self.gridLayout_8.addLayout(self.horizontalLayout, 1, 0, 1, 3)
self.tabWidgetSettings.addTab(self.tabNamecoin, _fromUtf8(""))
self.tabResendsExpire = QtGui.QWidget()
self.tabResendsExpire.setObjectName(_fromUtf8("tabResendsExpire"))
self.gridLayout_5 = QtGui.QGridLayout(self.tabResendsExpire)
self.gridLayout_5.setObjectName(_fromUtf8("gridLayout_5"))
self.label_7 = QtGui.QLabel(self.tabResendsExpire)
self.label_7.setWordWrap(True)
self.label_7.setObjectName(_fromUtf8("label_7"))
self.gridLayout_5.addWidget(self.label_7, 0, 0, 1, 3)
spacerItem14 = QtGui.QSpacerItem(212, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.gridLayout_5.addItem(spacerItem14, 1, 0, 1, 1)
self.widget = QtGui.QWidget(self.tabResendsExpire)
self.widget.setMinimumSize(QtCore.QSize(231, 75))
self.widget.setObjectName(_fromUtf8("widget"))
self.label_19 = QtGui.QLabel(self.widget)
self.label_19.setGeometry(QtCore.QRect(10, 20, 101, 20))
self.label_19.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter)
self.label_19.setObjectName(_fromUtf8("label_19"))
self.label_20 = QtGui.QLabel(self.widget)
self.label_20.setGeometry(QtCore.QRect(30, 40, 80, 16))
self.label_20.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter)
self.label_20.setObjectName(_fromUtf8("label_20"))
self.lineEditDays = QtGui.QLineEdit(self.widget)
self.lineEditDays.setGeometry(QtCore.QRect(113, 20, 51, 20))
self.lineEditDays.setObjectName(_fromUtf8("lineEditDays"))
self.lineEditMonths = QtGui.QLineEdit(self.widget)
self.lineEditMonths.setGeometry(QtCore.QRect(113, 40, 51, 20))
self.lineEditMonths.setObjectName(_fromUtf8("lineEditMonths"))
self.label_22 = QtGui.QLabel(self.widget)
self.label_22.setGeometry(QtCore.QRect(169, 23, 61, 16))
self.label_22.setObjectName(_fromUtf8("label_22"))
self.label_23 = QtGui.QLabel(self.widget)
self.label_23.setGeometry(QtCore.QRect(170, 41, 71, 16))
self.label_23.setObjectName(_fromUtf8("label_23"))
self.gridLayout_5.addWidget(self.widget, 1, 2, 1, 1)
spacerItem15 = QtGui.QSpacerItem(20, 129, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
self.gridLayout_5.addItem(spacerItem15, 2, 1, 1, 1)
self.tabWidgetSettings.addTab(self.tabResendsExpire, _fromUtf8(""))
self.gridLayout.addWidget(self.tabWidgetSettings, 0, 0, 1, 1)
self.retranslateUi(settingsDialog) if 'darwin' in sys.platform:
self.tabWidgetSettings.setCurrentIndex(0) self.checkBoxStartOnLogon.setDisabled(True)
QtCore.QObject.connect( # pylint: disable=no-member self.checkBoxStartOnLogon.setText(_translate(
self.buttonBox, QtCore.SIGNAL(_fromUtf8("accepted()")), settingsDialog.accept) "MainWindow", "Start-on-login not yet supported on your OS."))
QtCore.QObject.connect( # pylint: disable=no-member self.checkBoxMinimizeToTray.setDisabled(True)
self.buttonBox, QtCore.SIGNAL(_fromUtf8("rejected()")), settingsDialog.reject) self.checkBoxMinimizeToTray.setText(_translate(
QtCore.QObject.connect( # pylint: disable=no-member "MainWindow", "Minimize-to-tray not yet supported on your OS."))
self.checkBoxAuthentication, self.checkBoxShowTrayNotifications.setDisabled(True)
QtCore.SIGNAL( self.checkBoxShowTrayNotifications.setText(_translate(
_fromUtf8("toggled(bool)")), "MainWindow", "Tray notifications not yet supported on your OS."))
self.lineEditSocksUsername.setEnabled) elif 'linux' in sys.platform:
QtCore.QObject.connect( # pylint: disable=no-member self.checkBoxStartOnLogon.setDisabled(True)
self.checkBoxAuthentication, self.checkBoxStartOnLogon.setText(_translate(
QtCore.SIGNAL( "MainWindow", "Start-on-login not yet supported on your OS."))
_fromUtf8("toggled(bool)")), # On the Network settings tab:
self.lineEditSocksPassword.setEnabled) self.lineEditTCPPort.setText(str(
QtCore.QMetaObject.connectSlotsByName(settingsDialog) config.get('bitmessagesettings', 'port')))
settingsDialog.setTabOrder(self.tabWidgetSettings, self.checkBoxStartOnLogon) self.checkBoxUPnP.setChecked(
settingsDialog.setTabOrder(self.checkBoxStartOnLogon, self.checkBoxStartInTray) config.safeGetBoolean('bitmessagesettings', 'upnp'))
settingsDialog.setTabOrder(self.checkBoxStartInTray, self.checkBoxMinimizeToTray) self.checkBoxAuthentication.setChecked(
settingsDialog.setTabOrder(self.checkBoxMinimizeToTray, self.lineEditTCPPort) config.getboolean('bitmessagesettings', 'socksauthentication'))
settingsDialog.setTabOrder(self.lineEditTCPPort, self.comboBoxProxyType) self.checkBoxSocksListen.setChecked(
settingsDialog.setTabOrder(self.comboBoxProxyType, self.lineEditSocksHostname) config.getboolean('bitmessagesettings', 'sockslisten'))
settingsDialog.setTabOrder(self.lineEditSocksHostname, self.lineEditSocksPort)
settingsDialog.setTabOrder(self.lineEditSocksPort, self.checkBoxAuthentication)
settingsDialog.setTabOrder(self.checkBoxAuthentication, self.lineEditSocksUsername)
settingsDialog.setTabOrder(self.lineEditSocksUsername, self.lineEditSocksPassword)
settingsDialog.setTabOrder(self.lineEditSocksPassword, self.checkBoxSocksListen)
settingsDialog.setTabOrder(self.checkBoxSocksListen, self.buttonBox)
def retranslateUi(self, settingsDialog): proxy_type = config.safeGet(
"""Re-translate the UI into the supported languages""" 'bitmessagesettings', 'socksproxytype', 'none')
if proxy_type == 'none':
self.comboBoxProxyType.setCurrentIndex(0)
self.lineEditSocksHostname.setEnabled(False)
self.lineEditSocksPort.setEnabled(False)
self.lineEditSocksUsername.setEnabled(False)
self.lineEditSocksPassword.setEnabled(False)
self.checkBoxAuthentication.setEnabled(False)
self.checkBoxSocksListen.setEnabled(False)
elif proxy_type == 'SOCKS4a':
self.comboBoxProxyType.setCurrentIndex(1)
elif proxy_type == 'SOCKS5':
self.comboBoxProxyType.setCurrentIndex(2)
settingsDialog.setWindowTitle(_translate("settingsDialog", "Settings", None)) self.lineEditSocksHostname.setText(
self.checkBoxStartOnLogon.setText(_translate("settingsDialog", "Start Bitmessage on user login", None)) config.get('bitmessagesettings', 'sockshostname'))
self.groupBoxTray.setTitle(_translate("settingsDialog", "Tray", None)) self.lineEditSocksPort.setText(str(
self.checkBoxStartInTray.setText( config.get('bitmessagesettings', 'socksport')))
_translate( self.lineEditSocksUsername.setText(
"settingsDialog", config.get('bitmessagesettings', 'socksusername'))
"Start Bitmessage in the tray (don\'t show main window)", self.lineEditSocksPassword.setText(
None)) config.get('bitmessagesettings', 'sockspassword'))
self.checkBoxMinimizeToTray.setText(_translate("settingsDialog", "Minimize to tray", None))
self.checkBoxTrayOnClose.setText(_translate("settingsDialog", "Close to tray", None)) self.lineEditMaxDownloadRate.setText(str(
self.checkBoxHideTrayConnectionNotifications.setText( config.get('bitmessagesettings', 'maxdownloadrate')))
_translate("settingsDialog", "Hide connection notifications", None)) self.lineEditMaxUploadRate.setText(str(
self.checkBoxShowTrayNotifications.setText( config.get('bitmessagesettings', 'maxuploadrate')))
_translate( self.lineEditMaxOutboundConnections.setText(str(
"settingsDialog", config.get('bitmessagesettings', 'maxoutboundconnections')))
"Show notification when message received",
None)) # Demanded difficulty tab
self.checkBoxPortableMode.setText(_translate("settingsDialog", "Run in Portable Mode", None)) self.lineEditTotalDifficulty.setText(str((float(
self.PortableModeDescription.setText( config.getint(
_translate( 'bitmessagesettings', 'defaultnoncetrialsperbyte')
"settingsDialog", ) / defaults.networkDefaultProofOfWorkNonceTrialsPerByte)))
"In Portable Mode, messages and config files are stored in the same directory as the" self.lineEditSmallMessageDifficulty.setText(str((float(
" program rather than the normal application-data folder. This makes it convenient to" config.getint(
" run Bitmessage from a USB thumb drive.", 'bitmessagesettings', 'defaultpayloadlengthextrabytes')
None)) ) / defaults.networkDefaultPayloadLengthExtraBytes)))
self.checkBoxWillinglySendToMobile.setText(
_translate( # Max acceptable difficulty tab
"settingsDialog", self.lineEditMaxAcceptableTotalDifficulty.setText(str((float(
"Willingly include unencrypted destination address when sending to a mobile device", config.getint(
None)) 'bitmessagesettings', 'maxacceptablenoncetrialsperbyte')
self.checkBoxUseIdenticons.setText(_translate("settingsDialog", "Use Identicons", None)) ) / defaults.networkDefaultProofOfWorkNonceTrialsPerByte)))
self.checkBoxReplyBelow.setText(_translate("settingsDialog", "Reply below Quote", None)) self.lineEditMaxAcceptableSmallMessageDifficulty.setText(str((float(
self.groupBox.setTitle(_translate("settingsDialog", "Interface Language", None)) config.getint(
self.languageComboBox.setItemText(0, _translate("settingsDialog", "System Settings", "system")) 'bitmessagesettings', 'maxacceptablepayloadlengthextrabytes')
self.tabWidgetSettings.setTabText( ) / defaults.networkDefaultPayloadLengthExtraBytes)))
self.tabWidgetSettings.indexOf(
self.tabUserInterface), # OpenCL
_translate( self.comboBoxOpenCL.setEnabled(openclpow.openclAvailable())
"settingsDialog", "User Interface", None)) self.comboBoxOpenCL.clear()
self.groupBox1.setTitle(_translate("settingsDialog", "Listening port", None)) self.comboBoxOpenCL.addItem("None")
self.label.setText(_translate("settingsDialog", "Listen for connections on port:", None)) self.comboBoxOpenCL.addItems(openclpow.vendors)
self.labelUPnP.setText(_translate("settingsDialog", "UPnP:", None)) self.comboBoxOpenCL.setCurrentIndex(0)
self.groupBox_3.setTitle(_translate("settingsDialog", "Bandwidth limit", None)) for i in range(self.comboBoxOpenCL.count()):
self.label_24.setText(_translate("settingsDialog", "Maximum download rate (kB/s): [0: unlimited]", None)) if self.comboBoxOpenCL.itemText(i) == config.safeGet(
self.label_25.setText(_translate("settingsDialog", "Maximum upload rate (kB/s): [0: unlimited]", None)) 'bitmessagesettings', 'opencl'):
self.label_26.setText(_translate("settingsDialog", "Maximum outbound connections: [0: none]", None)) self.comboBoxOpenCL.setCurrentIndex(i)
self.groupBox_2.setTitle(_translate("settingsDialog", "Proxy server / Tor", None)) break
self.label_2.setText(_translate("settingsDialog", "Type:", None))
self.label_3.setText(_translate("settingsDialog", "Server hostname:", None)) # Namecoin integration tab
self.label_4.setText(_translate("settingsDialog", "Port:", None)) nmctype = config.get('bitmessagesettings', 'namecoinrpctype')
self.checkBoxAuthentication.setText(_translate("settingsDialog", "Authentication", None)) self.lineEditNamecoinHost.setText(
self.label_5.setText(_translate("settingsDialog", "Username:", None)) config.get('bitmessagesettings', 'namecoinrpchost'))
self.label_6.setText(_translate("settingsDialog", "Pass:", None)) self.lineEditNamecoinPort.setText(str(
self.checkBoxSocksListen.setText( config.get('bitmessagesettings', 'namecoinrpcport')))
_translate( self.lineEditNamecoinUser.setText(
"settingsDialog", config.get('bitmessagesettings', 'namecoinrpcuser'))
"Listen for incoming connections when using proxy", self.lineEditNamecoinPassword.setText(
None)) config.get('bitmessagesettings', 'namecoinrpcpassword'))
self.comboBoxProxyType.setItemText(0, _translate("settingsDialog", "none", None))
self.comboBoxProxyType.setItemText(1, _translate("settingsDialog", "SOCKS4a", None)) if nmctype == "namecoind":
self.comboBoxProxyType.setItemText(2, _translate("settingsDialog", "SOCKS5", None)) self.radioButtonNamecoinNamecoind.setChecked(True)
self.tabWidgetSettings.setTabText( elif nmctype == "nmcontrol":
self.tabWidgetSettings.indexOf( self.radioButtonNamecoinNmcontrol.setChecked(True)
self.tabNetworkSettings), self.lineEditNamecoinUser.setEnabled(False)
_translate( self.labelNamecoinUser.setEnabled(False)
"settingsDialog", "Network Settings", None)) self.lineEditNamecoinPassword.setEnabled(False)
self.label_9.setText(_translate("settingsDialog", "Total difficulty:", None)) self.labelNamecoinPassword.setEnabled(False)
self.label_10.setText( else:
_translate( assert False
"settingsDialog",
"The \'Total difficulty\' affects the absolute amount of work the sender must complete." # Message Resend tab
" Doubling this value doubles the amount of work.", self.lineEditDays.setText(str(
None)) config.get('bitmessagesettings', 'stopresendingafterxdays')))
self.label_11.setText(_translate("settingsDialog", "Small message difficulty:", None)) self.lineEditMonths.setText(str(
self.label_8.setText(_translate( config.get('bitmessagesettings', 'stopresendingafterxmonths')))
"settingsDialog",
"When someone sends you a message, their computer must first complete some work. The difficulty of this" def comboBoxProxyTypeChanged(self, comboBoxIndex):
" work, by default, is 1. You may raise this default for new addresses you create by changing the values" """A callback for currentIndexChanged event of comboBoxProxyType"""
" here. Any new addresses you create will require senders to meet the higher difficulty. There is one" if comboBoxIndex == 0:
" exception: if you add a friend or acquaintance to your address book, Bitmessage will automatically" self.lineEditSocksHostname.setEnabled(False)
" notify them when you next send a message that they need only complete the minimum amount of" self.lineEditSocksPort.setEnabled(False)
" work: difficulty 1. ", self.lineEditSocksUsername.setEnabled(False)
None)) self.lineEditSocksPassword.setEnabled(False)
self.label_12.setText( self.checkBoxAuthentication.setEnabled(False)
_translate( self.checkBoxSocksListen.setEnabled(False)
"settingsDialog", elif comboBoxIndex in (1, 2):
"The \'Small message difficulty\' mostly only affects the difficulty of sending small messages." self.lineEditSocksHostname.setEnabled(True)
" Doubling this value makes it almost twice as difficult to send a small message but doesn\'t really" self.lineEditSocksPort.setEnabled(True)
" affect large messages.", self.checkBoxAuthentication.setEnabled(True)
None)) self.checkBoxSocksListen.setEnabled(True)
self.tabWidgetSettings.setTabText( if self.checkBoxAuthentication.isChecked():
self.tabWidgetSettings.indexOf( self.lineEditSocksUsername.setEnabled(True)
self.tabDemandedDifficulty), self.lineEditSocksPassword.setEnabled(True)
_translate(
"settingsDialog", "Demanded difficulty", None)) def getNamecoinType(self):
self.label_15.setText( """
_translate( Check status of namecoin integration radio buttons
"settingsDialog", and translate it to a string as in the options.
"Here you may set the maximum amount of work you are willing to do to send a message to another" """
" person. Setting these values to 0 means that any value is acceptable.", if self.radioButtonNamecoinNamecoind.isChecked():
None)) return "namecoind"
self.label_13.setText(_translate("settingsDialog", "Maximum acceptable total difficulty:", None)) if self.radioButtonNamecoinNmcontrol.isChecked():
self.label_14.setText(_translate("settingsDialog", "Maximum acceptable small message difficulty:", None)) return "nmcontrol"
self.tabWidgetSettings.setTabText( assert False
self.tabWidgetSettings.indexOf(
self.tabMaxAcceptableDifficulty), # Namecoin connection type was changed.
_translate( def namecoinTypeChanged(self, checked): # pylint: disable=unused-argument
"settingsDialog", "Max acceptable difficulty", None)) """A callback for toggled event of radioButtonNamecoinNamecoind"""
self.labelOpenCL.setText(_translate("settingsDialog", "Hardware GPU acceleration (OpenCL):", None)) nmctype = self.getNamecoinType()
self.label_16.setText(_translate( assert nmctype == "namecoind" or nmctype == "nmcontrol"
"settingsDialog",
"<html><head/><body><p>Bitmessage can utilize a different Bitcoin-based program called Namecoin to make" isNamecoind = (nmctype == "namecoind")
" addresses human-friendly. For example, instead of having to tell your friend your long Bitmessage" self.lineEditNamecoinUser.setEnabled(isNamecoind)
" address, you can simply tell him to send a message to <span style=\" font-style:italic;\">test." self.labelNamecoinUser.setEnabled(isNamecoind)
" </span></p><p>(Getting your own Bitmessage address into Namecoin is still rather difficult).</p>" self.lineEditNamecoinPassword.setEnabled(isNamecoind)
"<p>Bitmessage can use either namecoind directly or a running nmcontrol instance.</p></body></html>", self.labelNamecoinPassword.setEnabled(isNamecoind)
None))
self.label_17.setText(_translate("settingsDialog", "Host:", None)) if isNamecoind:
self.label_18.setText(_translate("settingsDialog", "Port:", None)) self.lineEditNamecoinPort.setText(defaults.namecoinDefaultRpcPort)
self.labelNamecoinUser.setText(_translate("settingsDialog", "Username:", None)) else:
self.labelNamecoinPassword.setText(_translate("settingsDialog", "Password:", None)) self.lineEditNamecoinPort.setText("9000")
self.pushButtonNamecoinTest.setText(_translate("settingsDialog", "Test", None))
self.label_21.setText(_translate("settingsDialog", "Connect to:", None)) def click_pushButtonNamecoinTest(self):
self.radioButtonNamecoinNamecoind.setText(_translate("settingsDialog", "Namecoind", None)) """Test the namecoin settings specified in the settings dialog."""
self.radioButtonNamecoinNmcontrol.setText(_translate("settingsDialog", "NMControl", None)) self.labelNamecoinTestResult.setText(
self.tabWidgetSettings.setTabText( _translate("MainWindow", "Testing..."))
self.tabWidgetSettings.indexOf( nc = namecoin.namecoinConnection({
self.tabNamecoin), 'type': self.getNamecoinType(),
_translate( 'host': str(self.lineEditNamecoinHost.text().toUtf8()),
"settingsDialog", "Namecoin integration", None)) 'port': str(self.lineEditNamecoinPort.text().toUtf8()),
self.label_7.setText(_translate( 'user': str(self.lineEditNamecoinUser.text().toUtf8()),
"settingsDialog", 'password': str(self.lineEditNamecoinPassword.text().toUtf8())
"<html><head/><body><p>By default, if you send a message to someone and he is offline for more than two" })
" days, Bitmessage will send the message again after an additional two days. This will be continued with" status, text = nc.test()
" exponential backoff forever; messages will be resent after 5, 10, 20 days ect. until the receiver" self.labelNamecoinTestResult.setText(text)
" acknowledges them. Here you may change that behavior by having Bitmessage give up after a certain" if status == 'success':
" number of days or months.</p><p>Leave these input fields blank for the default behavior." self.parent.namecoin = nc
" </p></body></html>",
None)) def accept(self):
self.label_19.setText(_translate("settingsDialog", "Give up after", None)) """A callback for accepted event of buttonBox (OK button pressed)"""
self.label_20.setText(_translate("settingsDialog", "and", None)) # pylint: disable=too-many-branches,too-many-statements
self.label_22.setText(_translate("settingsDialog", "days", None)) super(SettingsDialog, self).accept()
self.label_23.setText(_translate("settingsDialog", "months.", None)) if self.firstrun:
self.tabWidgetSettings.setTabText( self.config.remove_option('bitmessagesettings', 'dontconnect')
self.tabWidgetSettings.indexOf( self.config.set('bitmessagesettings', 'startonlogon', str(
self.tabResendsExpire), self.checkBoxStartOnLogon.isChecked()))
_translate( self.config.set('bitmessagesettings', 'minimizetotray', str(
"settingsDialog", "Resends Expire", None)) self.checkBoxMinimizeToTray.isChecked()))
self.config.set('bitmessagesettings', 'trayonclose', str(
self.checkBoxTrayOnClose.isChecked()))
self.config.set(
'bitmessagesettings', 'hidetrayconnectionnotifications',
str(self.checkBoxHideTrayConnectionNotifications.isChecked()))
self.config.set('bitmessagesettings', 'showtraynotifications', str(
self.checkBoxShowTrayNotifications.isChecked()))
self.config.set('bitmessagesettings', 'startintray', str(
self.checkBoxStartInTray.isChecked()))
self.config.set('bitmessagesettings', 'willinglysendtomobile', str(
self.checkBoxWillinglySendToMobile.isChecked()))
self.config.set('bitmessagesettings', 'useidenticons', str(
self.checkBoxUseIdenticons.isChecked()))
self.config.set('bitmessagesettings', 'replybelow', str(
self.checkBoxReplyBelow.isChecked()))
lang = str(self.languageComboBox.itemData(
self.languageComboBox.currentIndex()).toString())
self.config.set('bitmessagesettings', 'userlocale', lang)
self.parent.change_translation()
if int(self.config.get('bitmessagesettings', 'port')) != int(
self.lineEditTCPPort.text()):
self.config.set(
'bitmessagesettings', 'port', str(self.lineEditTCPPort.text()))
if not self.config.safeGetBoolean('bitmessagesettings', 'dontconnect'):
self.net_restart_needed = True
if self.checkBoxUPnP.isChecked() != self.config.safeGetBoolean(
'bitmessagesettings', 'upnp'):
self.config.set(
'bitmessagesettings', 'upnp',
str(self.checkBoxUPnP.isChecked()))
if self.checkBoxUPnP.isChecked():
import upnp
upnpThread = upnp.uPnPThread()
upnpThread.start()
proxy_type = self.config.safeGet(
'bitmessagesettings', 'socksproxytype', 'none')
if (
proxy_type == 'none' and
self.comboBoxProxyType.currentText()[0:5] == 'SOCKS' and
shared.statusIconColor != 'red'
):
self.net_restart_needed = True
if (
proxy_type[0:5] == 'SOCKS' and
self.comboBoxProxyType.currentText()[0:5] != 'SOCKS'
):
self.net_restart_needed = True
self.parent.statusbar.clearMessage()
self.config.set(
'bitmessagesettings', 'socksproxytype',
str(self.comboBoxProxyType.currentText())
if self.comboBoxProxyType.currentText()[0:5] == 'SOCKS'
else 'none'
)
self.config.set('bitmessagesettings', 'socksauthentication', str(
self.checkBoxAuthentication.isChecked()))
self.config.set('bitmessagesettings', 'sockshostname', str(
self.lineEditSocksHostname.text()))
self.config.set('bitmessagesettings', 'socksport', str(
self.lineEditSocksPort.text()))
self.config.set('bitmessagesettings', 'socksusername', str(
self.lineEditSocksUsername.text()))
self.config.set('bitmessagesettings', 'sockspassword', str(
self.lineEditSocksPassword.text()))
self.config.set('bitmessagesettings', 'sockslisten', str(
self.checkBoxSocksListen.isChecked()))
try:
# Rounding to integers just for aesthetics
self.config.set('bitmessagesettings', 'maxdownloadrate', str(
int(float(self.lineEditMaxDownloadRate.text()))))
self.config.set('bitmessagesettings', 'maxuploadrate', str(
int(float(self.lineEditMaxUploadRate.text()))))
except ValueError:
QtGui.QMessageBox.about(
self, _translate("MainWindow", "Number needed"),
_translate(
"MainWindow",
"Your maximum download and upload rate must be numbers."
" Ignoring what you typed.")
)
else:
set_rates(
self.config.safeGetInt('bitmessagesettings', 'maxdownloadrate'),
self.config.safeGetInt('bitmessagesettings', 'maxuploadrate'))
self.config.set('bitmessagesettings', 'maxoutboundconnections', str(
int(float(self.lineEditMaxOutboundConnections.text()))))
self.config.set(
'bitmessagesettings', 'namecoinrpctype', self.getNamecoinType())
self.config.set('bitmessagesettings', 'namecoinrpchost', str(
self.lineEditNamecoinHost.text()))
self.config.set('bitmessagesettings', 'namecoinrpcport', str(
self.lineEditNamecoinPort.text()))
self.config.set('bitmessagesettings', 'namecoinrpcuser', str(
self.lineEditNamecoinUser.text()))
self.config.set('bitmessagesettings', 'namecoinrpcpassword', str(
self.lineEditNamecoinPassword.text()))
self.parent.resetNamecoinConnection()
# Demanded difficulty tab
if float(self.lineEditTotalDifficulty.text()) >= 1:
self.config.set(
'bitmessagesettings', 'defaultnoncetrialsperbyte',
str(int(
float(self.lineEditTotalDifficulty.text()) *
defaults.networkDefaultProofOfWorkNonceTrialsPerByte)))
if float(self.lineEditSmallMessageDifficulty.text()) >= 1:
self.config.set(
'bitmessagesettings', 'defaultpayloadlengthextrabytes',
str(int(
float(self.lineEditSmallMessageDifficulty.text()) *
defaults.networkDefaultPayloadLengthExtraBytes)))
if self.comboBoxOpenCL.currentText().toUtf8() != self.config.safeGet(
'bitmessagesettings', 'opencl'):
self.config.set(
'bitmessagesettings', 'opencl',
str(self.comboBoxOpenCL.currentText()))
queues.workerQueue.put(('resetPoW', ''))
acceptableDifficultyChanged = False
if (
float(self.lineEditMaxAcceptableTotalDifficulty.text()) >= 1 or
float(self.lineEditMaxAcceptableTotalDifficulty.text()) == 0
):
if self.config.get(
'bitmessagesettings', 'maxacceptablenoncetrialsperbyte'
) != str(int(
float(self.lineEditMaxAcceptableTotalDifficulty.text()) *
defaults.networkDefaultProofOfWorkNonceTrialsPerByte)
):
# the user changed the max acceptable total difficulty
acceptableDifficultyChanged = True
self.config.set(
'bitmessagesettings', 'maxacceptablenoncetrialsperbyte',
str(int(
float(self.lineEditMaxAcceptableTotalDifficulty.text()) *
defaults.networkDefaultProofOfWorkNonceTrialsPerByte))
)
if (
float(self.lineEditMaxAcceptableSmallMessageDifficulty.text()) >= 1 or
float(self.lineEditMaxAcceptableSmallMessageDifficulty.text()) == 0
):
if self.config.get(
'bitmessagesettings', 'maxacceptablepayloadlengthextrabytes'
) != str(int(
float(self.lineEditMaxAcceptableSmallMessageDifficulty.text()) *
defaults.networkDefaultPayloadLengthExtraBytes)
):
# the user changed the max acceptable small message difficulty
acceptableDifficultyChanged = True
self.config.set(
'bitmessagesettings', 'maxacceptablepayloadlengthextrabytes',
str(int(
float(self.lineEditMaxAcceptableSmallMessageDifficulty.text()) *
defaults.networkDefaultPayloadLengthExtraBytes))
)
if acceptableDifficultyChanged:
# It might now be possible to send msgs which were previously
# marked as toodifficult. Let us change them to 'msgqueued'.
# The singleWorker will try to send them and will again mark
# them as toodifficult if the receiver's required difficulty
# is still higher than we are willing to do.
sqlExecute(
"UPDATE sent SET status='msgqueued'"
" WHERE status='toodifficult'")
queues.workerQueue.put(('sendmessage', ''))
# UI setting to stop trying to send messages after X days/months
# I'm open to changing this UI to something else if someone has a better idea.
if self.lineEditDays.text() == '' and self.lineEditMonths.text() == '':
# We need to handle this special case. Bitmessage has its
# default behavior. The input is blank/blank
self.config.set('bitmessagesettings', 'stopresendingafterxdays', '')
self.config.set('bitmessagesettings', 'stopresendingafterxmonths', '')
shared.maximumLengthOfTimeToBotherResendingMessages = float('inf')
try:
days = float(self.lineEditDays.text())
except ValueError:
self.lineEditDays.setText("0")
days = 0.0
try:
months = float(self.lineEditMonths.text())
except ValueError:
self.lineEditMonths.setText("0")
months = 0.0
if days >= 0 and months >= 0:
shared.maximumLengthOfTimeToBotherResendingMessages = \
days * 24 * 60 * 60 + months * 60 * 60 * 24 * 365 / 12
if shared.maximumLengthOfTimeToBotherResendingMessages < 432000:
# If the time period is less than 5 hours, we give
# zero values to all fields. No message will be sent again.
QtGui.QMessageBox.about(
self,
_translate("MainWindow", "Will not resend ever"),
_translate(
"MainWindow",
"Note that the time limit you entered is less"
" than the amount of time Bitmessage waits for"
" the first resend attempt therefore your"
" messages will never be resent.")
)
self.config.set(
'bitmessagesettings', 'stopresendingafterxdays', '0')
self.config.set(
'bitmessagesettings', 'stopresendingafterxmonths', '0')
shared.maximumLengthOfTimeToBotherResendingMessages = 0.0
else:
self.config.set(
'bitmessagesettings', 'stopresendingafterxdays', str(days))
self.config.set(
'bitmessagesettings', 'stopresendingafterxmonths',
str(months))
self.config.save()
if self.net_restart_needed:
self.net_restart_needed = False
self.config.setTemp('bitmessagesettings', 'dontconnect', 'true')
self.timer.singleShot(
5000, lambda:
self.config.setTemp(
'bitmessagesettings', 'dontconnect', 'false')
)
self.parent.updateStartOnLogon()
if (
state.appdata != paths.lookupExeFolder() and
self.checkBoxPortableMode.isChecked()
):
# If we are NOT using portable mode now but the user selected
# that we should...
# Write the keys.dat file to disk in the new location
sqlStoredProcedure('movemessagstoprog')
with open(paths.lookupExeFolder() + 'keys.dat', 'wb') as configfile:
self.config.write(configfile)
# Write the knownnodes.dat file to disk in the new location
knownnodes.saveKnownNodes(paths.lookupExeFolder())
os.remove(state.appdata + 'keys.dat')
os.remove(state.appdata + 'knownnodes.dat')
previousAppdataLocation = state.appdata
state.appdata = paths.lookupExeFolder()
debug.resetLogging()
try:
os.remove(previousAppdataLocation + 'debug.log')
os.remove(previousAppdataLocation + 'debug.log.1')
except:
pass
if (
state.appdata == paths.lookupExeFolder() and
not self.checkBoxPortableMode.isChecked()
):
# If we ARE using portable mode now but the user selected
# that we shouldn't...
state.appdata = paths.lookupAppdataFolder()
if not os.path.exists(state.appdata):
os.makedirs(state.appdata)
sqlStoredProcedure('movemessagstoappdata')
# Write the keys.dat file to disk in the new location
self.config.save()
# Write the knownnodes.dat file to disk in the new location
knownnodes.saveKnownNodes(state.appdata)
os.remove(paths.lookupExeFolder() + 'keys.dat')
os.remove(paths.lookupExeFolder() + 'knownnodes.dat')
debug.resetLogging()
try:
os.remove(paths.lookupExeFolder() + 'debug.log')
os.remove(paths.lookupExeFolder() + 'debug.log.1')
except:
pass

View File

@ -37,6 +37,18 @@
<string>User Interface</string> <string>User Interface</string>
</attribute> </attribute>
<layout class="QFormLayout" name="formLayout"> <layout class="QFormLayout" name="formLayout">
<property name="leftMargin">
<number>8</number>
</property>
<property name="topMargin">
<number>8</number>
</property>
<property name="rightMargin">
<number>8</number>
</property>
<property name="bottomMargin">
<number>8</number>
</property>
<item row="0" column="0"> <item row="0" column="0">
<widget class="QCheckBox" name="checkBoxStartOnLogon"> <widget class="QCheckBox" name="checkBoxStartOnLogon">
<property name="text"> <property name="text">
@ -44,20 +56,43 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="0" colspan="2"> <item row="1" column="0">
<widget class="QCheckBox" name="checkBoxStartInTray"> <widget class="QGroupBox" name="groupBoxTray">
<property name="text"> <property name="title">
<string>Start Bitmessage in the tray (don't show main window)</string> <string>Tray</string>
</property> </property>
<layout class="QVBoxLayout" name="formLayoutTray">
<item>
<widget class="QCheckBox" name="checkBoxStartInTray">
<property name="text">
<string>Start Bitmessage in the tray (don't show main window)</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBoxMinimizeToTray">
<property name="text">
<string>Minimize to tray</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBoxTrayOnClose">
<property name="text">
<string>Close to tray</string>
</property>
</widget>
</item>
</layout>
</widget> </widget>
</item> </item>
<item row="2" column="0"> <item row="2" column="0">
<widget class="QCheckBox" name="checkBoxMinimizeToTray"> <widget class="QCheckBox" name="checkBoxHideTrayConnectionNotifications">
<property name="text"> <property name="text">
<string>Minimize to tray</string> <string>Hide connection notifications</string>
</property>
<property name="checked">
<bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
@ -117,90 +152,15 @@
<property name="title"> <property name="title">
<string>Interface Language</string> <string>Interface Language</string>
</property> </property>
<layout class="QFormLayout" name="formLayout_2"> <layout class="QVBoxLayout">
<item row="0" column="0"> <item>
<widget class="QComboBox" name="languageComboBox"> <widget class="LanguageBox" name="languageComboBox">
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
<width>100</width> <width>100</width>
<height>0</height> <height>0</height>
</size> </size>
</property> </property>
<item>
<property name="text">
<string comment="system">System Settings</string>
</property>
</item>
<item>
<property name="text">
<string notr="true" comment="en">English</string>
</property>
</item>
<item>
<property name="text">
<string notr="true" comment="eo">Esperanto</string>
</property>
</item>
<item>
<property name="text">
<string notr="true" comment="fr">Français</string>
</property>
</item>
<item>
<property name="text">
<string notr="true" comment="de">Deutsch</string>
</property>
</item>
<item>
<property name="text">
<string notr="true" comment="es">Español</string>
</property>
</item>
<item>
<property name="text">
<string notr="true" comment="ru">русский</string>
</property>
</item>
<item>
<property name="text">
<string notr="true" comment="no">Norsk</string>
</property>
</item>
<item>
<property name="text">
<string notr="true" comment="ar">العربية</string>
</property>
</item>
<item>
<property name="text">
<string notr="true" comment="zh_cn">简体中文</string>
</property>
</item>
<item>
<property name="text">
<string notr="true" comment="ja">日本語</string>
</property>
</item>
<item>
<property name="text">
<string notr="true" comment="nl">Nederlands</string>
</property>
</item>
<item>
<property name="text">
<string notr="true" comment="cs">Česky</string>
</property>
</item>
<item>
<property name="text">
<string comment="en_pirate">Pirate English</string>
</property>
</item>
<item>
<property name="text">
<string comment="other">Other (set in keys.dat)</string>
</property>
</item>
</widget> </widget>
</item> </item>
</layout> </layout>
@ -213,6 +173,18 @@
<string>Network Settings</string> <string>Network Settings</string>
</attribute> </attribute>
<layout class="QGridLayout" name="gridLayout_4"> <layout class="QGridLayout" name="gridLayout_4">
<property name="leftMargin">
<number>8</number>
</property>
<property name="topMargin">
<number>8</number>
</property>
<property name="rightMargin">
<number>8</number>
</property>
<property name="bottomMargin">
<number>8</number>
</property>
<item row="0" column="0"> <item row="0" column="0">
<widget class="QGroupBox" name="groupBox"> <widget class="QGroupBox" name="groupBox">
<property name="title"> <property name="title">
@ -220,26 +192,13 @@
</property> </property>
<layout class="QGridLayout" name="gridLayout_3"> <layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0"> <item row="0" column="0">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>125</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label"> <widget class="QLabel" name="label">
<property name="text"> <property name="text">
<string>Listen for connections on port:</string> <string>Listen for connections on port:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="2"> <item row="0" column="1">
<widget class="QLineEdit" name="lineEditTCPPort"> <widget class="QLineEdit" name="lineEditTCPPort">
<property name="maximumSize"> <property name="maximumSize">
<size> <size>
@ -249,6 +208,26 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="4">
<widget class="QCheckBox" name="checkBoxUPnP">
<property name="text">
<string>UPnP</string>
</property>
</widget>
</item>
<item row="0" column="2">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>
@ -466,6 +445,18 @@
<string>Demanded difficulty</string> <string>Demanded difficulty</string>
</attribute> </attribute>
<layout class="QGridLayout" name="gridLayout_6"> <layout class="QGridLayout" name="gridLayout_6">
<property name="leftMargin">
<number>8</number>
</property>
<property name="topMargin">
<number>8</number>
</property>
<property name="rightMargin">
<number>8</number>
</property>
<property name="bottomMargin">
<number>8</number>
</property>
<item row="1" column="1"> <item row="1" column="1">
<widget class="QLabel" name="label_9"> <widget class="QLabel" name="label_9">
<property name="text"> <property name="text">
@ -594,6 +585,18 @@
<string>Max acceptable difficulty</string> <string>Max acceptable difficulty</string>
</attribute> </attribute>
<layout class="QGridLayout" name="gridLayout_7"> <layout class="QGridLayout" name="gridLayout_7">
<property name="leftMargin">
<number>8</number>
</property>
<property name="topMargin">
<number>8</number>
</property>
<property name="rightMargin">
<number>8</number>
</property>
<property name="bottomMargin">
<number>8</number>
</property>
<item row="0" column="0" colspan="3"> <item row="0" column="0" colspan="3">
<widget class="QLabel" name="label_15"> <widget class="QLabel" name="label_15">
<property name="text"> <property name="text">
@ -698,6 +701,33 @@
</property> </property>
</spacer> </spacer>
</item> </item>
<item row="4" column="0" colspan="3">
<layout class="QHBoxLayout">
<item>
<widget class="QLabel" name="labelOpenCL">
<property name="text">
<string>Hardware GPU acceleration (OpenCL):</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboBoxOpenCL"/>
</item>
<item>
<spacer name="horizontalSpacer_12">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="tabNamecoin"> <widget class="QWidget" name="tabNamecoin">
@ -705,6 +735,18 @@
<string>Namecoin integration</string> <string>Namecoin integration</string>
</attribute> </attribute>
<layout class="QGridLayout" name="gridLayout_8"> <layout class="QGridLayout" name="gridLayout_8">
<property name="leftMargin">
<number>8</number>
</property>
<property name="topMargin">
<number>8</number>
</property>
<property name="rightMargin">
<number>8</number>
</property>
<property name="bottomMargin">
<number>8</number>
</property>
<item row="2" column="0"> <item row="2" column="0">
<spacer name="horizontalSpacer_6"> <spacer name="horizontalSpacer_6">
<property name="orientation"> <property name="orientation">
@ -888,6 +930,18 @@
<string>Resends Expire</string> <string>Resends Expire</string>
</attribute> </attribute>
<layout class="QGridLayout" name="gridLayout_5"> <layout class="QGridLayout" name="gridLayout_5">
<property name="leftMargin">
<number>8</number>
</property>
<property name="topMargin">
<number>8</number>
</property>
<property name="rightMargin">
<number>8</number>
</property>
<property name="bottomMargin">
<number>8</number>
</property>
<item row="0" column="0" colspan="3"> <item row="0" column="0" colspan="3">
<widget class="QLabel" name="label_7"> <widget class="QLabel" name="label_7">
<property name="text"> <property name="text">
@ -912,91 +966,69 @@
</spacer> </spacer>
</item> </item>
<item row="1" column="2"> <item row="1" column="2">
<widget class="QWidget" name="widget" native="true"> <widget class="QGroupBox">
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
<width>231</width> <width>231</width>
<height>75</height> <height>75</height>
</size> </size>
</property> </property>
<layout class="QGridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_19"> <widget class="QLabel" name="label_19">
<property name="geometry">
<rect>
<x>10</x>
<y>20</y>
<width>101</width>
<height>20</height>
</rect>
</property>
<property name="text"> <property name="text">
<string>Give up after</string> <string>Give up after</string>
</property> </property>
<property name="alignment"> <property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property> </property>
</widget> </widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_20"> <widget class="QLabel" name="label_20">
<property name="geometry">
<rect>
<x>30</x>
<y>40</y>
<width>80</width>
<height>16</height>
</rect>
</property>
<property name="text"> <property name="text">
<string>and</string> <string>and</string>
</property> </property>
<property name="alignment"> <property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property> </property>
</widget> </widget>
<widget class="QLineEdit" name="lineEditDays"> </item>
<property name="geometry"> <item row="0" column="1">
<rect> <widget class="QLineEdit" name="lineEditDays">
<x>113</x> <property name="maximumSize">
<y>20</y> <size>
<width>51</width> <width>55</width>
<height>20</height> <height>100</height>
</rect> </size>
</property> </property>
</widget> </widget>
<widget class="QLineEdit" name="lineEditMonths"> </item>
<property name="geometry"> <item row="1" column="1">
<rect> <widget class="QLineEdit" name="lineEditMonths">
<x>113</x> <property name="maximumSize">
<y>40</y> <size>
<width>51</width> <width>55</width>
<height>20</height> <height>100</height>
</rect> </size>
</property> </property>
</widget> </widget>
<widget class="QLabel" name="label_22"> </item>
<property name="geometry"> <item row="0" column="2">
<rect> <widget class="QLabel">
<x>169</x>
<y>23</y>
<width>61</width>
<height>16</height>
</rect>
</property>
<property name="text"> <property name="text">
<string>days</string> <string>days</string>
</property> </property>
</widget> </widget>
<widget class="QLabel" name="label_23"> </item>
<property name="geometry"> <item row="1" column="2">
<rect> <widget class="QLabel">
<x>170</x>
<y>41</y>
<width>71</width>
<height>16</height>
</rect>
</property>
<property name="text"> <property name="text">
<string>months.</string> <string>months.</string>
</property> </property>
</widget> </widget>
</item>
</layout>
</widget> </widget>
</item> </item>
<item row="2" column="1"> <item row="2" column="1">
@ -1017,7 +1049,14 @@
</widget> </widget>
</item> </item>
</layout> </layout>
</widget> </widget>
<customwidgets>
<customwidget>
<class>LanguageBox</class>
<extends>QComboBox</extends>
<header>bitmessageqt.languagebox</header>
</customwidget>
</customwidgets>
<tabstops> <tabstops>
<tabstop>tabWidgetSettings</tabstop> <tabstop>tabWidgetSettings</tabstop>
<tabstop>checkBoxStartOnLogon</tabstop> <tabstop>checkBoxStartOnLogon</tabstop>
@ -1101,5 +1140,53 @@
</hint> </hint>
</hints> </hints>
</connection> </connection>
<connection>
<sender>comboBoxProxyType</sender>
<signal>currentIndexChanged(int)</signal>
<receiver>settingsDialog</receiver>
<slot>comboBoxProxyTypeChanged</slot>
<hints>
<hint type="sourcelabel">
<x>20</x>
<y>20</y>
</hint>
<hint type="destinationlabel">
<x>20</x>
<y>20</y>
</hint>
</hints>
</connection>
<connection>
<sender>radioButtonNamecoinNamecoind</sender>
<signal>toggled(bool)</signal>
<receiver>settingsDialog</receiver>
<slot>namecoinTypeChanged</slot>
<hints>
<hint type="sourcelabel">
<x>20</x>
<y>20</y>
</hint>
<hint type="destinationlabel">
<x>20</x>
<y>20</y>
</hint>
</hints>
</connection>
<connection>
<sender>pushButtonNamecoinTest</sender>
<signal>clicked()</signal>
<receiver>settingsDialog</receiver>
<slot>click_pushButtonNamecoinTest</slot>
<hints>
<hint type="sourcelabel">
<x>20</x>
<y>20</y>
</hint>
<hint type="destinationlabel">
<x>20</x>
<y>20</y>
</hint>
</hints>
</connection>
</connections> </connections>
</ui> </ui>

View File

@ -46,6 +46,8 @@ class BMConfigParser(ConfigParser.SafeConfigParser):
"""Singleton class inherited from ConfigParser.SafeConfigParser """Singleton class inherited from ConfigParser.SafeConfigParser
with additional methods specific to bitmessage config.""" with additional methods specific to bitmessage config."""
_temp = {}
def set(self, section, option, value=None): def set(self, section, option, value=None):
if self._optcre is self.OPTCRE or value: if self._optcre is self.OPTCRE or value:
if not isinstance(value, basestring): if not isinstance(value, basestring):
@ -59,6 +61,10 @@ class BMConfigParser(ConfigParser.SafeConfigParser):
if section == "bitmessagesettings" and option == "timeformat": if section == "bitmessagesettings" and option == "timeformat":
return ConfigParser.ConfigParser.get( return ConfigParser.ConfigParser.get(
self, section, option, raw, variables) self, section, option, raw, variables)
try:
return self._temp[section][option]
except KeyError:
pass
return ConfigParser.ConfigParser.get( return ConfigParser.ConfigParser.get(
self, section, option, True, variables) self, section, option, True, variables)
except ConfigParser.InterpolationError: except ConfigParser.InterpolationError:
@ -70,6 +76,13 @@ class BMConfigParser(ConfigParser.SafeConfigParser):
except (KeyError, ValueError, AttributeError): except (KeyError, ValueError, AttributeError):
raise e raise e
def setTemp(self, section, option, value=None):
"""Temporary set option to value, not saving."""
try:
self._temp[section][option] = value
except KeyError:
self._temp[section] = {option: value}
def safeGetBoolean(self, section, field): def safeGetBoolean(self, section, field):
try: try:
return self.getboolean(section, field) return self.getboolean(section, field)

View File

@ -1,84 +0,0 @@
import socket
import knownnodes
import socks
import state
from bmconfigparser import BMConfigParser
from debug import logger
def dns():
"""
DNS bootstrap. This could be programmed to use the SOCKS proxy to do the
DNS lookup some day but for now we will just rely on the entries in
defaultKnownNodes.py. Hopefully either they are up to date or the user
has run Bitmessage recently without SOCKS turned on and received good
bootstrap nodes using that method.
"""
def try_add_known_node(stream, addr, port, method=''):
try:
socket.inet_aton(addr)
except (TypeError, socket.error):
return
logger.info(
'Adding %s to knownNodes based on %s DNS bootstrap method',
addr, method)
knownnodes.addKnownNode(stream, state.Peer(addr, port))
proxy_type = BMConfigParser().get('bitmessagesettings', 'socksproxytype')
if proxy_type == 'none':
for port in [8080, 8444]:
try:
for item in socket.getaddrinfo(
'bootstrap%s.bitmessage.org' % port, 80):
try_add_known_node(1, item[4][0], port)
except:
logger.error(
'bootstrap%s.bitmessage.org DNS bootstrapping failed.',
port, exc_info=True
)
elif proxy_type == 'SOCKS5':
knownnodes.createDefaultKnownNodes(onion=True)
logger.debug('Adding default onion knownNodes.')
for port in [8080, 8444]:
logger.debug("Resolving %i through SOCKS...", port)
address_family = socket.AF_INET
sock = socks.socksocket(address_family, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.settimeout(20)
proxytype = socks.PROXY_TYPE_SOCKS5
sockshostname = BMConfigParser().get(
'bitmessagesettings', 'sockshostname')
socksport = BMConfigParser().getint(
'bitmessagesettings', 'socksport')
# Do domain name lookups through the proxy;
# though this setting doesn't really matter since we won't
# be doing any domain name lookups anyway.
rdns = True
if BMConfigParser().getboolean(
'bitmessagesettings', 'socksauthentication'):
socksusername = BMConfigParser().get(
'bitmessagesettings', 'socksusername')
sockspassword = BMConfigParser().get(
'bitmessagesettings', 'sockspassword')
sock.setproxy(
proxytype, sockshostname, socksport, rdns,
socksusername, sockspassword)
else:
sock.setproxy(
proxytype, sockshostname, socksport, rdns)
try:
ip = sock.resolve("bootstrap" + str(port) + ".bitmessage.org")
sock.shutdown(socket.SHUT_RDWR)
sock.close()
except:
logger.error("SOCKS DNS resolving failed", exc_info=True)
else:
try_add_known_node(1, ip, port, 'SOCKS')
else:
logger.info(
'DNS bootstrap skipped because the proxy type does not support'
' DNS resolution.'
)

View File

@ -11,7 +11,6 @@ import time
import state import state
from bmconfigparser import BMConfigParser from bmconfigparser import BMConfigParser
from debug import logger from debug import logger
from helper_bootstrap import dns
knownNodesLock = threading.Lock() knownNodesLock = threading.Lock()
knownNodes = {stream: {} for stream in range(1, 4)} knownNodes = {stream: {} for stream in range(1, 4)}
@ -35,10 +34,6 @@ DEFAULT_NODES = (
state.Peer('178.11.46.221', 8444) state.Peer('178.11.46.221', 8444)
) )
DEFAULT_NODES_ONION = (
state.Peer('quzwelsuziwqgpt2.onion', 8444),
)
def json_serialize_knownnodes(output): def json_serialize_knownnodes(output):
""" """
@ -67,8 +62,7 @@ def json_deserialize_knownnodes(source):
if ( if (
not (knownNodesActual or info.get('self')) and not (knownNodesActual or info.get('self')) and
peer not in DEFAULT_NODES and peer not in DEFAULT_NODES
peer not in DEFAULT_NODES_ONION
): ):
knownNodesActual = True knownNodesActual = True
@ -103,9 +97,9 @@ def addKnownNode(stream, peer, lastseen=None, is_self=False):
} }
def createDefaultKnownNodes(onion=False): def createDefaultKnownNodes():
past = time.time() - 2418600 # 28 days - 10 min past = time.time() - 2418600 # 28 days - 10 min
for peer in DEFAULT_NODES_ONION if onion else DEFAULT_NODES: for peer in DEFAULT_NODES:
addKnownNode(1, peer, past) addKnownNode(1, peer, past)
saveKnownNodes() saveKnownNodes()
@ -177,39 +171,37 @@ def trimKnownNodes(recAddrStream=1):
del knownNodes[recAddrStream][oldest] del knownNodes[recAddrStream][oldest]
def dns():
"""Add DNS names to knownnodes"""
for port in [8080, 8444]:
addKnownNode(
1, state.Peer('bootstrap%s.bitmessage.org' % port, port))
def cleanupKnownNodes(): def cleanupKnownNodes():
""" """
Cleanup knownnodes: remove old nodes and nodes with low rating Cleanup knownnodes: remove old nodes and nodes with low rating
""" """
now = int(time.time()) now = int(time.time())
needToWriteKnownNodesToDisk = False needToWriteKnownNodesToDisk = False
dns_done = False
spawnConnections = not BMConfigParser().safeGetBoolean(
'bitmessagesettings', 'dontconnect'
) and BMConfigParser().safeGetBoolean(
'bitmessagesettings', 'sendoutgoingconnections')
with knownNodesLock: with knownNodesLock:
for stream in knownNodes: for stream in knownNodes:
if stream not in state.streamsInWhichIAmParticipating: if stream not in state.streamsInWhichIAmParticipating:
continue continue
keys = knownNodes[stream].keys() keys = knownNodes[stream].keys()
if len(keys) <= 1: # leave at least one node
if not dns_done and spawnConnections:
dns()
dns_done = True
continue
for node in keys: for node in keys:
if len(knownNodes[stream]) <= 1: # leave at least one node
break
try: try:
# scrap old nodes age = now - knownNodes[stream][node]["lastseen"]
if (now - knownNodes[stream][node]["lastseen"] > # scrap old nodes (age > 28 days)
2419200): # 28 days if age > 2419200:
needToWriteKnownNodesToDisk = True needToWriteKnownNodesToDisk = True
del knownNodes[stream][node] del knownNodes[stream][node]
continue continue
# scrap old nodes with low rating # scrap old nodes (age > 3 hours) with low rating
if (now - knownNodes[stream][node]["lastseen"] > 10800 and if (age > 10800 and knownNodes[stream][node]["rating"] <=
knownNodes[stream][node]["rating"] <=
knownNodesForgetRating): knownNodesForgetRating):
needToWriteKnownNodesToDisk = True needToWriteKnownNodesToDisk = True
del knownNodes[stream][node] del knownNodes[stream][node]

View File

@ -1,3 +1,7 @@
"""
src/messagetypes/__init__.py
============================
"""
from importlib import import_module from importlib import import_module
from os import path, listdir from os import path, listdir
from string import lower from string import lower
@ -6,12 +10,15 @@ from debug import logger
import messagetypes import messagetypes
import paths import paths
class MsgBase(object):
def encode(self): class MsgBase(object): # pylint: disable=too-few-public-methods
"""Base class for message types"""
def __init__(self):
self.data = {"": lower(type(self).__name__)} self.data = {"": lower(type(self).__name__)}
def constructObject(data): def constructObject(data):
"""Constructing an object"""
whitelist = ["message"] whitelist = ["message"]
if data[""] not in whitelist: if data[""] not in whitelist:
return None return None
@ -32,6 +39,7 @@ def constructObject(data):
else: else:
return returnObj return returnObj
if paths.frozen is not None: if paths.frozen is not None:
import messagetypes.message import messagetypes.message
import messagetypes.vote import messagetypes.vote

View File

@ -1,24 +1,30 @@
"""
src/messagetypes/message.py
===========================
"""
from debug import logger from debug import logger
from messagetypes import MsgBase from messagetypes import MsgBase
class Message(MsgBase): class Message(MsgBase):
def __init__(self): """Encapsulate a message"""
return # pylint: disable=attribute-defined-outside-init
def decode(self, data): def decode(self, data):
"""Decode a message"""
# UTF-8 and variable type validator # UTF-8 and variable type validator
if type(data["subject"]) is str: if isinstance(data["subject"], str):
self.subject = unicode(data["subject"], 'utf-8', 'replace') self.subject = unicode(data["subject"], 'utf-8', 'replace')
else: else:
self.subject = unicode(str(data["subject"]), 'utf-8', 'replace') self.subject = unicode(str(data["subject"]), 'utf-8', 'replace')
if type(data["body"]) is str: if isinstance(data["body"], str):
self.body = unicode(data["body"], 'utf-8', 'replace') self.body = unicode(data["body"], 'utf-8', 'replace')
else: else:
self.body = unicode(str(data["body"]), 'utf-8', 'replace') self.body = unicode(str(data["body"]), 'utf-8', 'replace')
def encode(self, data): def encode(self, data):
super(Message, self).encode() """Encode a message"""
super(Message, self).__init__()
try: try:
self.data["subject"] = data["subject"] self.data["subject"] = data["subject"]
self.data["body"] = data["body"] self.data["body"] = data["body"]
@ -27,5 +33,6 @@ class Message(MsgBase):
return self.data return self.data
def process(self): def process(self):
"""Process a message"""
logger.debug("Subject: %i bytes", len(self.subject)) logger.debug("Subject: %i bytes", len(self.subject))
logger.debug("Body: %i bytes", len(self.body)) logger.debug("Body: %i bytes", len(self.body))

View File

@ -1,23 +1,31 @@
"""
src/messagetypes/vote.py
========================
"""
from debug import logger from debug import logger
from messagetypes import MsgBase from messagetypes import MsgBase
class Vote(MsgBase): class Vote(MsgBase):
def __init__(self): """Module used to vote"""
return
def decode(self, data): def decode(self, data):
"""decode a vote"""
# pylint: disable=attribute-defined-outside-init
self.msgid = data["msgid"] self.msgid = data["msgid"]
self.vote = data["vote"] self.vote = data["vote"]
def encode(self, data): def encode(self, data):
super(Vote, self).encode() """Encode a vote"""
super(Vote, self).__init__()
try: try:
self.data["msgid"] = data["msgid"] self.data["msgid"] = data["msgid"]
self.data["vote"] = data["vote"] self.data["vote"] = data["vote"]
except KeyError as e: except KeyError as e:
logger.error("Missing key %s", e.name) logger.error("Missing key %s", e)
return self.data return self.data
def process(self): def process(self):
"""Encode a vote"""
logger.debug("msgid: %s", self.msgid) logger.debug("msgid: %s", self.msgid)
logger.debug("vote: %s", self.vote) logger.debug("vote: %s", self.vote)

View File

@ -1,3 +1,4 @@
# pylint: disable=too-many-branches
import random # nosec import random # nosec
import knownnodes import knownnodes
@ -38,7 +39,10 @@ def chooseConnection(stream):
for _ in range(50): for _ in range(50):
peer = random.choice(knownnodes.knownNodes[stream].keys()) peer = random.choice(knownnodes.knownNodes[stream].keys())
try: try:
rating = knownnodes.knownNodes[stream][peer]['rating'] peer_info = knownnodes.knownNodes[stream][peer]
if peer_info.get('self'):
continue
rating = peer_info["rating"]
except TypeError: except TypeError:
logger.warning('Error in %s', peer) logger.warning('Error in %s', peer)
rating = 0 rating = 0
@ -46,7 +50,8 @@ def chooseConnection(stream):
# onion addresses have a higher priority when SOCKS # onion addresses have a higher priority when SOCKS
if peer.host.endswith('.onion') and rating > 0: if peer.host.endswith('.onion') and rating > 0:
rating = 1 rating = 1
else: # TODO: need better check
elif not peer.host.startswith('bootstrap'):
encodedAddr = protocol.encodeHost(peer.host) encodedAddr = protocol.encodeHost(peer.host)
# don't connect to local IPs when using SOCKS # don't connect to local IPs when using SOCKS
if not protocol.checkIPAddress(encodedAddr, False): if not protocol.checkIPAddress(encodedAddr, False):

View File

@ -8,7 +8,6 @@ import socket
import time import time
import asyncore_pollchoose as asyncore import asyncore_pollchoose as asyncore
import helper_bootstrap
import helper_random import helper_random
import knownnodes import knownnodes
import protocol import protocol
@ -19,7 +18,8 @@ from debug import logger
from proxy import Proxy from proxy import Proxy
from singleton import Singleton from singleton import Singleton
from tcp import ( from tcp import (
TCPServer, Socks5BMConnection, Socks4aBMConnection, TCPConnection) bootstrap, Socks4aBMConnection, Socks5BMConnection,
TCPConnection, TCPServer)
from udp import UDPSocket from udp import UDPSocket
@ -160,7 +160,35 @@ class BMConnectionPool(object):
udpSocket = UDPSocket(host=bind, announcing=True) udpSocket = UDPSocket(host=bind, announcing=True)
self.udpSockets[udpSocket.listening.host] = udpSocket self.udpSockets[udpSocket.listening.host] = udpSocket
def loop(self): # pylint: disable=too-many-branches, too-many-statements def startBootstrappers(self):
"""Run the process of resolving bootstrap hostnames"""
proxy_type = BMConfigParser().safeGet(
'bitmessagesettings', 'socksproxytype')
# A plugins may be added here
hostname = None
if not proxy_type or proxy_type == 'none':
connection_base = TCPConnection
elif proxy_type == 'SOCKS5':
connection_base = Socks5BMConnection
hostname = helper_random.randomchoice([
'quzwelsuziwqgpt2.onion', None
])
elif proxy_type == 'SOCKS4a':
connection_base = Socks4aBMConnection # FIXME: I cannot test
else:
# This should never happen because socksproxytype setting
# is handled in bitmessagemain before starting the connectionpool
return
bootstrapper = bootstrap(connection_base)
if not hostname:
port = helper_random.randomchoice([8080, 8444])
hostname = 'bootstrap%s.bitmessage.org' % port
else:
port = 8444
self.addConnection(bootstrapper(hostname, port))
def loop(self): # pylint: disable=too-many-branches,too-many-statements
"""Main Connectionpool's loop""" """Main Connectionpool's loop"""
# defaults to empty loop if outbound connections are maxed # defaults to empty loop if outbound connections are maxed
spawnConnections = False spawnConnections = False
@ -185,7 +213,8 @@ class BMConnectionPool(object):
# pylint: disable=too-many-nested-blocks # pylint: disable=too-many-nested-blocks
if spawnConnections: if spawnConnections:
if not knownnodes.knownNodesActual: if not knownnodes.knownNodesActual:
helper_bootstrap.dns() self.startBootstrappers()
knownnodes.knownNodesActual = True
if not self.bootstrapped: if not self.bootstrapped:
self.bootstrapped = True self.bootstrapped = True
Proxy.proxy = ( Proxy.proxy = (

View File

@ -8,6 +8,7 @@ src/network/socks5.py
import socket import socket
import struct import struct
import state
from proxy import GeneralProxyError, Proxy, ProxyError from proxy import GeneralProxyError, Proxy, ProxyError
@ -160,9 +161,6 @@ class Socks5(Proxy):
class Socks5Connection(Socks5): class Socks5Connection(Socks5):
"""Child socks5 class used for making outbound connections.""" """Child socks5 class used for making outbound connections."""
def __init__(self, address):
Socks5.__init__(self, address=address)
def state_auth_done(self): def state_auth_done(self):
"""Request connection to be made""" """Request connection to be made"""
# Now we can request the actual connection # Now we can request the actual connection
@ -172,9 +170,9 @@ class Socks5Connection(Socks5):
try: try:
self.ipaddr = socket.inet_aton(self.destination[0]) self.ipaddr = socket.inet_aton(self.destination[0])
self.append_write_buf(chr(0x01).encode() + self.ipaddr) self.append_write_buf(chr(0x01).encode() + self.ipaddr)
except socket.error: except socket.error: # may be IPv6!
# Well it's not an IP number, so it's probably a DNS name. # Well it's not an IP number, so it's probably a DNS name.
if Proxy._remote_dns: # pylint: disable=protected-access if self._remote_dns:
# Resolve remotely # Resolve remotely
self.ipaddr = None self.ipaddr = None
self.append_write_buf(chr(0x03).encode() + chr( self.append_write_buf(chr(0x03).encode() + chr(
@ -202,7 +200,7 @@ class Socks5Resolver(Socks5):
def __init__(self, host): def __init__(self, host):
self.host = host self.host = host
self.port = 8444 self.port = 8444
Socks5.__init__(self, address=(self.host, self.port)) Socks5.__init__(self, address=state.Peer(self.host, self.port))
def state_auth_done(self): def state_auth_done(self):
"""Perform resolving""" """Perform resolving"""

View File

@ -73,11 +73,14 @@ class TCPConnection(BMProto, TLSDispatcher):
logger.debug( logger.debug(
'Connecting to %s:%i', 'Connecting to %s:%i',
self.destination.host, self.destination.port) self.destination.host, self.destination.port)
encodedAddr = protocol.encodeHost(self.destination.host) try:
self.local = all([ self.local = (
protocol.checkIPAddress(encodedAddr, True), protocol.checkIPAddress(
not protocol.checkSocksIP(self.destination.host) protocol.encodeHost(self.destination.host), True) and
]) not protocol.checkSocksIP(self.destination.host)
)
except socket.error:
pass # it's probably a hostname
ObjectTracker.__init__(self) # pylint: disable=non-parent-init-called ObjectTracker.__init__(self) # pylint: disable=non-parent-init-called
self.bm_proto_reset() self.bm_proto_reset()
self.set_state("bm_header", expectBytes=protocol.Header.size) self.set_state("bm_header", expectBytes=protocol.Header.size)
@ -322,6 +325,39 @@ class Socks4aBMConnection(Socks4aConnection, TCPConnection):
return True return True
def bootstrap(connection_class):
"""Make bootstrapper class for connection type (connection_class)"""
class Bootstrapper(connection_class):
"""Base class for bootstrappers"""
_connection_base = connection_class
def __init__(self, host, port):
self._connection_base.__init__(self, state.Peer(host, port))
self.close_reason = self._succeed = False
def bm_command_addr(self):
"""
Got addr message - the bootstrap succeed.
Let BMProto process the addr message and switch state to 'close'
"""
BMProto.bm_command_addr(self)
self._succeed = True
# pylint: disable=attribute-defined-outside-init
self.close_reason = "Thanks for bootstrapping!"
self.set_state("close")
def handle_close(self):
"""
After closing the connection switch knownnodes.knownNodesActual
back to False if the bootstrapper failed.
"""
self._connection_base.handle_close(self)
if not self._succeed:
knownnodes.knownNodesActual = False
return Bootstrapper
class TCPServer(AdvancedDispatcher): class TCPServer(AdvancedDispatcher):
"""TCP connection server for Bitmessage protocol""" """TCP connection server for Bitmessage protocol"""

View File

@ -1,4 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""
src/plugins/indicator_libmessaging.py
=====================================
"""
import gi import gi
gi.require_version('MessagingMenu', '1.0') # noqa:E402 gi.require_version('MessagingMenu', '1.0') # noqa:E402
@ -9,6 +13,7 @@ from pybitmessage.tr import _translate
class IndicatorLibmessaging(object): class IndicatorLibmessaging(object):
"""Plugin for libmessage indicator"""
def __init__(self, form): def __init__(self, form):
try: try:
self.app = MessagingMenu.App(desktop_id='pybitmessage.desktop') self.app = MessagingMenu.App(desktop_id='pybitmessage.desktop')
@ -32,15 +37,18 @@ class IndicatorLibmessaging(object):
if self.app: if self.app:
self.app.unregister() self.app.unregister()
def activate(self, app, source): def activate(self, app, source): # pylint: disable=unused-argument
"""Activate the libmessaging indicator plugin"""
self.form.appIndicatorInbox( self.form.appIndicatorInbox(
self.new_message_item if source == 'messages' self.new_message_item if source == 'messages'
else self.new_broadcast_item else self.new_broadcast_item
) )
# show the number of unread messages and subscriptions
# on the messaging menu
def show_unread(self, draw_attention=False): def show_unread(self, draw_attention=False):
"""
show the number of unread messages and subscriptions
on the messaging menu
"""
for source, count in zip( for source, count in zip(
('messages', 'subscriptions'), ('messages', 'subscriptions'),
self.form.getUnread() self.form.getUnread()

View File

@ -1,5 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
src/plugins/menu_qrcode.py
==========================
A menu plugin showing QR-Code for bitmessage address in modal dialog. A menu plugin showing QR-Code for bitmessage address in modal dialog.
""" """
@ -12,9 +15,10 @@ from pybitmessage.tr import _translate
# http://stackoverflow.com/questions/20452486 # http://stackoverflow.com/questions/20452486
class Image(qrcode.image.base.BaseImage): class Image(qrcode.image.base.BaseImage): # pylint: disable=abstract-method
"""Image output class for qrcode using QPainter""" """Image output class for qrcode using QPainter"""
def __init__(self, border, width, box_size):
def __init__(self, border, width, box_size): # pylint: disable=super-init-not-called
self.border = border self.border = border
self.width = width self.width = width
self.box_size = box_size self.box_size = box_size

View File

@ -1,4 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""
src/plugins/notification_notify2.py
===================================
"""
import gi import gi
gi.require_version('Notify', '0.7') gi.require_version('Notify', '0.7')
@ -6,10 +10,13 @@ from gi.repository import Notify
Notify.init('pybitmessage') Notify.init('pybitmessage')
def connect_plugin(title, subtitle, category, label, icon): def connect_plugin(title, subtitle, category, label, icon):
"""Plugin for notify2"""
if not icon: if not icon:
icon = 'mail-message-new' if category == 2 else 'pybitmessage' icon = 'mail-message-new' if category == 2 else 'pybitmessage'
connect_plugin.notification.update(title, subtitle, icon) connect_plugin.notification.update(title, subtitle, icon)
connect_plugin.notification.show() connect_plugin.notification.show()
connect_plugin.notification = Notify.Notification.new("Init", "Init") connect_plugin.notification = Notify.Notification.new("Init", "Init")

View File

@ -1,5 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""
src/plugins/plugin.py
===================================
"""
import pkg_resources import pkg_resources

View File

@ -1,5 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""
src/plugins/proxyconfig_stem.py
===================================
"""
import os import os
import logging import logging
import random # noseq import random # noseq
@ -8,6 +11,7 @@ import tempfile
import stem import stem
import stem.control import stem.control
import stem.process import stem.process
import stem.version
class DebugLogger(object): class DebugLogger(object):
@ -28,14 +32,14 @@ class DebugLogger(object):
# Plugin's debug or unexpected log line from tor # Plugin's debug or unexpected log line from tor
self._logger.debug(line) self._logger.debug(line)
else: else:
self._logger.log(self._levels.get(level, 10), '(tor)' + line) self._logger.log(self._levels.get(level, 10), '(tor) %s', line)
def connect_plugin(config): def connect_plugin(config): # pylint: disable=too-many-branches
"""Run stem proxy configurator""" """Run stem proxy configurator"""
logwrite = DebugLogger() logwrite = DebugLogger()
if config.safeGet('bitmessagesettings', 'sockshostname') not in ( if config.safeGet('bitmessagesettings', 'sockshostname') not in (
'localhost', '127.0.0.1', '' 'localhost', '127.0.0.1', ''
): ):
# remote proxy is choosen for outbound connections, # remote proxy is choosen for outbound connections,
# nothing to do here, but need to set socksproxytype to SOCKS5! # nothing to do here, but need to set socksproxytype to SOCKS5!
@ -60,8 +64,14 @@ def connect_plugin(config):
# So if there is a system wide tor, use it for outbound connections. # So if there is a system wide tor, use it for outbound connections.
try: try:
stem.process.launch_tor_with_config( stem.process.launch_tor_with_config(
tor_config, take_ownership=True, init_msg_handler=logwrite) tor_config, take_ownership=True, timeout=20,
init_msg_handler=logwrite)
except OSError: except OSError:
if not attempt:
try:
stem.version.get_system_tor_version()
except IOError:
return
continue continue
else: else:
logwrite('Started tor on port %s' % port) logwrite('Started tor on port %s' % port)
@ -108,3 +118,5 @@ def connect_plugin(config):
onionhostname, 'keytype', response.private_key_type) onionhostname, 'keytype', response.private_key_type)
config.save() config.save()
config.set('bitmessagesettings', 'socksproxytype', 'SOCKS5') config.set('bitmessagesettings', 'socksproxytype', 'SOCKS5')
return True

View File

@ -1,4 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""
src/plugins/proxyconfig_stem.py
===================================
"""
from pybitmessage.bitmessageqt import sound from pybitmessage.bitmessageqt import sound
@ -14,7 +18,8 @@ _theme = {
} }
def connect_plugin(category, label=None): def connect_plugin(category, label=None): # pylint: disable=unused-argument
"""This function implements the entry point."""
try: try:
_canberra.play(0, pycanberra.CA_PROP_EVENT_ID, _theme[category], None) _canberra.play(0, pycanberra.CA_PROP_EVENT_ID, _theme[category], None)
except (KeyError, pycanberra.CanberraException): except (KeyError, pycanberra.CanberraException):

View File

@ -1,5 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""
src/plugins/sound_gstreamer.py
===================================
"""
import gi import gi
gi.require_version('Gst', '1.0') gi.require_version('Gst', '1.0')
from gi.repository import Gst # noqa: E402 from gi.repository import Gst # noqa: E402
@ -9,6 +12,7 @@ _player = Gst.ElementFactory.make("playbin", "player")
def connect_plugin(sound_file): def connect_plugin(sound_file):
"""Entry point for sound file"""
_player.set_state(Gst.State.NULL) _player.set_state(Gst.State.NULL)
_player.set_property("uri", "file://" + sound_file) _player.set_property("uri", "file://" + sound_file)
_player.set_state(Gst.State.PLAYING) _player.set_state(Gst.State.PLAYING)

View File

@ -1,10 +1,14 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""
src/plugins/sound_playfile.py
===================================
"""
try: try:
import winsound import winsound
def connect_plugin(sound_file): def connect_plugin(sound_file):
"""Plugin's entry point"""
winsound.PlaySound(sound_file, winsound.SND_FILENAME) winsound.PlaySound(sound_file, winsound.SND_FILENAME)
except ImportError: except ImportError:
import os import os
@ -18,7 +22,8 @@ except ImportError:
args, stdout=FNULL, stderr=subprocess.STDOUT, close_fds=True) args, stdout=FNULL, stderr=subprocess.STDOUT, close_fds=True)
def connect_plugin(sound_file): def connect_plugin(sound_file):
global play_cmd """This function implements the entry point."""
global play_cmd # pylint: disable=global-statement
ext = os.path.splitext(sound_file)[-1] ext = os.path.splitext(sound_file)[-1]
try: try:

View File

@ -264,7 +264,10 @@ def assembleVersionMessage(remoteHost, remotePort, participatingStreams, server=
else: else:
# use first 16 bytes if host data is longer # use first 16 bytes if host data is longer
# for example in case of onion v3 service # for example in case of onion v3 service
payload += encodeHost(remoteHost)[:16] try:
payload += encodeHost(remoteHost)[:16]
except socket.error:
payload += encodeHost('127.0.0.1')
payload += pack('>H', remotePort) # remote IPv6 and port payload += pack('>H', remotePort) # remote IPv6 and port
# bitflags of the services I offer. # bitflags of the services I offer.

View File

@ -1,3 +1,7 @@
"""
src/pyelliptic/__init__.py
=====================================
"""
# Copyright (C) 2010 # Copyright (C) 2010
# Author: Yann GUIBET # Author: Yann GUIBET
# Contact: <yannguibet@gmail.com> # Contact: <yannguibet@gmail.com>

View File

@ -1,5 +1,9 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""
src/pyelliptic/cipher.py
========================
"""
# Copyright (C) 2011 Yann GUIBET <yannguibet@gmail.com> # Copyright (C) 2011 Yann GUIBET <yannguibet@gmail.com>
# See LICENSE for details. # See LICENSE for details.
@ -7,7 +11,8 @@
from openssl import OpenSSL from openssl import OpenSSL
class Cipher: # pylint: disable=redefined-builtin
class Cipher(object):
""" """
Symmetric encryption Symmetric encryption
@ -44,30 +49,34 @@ class Cipher:
@staticmethod @staticmethod
def get_blocksize(ciphername): def get_blocksize(ciphername):
"""This Method returns cipher blocksize"""
cipher = OpenSSL.get_cipher(ciphername) cipher = OpenSSL.get_cipher(ciphername)
return cipher.get_blocksize() return cipher.get_blocksize()
@staticmethod @staticmethod
def gen_IV(ciphername): def gen_IV(ciphername):
"""Generate random initialization vector"""
cipher = OpenSSL.get_cipher(ciphername) cipher = OpenSSL.get_cipher(ciphername)
return OpenSSL.rand(cipher.get_blocksize()) return OpenSSL.rand(cipher.get_blocksize())
def update(self, input): def update(self, input):
"""Update result with more data"""
i = OpenSSL.c_int(0) i = OpenSSL.c_int(0)
buffer = OpenSSL.malloc(b"", len(input) + self.cipher.get_blocksize()) buffer = OpenSSL.malloc(b"", len(input) + self.cipher.get_blocksize())
inp = OpenSSL.malloc(input, len(input)) inp = OpenSSL.malloc(input, len(input))
if OpenSSL.EVP_CipherUpdate(self.ctx, OpenSSL.byref(buffer), if OpenSSL.EVP_CipherUpdate(self.ctx, OpenSSL.byref(buffer),
OpenSSL.byref(i), inp, len(input)) == 0: OpenSSL.byref(i), inp, len(input)) == 0:
raise Exception("[OpenSSL] EVP_CipherUpdate FAIL ...") raise Exception("[OpenSSL] EVP_CipherUpdate FAIL ...")
return buffer.raw[0:i.value] return buffer.raw[0:i.value] # pylint: disable=invalid-slice-index
def final(self): def final(self):
"""Returning the final value"""
i = OpenSSL.c_int(0) i = OpenSSL.c_int(0)
buffer = OpenSSL.malloc(b"", self.cipher.get_blocksize()) buffer = OpenSSL.malloc(b"", self.cipher.get_blocksize())
if (OpenSSL.EVP_CipherFinal_ex(self.ctx, OpenSSL.byref(buffer), if (OpenSSL.EVP_CipherFinal_ex(self.ctx, OpenSSL.byref(buffer),
OpenSSL.byref(i))) == 0: OpenSSL.byref(i))) == 0:
raise Exception("[OpenSSL] EVP_CipherFinal_ex FAIL ...") raise Exception("[OpenSSL] EVP_CipherFinal_ex FAIL ...")
return buffer.raw[0:i.value] return buffer.raw[0:i.value] # pylint: disable=invalid-slice-index
def ciphering(self, input): def ciphering(self, input):
""" """
@ -77,6 +86,7 @@ class Cipher:
return buff + self.final() return buff + self.final()
def __del__(self): def __del__(self):
# pylint: disable=protected-access
if OpenSSL._hexversion > 0x10100000 and not OpenSSL._libreSSL: if OpenSSL._hexversion > 0x10100000 and not OpenSSL._libreSSL:
OpenSSL.EVP_CIPHER_CTX_reset(self.ctx) OpenSSL.EVP_CIPHER_CTX_reset(self.ctx)
else: else:

View File

@ -1,6 +1,9 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""
src/pyelliptic/hash.py
=====================
"""
# Copyright (C) 2011 Yann GUIBET <yannguibet@gmail.com> # Copyright (C) 2011 Yann GUIBET <yannguibet@gmail.com>
# See LICENSE for details. # See LICENSE for details.
@ -27,10 +30,10 @@ def _equals_str(a, b):
def equals(a, b): def equals(a, b):
"""Compare two strings or bytearrays"""
if isinstance(a, str): if isinstance(a, str):
return _equals_str(a, b) return _equals_str(a, b)
else: return _equals_bytes(a, b)
return _equals_bytes(a, b)
def hmac_sha256(k, m): def hmac_sha256(k, m):
@ -58,6 +61,7 @@ def hmac_sha512(k, m):
def pbkdf2(password, salt=None, i=10000, keylen=64): def pbkdf2(password, salt=None, i=10000, keylen=64):
"""Key derivation function using SHA256"""
if salt is None: if salt is None:
salt = OpenSSL.rand(8) salt = OpenSSL.rand(8)
p_password = OpenSSL.malloc(password, len(password)) p_password = OpenSSL.malloc(password, len(password))

View File

@ -1,10 +1,14 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""
src/pyelliptic/openssl.py
=====================
"""
# Copyright (C) 2011 Yann GUIBET <yannguibet@gmail.com> # Copyright (C) 2011 Yann GUIBET <yannguibet@gmail.com>
# See LICENSE for details. # See LICENSE for details.
# #
# Software slightly changed by Jonathan Warren <bitmessage at-symbol jonwarren.org> # Software slightly changed by Jonathan Warren <bitmessage at-symbol jonwarren.org>
# pylint: disable=protected-access
import sys import sys
import ctypes import ctypes
@ -13,6 +17,9 @@ OpenSSL = None
from kivy.utils import platform from kivy.utils import platform
class CipherName: class CipherName:
"""Class returns cipher name, pointer and blocksize"""
# pylint: disable=old-style-class
def __init__(self, name, pointer, blocksize): def __init__(self, name, pointer, blocksize):
self._name = name self._name = name
self._pointer = pointer self._pointer = pointer
@ -24,16 +31,20 @@ class CipherName:
" | Function pointer : " + str(self._pointer) " | Function pointer : " + str(self._pointer)
def get_pointer(self): def get_pointer(self):
"""This method returns cipher pointer"""
return self._pointer() return self._pointer()
def get_name(self): def get_name(self):
"""This method returns cipher name"""
return self._name return self._name
def get_blocksize(self): def get_blocksize(self):
"""This method returns cipher blocksize"""
return self._blocksize return self._blocksize
def get_version(library): def get_version(library):
"""This function return version, hexversion and cflages"""
version = None version = None
hexversion = None hexversion = None
cflags = None cflags = None
@ -68,6 +79,7 @@ class _OpenSSL:
""" """
Wrapper for OpenSSL using ctypes Wrapper for OpenSSL using ctypes
""" """
# pylint: disable=too-many-statements, too-many-instance-attributes, old-style-class
def __init__(self, library): def __init__(self, library):
""" """
Build the wrapper Build the wrapper
@ -594,6 +606,7 @@ class _OpenSSL:
""" """
returns the name of a elliptic curve with his id returns the name of a elliptic curve with his id
""" """
# pylint: disable=redefined-builtin
res = None res = None
for i in self.curves: for i in self.curves:
if self.curves[i] == id: if self.curves[i] == id:
@ -607,6 +620,7 @@ class _OpenSSL:
""" """
OpenSSL random function OpenSSL random function
""" """
# pylint: disable=redefined-builtin
buffer = self.malloc(0, size) buffer = self.malloc(0, size)
# This pyelliptic library, by default, didn't check the return value of RAND_bytes. It is # This pyelliptic library, by default, didn't check the return value of RAND_bytes. It is
# evidently possible that it returned an error and not-actually-random data. However, in # evidently possible that it returned an error and not-actually-random data. However, in
@ -623,6 +637,7 @@ class _OpenSSL:
""" """
returns a create_string_buffer (ctypes) returns a create_string_buffer (ctypes)
""" """
# pylint: disable=redefined-builtin
buffer = None buffer = None
if data != 0: if data != 0:
if sys.version_info.major == 3 and isinstance(data, type('')): if sys.version_info.major == 3 and isinstance(data, type('')):
@ -634,6 +649,8 @@ class _OpenSSL:
def loadOpenSSL(): def loadOpenSSL():
"""This function finds and load the OpenSSL library"""
# pylint: disable=global-statement
global OpenSSL global OpenSSL
from os import path, environ from os import path, environ
from ctypes.util import find_library from ctypes.util import find_library

View File

@ -1,25 +0,0 @@
SocksiPy version 1.00
A Python SOCKS module.
(C) 2006 Dan-Haim. All rights reserved.
See LICENSE file for details.
KNOWN BUGS AND ISSUES
----------------------
There are no currently known bugs in this module.
There are some limits though:
1) Only outgoing connections are supported - This module currently only supports
outgoing TCP connections, though some servers may support incoming connections
as well. UDP is not supported either.
2) GSSAPI Socks5 authenticaion is not supported.
If you find any new bugs, please contact the author at:
negativeiq@users.sourceforge.net
Thank you!

View File

@ -1,22 +0,0 @@
Copyright 2006 Dan-Haim. All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of Dan Haim nor the names of his contributors may be used
to endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY DAN HAIM "AS IS" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL DAN HAIM OR HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMANGE.

View File

@ -1,201 +0,0 @@
SocksiPy version 1.00
A Python SOCKS module.
(C) 2006 Dan-Haim. All rights reserved.
See LICENSE file for details.
WHAT IS A SOCKS PROXY?
A SOCKS proxy is a proxy server at the TCP level. In other words, it acts as
a tunnel, relaying all traffic going through it without modifying it.
SOCKS proxies can be used to relay traffic using any network protocol that
uses TCP.
WHAT IS SOCKSIPY?
This Python module allows you to create TCP connections through a SOCKS
proxy without any special effort.
PROXY COMPATIBILITY
SocksiPy is compatible with three different types of proxies:
1. SOCKS Version 4 (Socks4), including the Socks4a extension.
2. SOCKS Version 5 (Socks5).
3. HTTP Proxies which support tunneling using the CONNECT method.
SYSTEM REQUIREMENTS
Being written in Python, SocksiPy can run on any platform that has a Python
interpreter and TCP/IP support.
This module has been tested with Python 2.3 and should work with greater versions
just as well.
INSTALLATION
-------------
Simply copy the file "socks.py" to your Python's lib/site-packages directory,
and you're ready to go.
USAGE
------
First load the socks module with the command:
>>> import socks
>>>
The socks module provides a class called "socksocket", which is the base to
all of the module's functionality.
The socksocket object has the same initialization parameters as the normal socket
object to ensure maximal compatibility, however it should be noted that socksocket
will only function with family being AF_INET and type being SOCK_STREAM.
Generally, it is best to initialize the socksocket object with no parameters
>>> s = socks.socksocket()
>>>
The socksocket object has an interface which is very similiar to socket's (in fact
the socksocket class is derived from socket) with a few extra methods.
To select the proxy server you would like to use, use the setproxy method, whose
syntax is:
setproxy(proxytype, addr[, port[, rdns[, username[, password]]]])
Explaination of the parameters:
proxytype - The type of the proxy server. This can be one of three possible
choices: PROXY_TYPE_SOCKS4, PROXY_TYPE_SOCKS5 and PROXY_TYPE_HTTP for Socks4,
Socks5 and HTTP servers respectively.
addr - The IP address or DNS name of the proxy server.
port - The port of the proxy server. Defaults to 1080 for socks and 8080 for http.
rdns - This is a boolean flag than modifies the behavior regarding DNS resolving.
If it is set to True, DNS resolving will be preformed remotely, on the server.
If it is set to False, DNS resolving will be preformed locally. Please note that
setting this to True with Socks4 servers actually use an extension to the protocol,
called Socks4a, which may not be supported on all servers (Socks5 and http servers
always support DNS). The default is True.
username - For Socks5 servers, this allows simple username / password authentication
with the server. For Socks4 servers, this parameter will be sent as the userid.
This parameter is ignored if an HTTP server is being used. If it is not provided,
authentication will not be used (servers may accept unauthentication requests).
password - This parameter is valid only for Socks5 servers and specifies the
respective password for the username provided.
Example of usage:
>>> s.setproxy(socks.PROXY_TYPE_SOCKS5,"socks.example.com")
>>>
After the setproxy method has been called, simply call the connect method with the
traditional parameters to establish a connection through the proxy:
>>> s.connect(("www.sourceforge.net",80))
>>>
Connection will take a bit longer to allow negotiation with the proxy server.
Please note that calling connect without calling setproxy earlier will connect
without a proxy (just like a regular socket).
Errors: Any errors in the connection process will trigger exceptions. The exception
may either be generated by the underlying socket layer or may be custom module
exceptions, whose details follow:
class ProxyError - This is a base exception class. It is not raised directly but
rather all other exception classes raised by this module are derived from it.
This allows an easy way to catch all proxy-related errors.
class GeneralProxyError - When thrown, it indicates a problem which does not fall
into another category. The parameter is a tuple containing an error code and a
description of the error, from the following list:
1 - invalid data - This error means that unexpected data has been received from
the server. The most common reason is that the server specified as the proxy is
not really a Socks4/Socks5/HTTP proxy, or maybe the proxy type specified is wrong.
4 - bad proxy type - This will be raised if the type of the proxy supplied to the
setproxy function was not PROXY_TYPE_SOCKS4/PROXY_TYPE_SOCKS5/PROXY_TYPE_HTTP.
5 - bad input - This will be raised if the connect method is called with bad input
parameters.
class Socks5AuthError - This indicates that the connection through a Socks5 server
failed due to an authentication problem. The parameter is a tuple containing a
code and a description message according to the following list:
1 - authentication is required - This will happen if you use a Socks5 server which
requires authentication without providing a username / password at all.
2 - all offered authentication methods were rejected - This will happen if the proxy
requires a special authentication method which is not supported by this module.
3 - unknown username or invalid password - Self descriptive.
class Socks5Error - This will be raised for Socks5 errors which are not related to
authentication. The parameter is a tuple containing a code and a description of the
error, as given by the server. The possible errors, according to the RFC are:
1 - General SOCKS server failure - If for any reason the proxy server is unable to
fulfill your request (internal server error).
2 - connection not allowed by ruleset - If the address you're trying to connect to
is blacklisted on the server or requires authentication.
3 - Network unreachable - The target could not be contacted. A router on the network
had replied with a destination net unreachable error.
4 - Host unreachable - The target could not be contacted. A router on the network
had replied with a destination host unreachable error.
5 - Connection refused - The target server has actively refused the connection
(the requested port is closed).
6 - TTL expired - The TTL value of the SYN packet from the proxy to the target server
has expired. This usually means that there are network problems causing the packet
to be caught in a router-to-router "ping-pong".
7 - Command not supported - The client has issued an invalid command. When using this
module, this error should not occur.
8 - Address type not supported - The client has provided an invalid address type.
When using this module, this error should not occur.
class Socks4Error - This will be raised for Socks4 errors. The parameter is a tuple
containing a code and a description of the error, as given by the server. The
possible error, according to the specification are:
1 - Request rejected or failed - Will be raised in the event of an failure for any
reason other then the two mentioned next.
2 - request rejected because SOCKS server cannot connect to identd on the client -
The Socks server had tried an ident lookup on your computer and has failed. In this
case you should run an identd server and/or configure your firewall to allow incoming
connections to local port 113 from the remote server.
3 - request rejected because the client program and identd report different user-ids -
The Socks server had performed an ident lookup on your computer and has received a
different userid than the one you have provided. Change your userid (through the
username parameter of the setproxy method) to match and try again.
class HTTPError - This will be raised for HTTP errors. The parameter is a tuple
containing the HTTP status code and the description of the server.
After establishing the connection, the object behaves like a standard socket.
Call the close method to close the connection.
In addition to the socksocket class, an additional function worth mentioning is the
setdefaultproxy function. The parameters are the same as the setproxy method.
This function will set default proxy settings for newly created socksocket objects,
in which the proxy settings haven't been changed via the setproxy method.
This is quite useful if you wish to force 3rd party modules to use a socks proxy,
by overriding the socket object.
For example:
>>> socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5,"socks.example.com")
>>> socket.socket = socks.socksocket
>>> urllib.urlopen("http://www.sourceforge.net/")
PROBLEMS
---------
If you have any problems using this module, please first refer to the BUGS file
(containing current bugs and issues). If your problem is not mentioned you may
contact the author at the following E-Mail address:
negativeiq@users.sourceforge.net
Please allow some time for your question to be received and handled.
Dan-Haim,
Author.

View File

@ -1,476 +0,0 @@
"""SocksiPy - Python SOCKS module.
Version 1.00
Copyright 2006 Dan-Haim. All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of Dan Haim nor the names of his contributors may be used
to endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY DAN HAIM "AS IS" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL DAN HAIM OR HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMANGE.
This module provides a standard socket-like interface for Python
for tunneling connections through SOCKS proxies.
"""
"""
Minor modifications made by Christopher Gilbert (http://motomastyle.com/)
for use in PyLoris (http://pyloris.sourceforge.net/)
Minor modifications made by Mario Vilas (http://breakingcode.wordpress.com/)
mainly to merge bug fixes found in Sourceforge
"""
import socket
import struct
import sys
PROXY_TYPE_SOCKS4 = 1
PROXY_TYPE_SOCKS5 = 2
PROXY_TYPE_HTTP = 3
_defaultproxy = None
_orgsocket = socket.socket
class ProxyError(Exception): pass
class GeneralProxyError(ProxyError): pass
class Socks5AuthError(ProxyError): pass
class Socks5Error(ProxyError): pass
class Socks4Error(ProxyError): pass
class HTTPError(ProxyError): pass
_generalerrors = ("success",
"invalid data",
"not connected",
"not available",
"bad proxy type",
"bad input",
"timed out",
"network unreachable",
"connection refused",
"host unreachable")
_socks5errors = ("succeeded",
"general SOCKS server failure",
"connection not allowed by ruleset",
"Network unreachable",
"Host unreachable",
"Connection refused",
"TTL expired",
"Command not supported",
"Address type not supported",
"Unknown error")
_socks5autherrors = ("succeeded",
"authentication is required",
"all offered authentication methods were rejected",
"unknown username or invalid password",
"unknown error")
_socks4errors = ("request granted",
"request rejected or failed",
"request rejected because SOCKS server cannot connect to identd on the client",
"request rejected because the client program and identd report different user-ids",
"unknown error")
def setdefaultproxy(proxytype=None, addr=None, port=None, rdns=True, username=None, password=None):
"""setdefaultproxy(proxytype, addr[, port[, rdns[, username[, password]]]])
Sets a default proxy which all further socksocket objects will use,
unless explicitly changed.
"""
global _defaultproxy
_defaultproxy = (proxytype, addr, port, rdns, username, password)
def wrapmodule(module):
"""wrapmodule(module)
Attempts to replace a module's socket library with a SOCKS socket. Must set
a default proxy using setdefaultproxy(...) first.
This will only work on modules that import socket directly into the namespace;
most of the Python Standard Library falls into this category.
"""
if _defaultproxy != None:
module.socket.socket = socksocket
else:
raise GeneralProxyError((4, "no proxy specified"))
class socksocket(socket.socket):
"""socksocket([family[, type[, proto]]]) -> socket object
Open a SOCKS enabled socket. The parameters are the same as
those of the standard socket init. In order for SOCKS to work,
you must specify family=AF_INET, type=SOCK_STREAM and proto=0.
"""
def __init__(self, family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0, _sock=None):
_orgsocket.__init__(self, family, type, proto, _sock)
if _defaultproxy != None:
self.__proxy = _defaultproxy
else:
self.__proxy = (None, None, None, None, None, None)
self.__proxysockname = None
self.__proxypeername = None
def __recvall(self, count):
"""__recvall(count) -> data
Receive EXACTLY the number of bytes requested from the socket.
Blocks until the required number of bytes have been received.
"""
try:
data = self.recv(count)
except socket.timeout:
raise GeneralProxyError((6, "timed out"))
while len(data) < count:
d = self.recv(count-len(data))
if not d: raise GeneralProxyError((0, "connection closed unexpectedly"))
data = data + d
return data
def setproxy(self, proxytype=None, addr=None, port=None, rdns=True, username=None, password=None):
"""setproxy(proxytype, addr[, port[, rdns[, username[, password]]]])
Sets the proxy to be used.
proxytype
The type of the proxy to be used. Three types
are supported: PROXY_TYPE_SOCKS4 (including socks4a),
PROXY_TYPE_SOCKS5 and PROXY_TYPE_HTTP
addr
The address of the server (IP or DNS).
port
The port of the server. Defaults to 1080 for SOCKS
servers and 8080 for HTTP proxy servers.
rdns
Should DNS queries be preformed on the remote side
(rather than the local side). The default is True.
Note: This has no effect with SOCKS4 servers.
username
Username to authenticate with to the server.
The default is no authentication.
password
Password to authenticate with to the server.
Only relevant when username is also provided.
"""
self.__proxy = (proxytype, addr, port, rdns, username, password)
def __negotiatesocks5(self):
"""__negotiatesocks5(self,destaddr,destport)
Negotiates a connection through a SOCKS5 server.
"""
# First we'll send the authentication packages we support.
if (self.__proxy[4]!=None) and (self.__proxy[5]!=None):
# The username/password details were supplied to the
# setproxy method so we support the USERNAME/PASSWORD
# authentication (in addition to the standard none).
self.sendall(struct.pack('BBBB', 0x05, 0x02, 0x00, 0x02))
else:
# No username/password were entered, therefore we
# only support connections with no authentication.
self.sendall(struct.pack('BBB', 0x05, 0x01, 0x00))
# We'll receive the server's response to determine which
# method was selected
chosenauth = self.__recvall(2)
if chosenauth[0:1] != chr(0x05).encode():
self.close()
raise GeneralProxyError((1, _generalerrors[1]))
# Check the chosen authentication method
if chosenauth[1:2] == chr(0x00).encode():
# No authentication is required
pass
elif chosenauth[1:2] == chr(0x02).encode():
# Okay, we need to perform a basic username/password
# authentication.
self.sendall(chr(0x01).encode() + chr(len(self.__proxy[4])) + self.__proxy[4] + chr(len(self.__proxy[5])) + self.__proxy[5])
authstat = self.__recvall(2)
if authstat[0:1] != chr(0x01).encode():
# Bad response
self.close()
raise GeneralProxyError((1, _generalerrors[1]))
if authstat[1:2] != chr(0x00).encode():
# Authentication failed
self.close()
raise Socks5AuthError((3, _socks5autherrors[3]))
# Authentication succeeded
else:
# Reaching here is always bad
self.close()
if chosenauth[1] == chr(0xFF).encode():
raise Socks5AuthError((2, _socks5autherrors[2]))
else:
raise GeneralProxyError((1, _generalerrors[1]))
def __connectsocks5(self, destaddr, destport):
# Now we can request the actual connection
req = struct.pack('BBB', 0x05, 0x01, 0x00)
# If the given destination address is an IP address, we'll
# use the IPv4 address request even if remote resolving was specified.
try:
ipaddr = socket.inet_aton(destaddr)
req = req + chr(0x01).encode() + ipaddr
except socket.error:
# Well it's not an IP number, so it's probably a DNS name.
if self.__proxy[3]:
# Resolve remotely
ipaddr = None
req = req + chr(0x03).encode() + chr(len(destaddr)).encode() + destaddr
else:
# Resolve locally
ipaddr = socket.inet_aton(socket.gethostbyname(destaddr))
req = req + chr(0x01).encode() + ipaddr
req = req + struct.pack(">H", destport)
self.sendall(req)
# Get the response
resp = self.__recvall(4)
if resp[0:1] != chr(0x05).encode():
self.close()
raise GeneralProxyError((1, _generalerrors[1]))
elif resp[1:2] != chr(0x00).encode():
# Connection failed
self.close()
if ord(resp[1:2])<=8:
raise Socks5Error((ord(resp[1:2]), _socks5errors[ord(resp[1:2])]))
else:
raise Socks5Error((9, _socks5errors[9]))
# Get the bound address/port
elif resp[3:4] == chr(0x01).encode():
boundaddr = self.__recvall(4)
elif resp[3:4] == chr(0x03).encode():
resp = resp + self.recv(1)
boundaddr = self.__recvall(ord(resp[4:5]))
else:
self.close()
raise GeneralProxyError((1,_generalerrors[1]))
boundport = struct.unpack(">H", self.__recvall(2))[0]
self.__proxysockname = (boundaddr, boundport)
if ipaddr != None:
self.__proxypeername = (socket.inet_ntoa(ipaddr), destport)
else:
self.__proxypeername = (destaddr, destport)
def __resolvesocks5(self, host):
# Now we can request the actual connection
req = struct.pack('BBB', 0x05, 0xF0, 0x00)
req += chr(0x03).encode() + chr(len(host)).encode() + host
req = req + struct.pack(">H", 8444)
self.sendall(req)
# Get the response
ip = ""
resp = self.__recvall(4)
if resp[0:1] != chr(0x05).encode():
self.close()
raise GeneralProxyError((1, _generalerrors[1]))
elif resp[1:2] != chr(0x00).encode():
# Connection failed
self.close()
if ord(resp[1:2])<=8:
raise Socks5Error((ord(resp[1:2]), _socks5errors[ord(resp[1:2])]))
else:
raise Socks5Error((9, _socks5errors[9]))
# Get the bound address/port
elif resp[3:4] == chr(0x01).encode():
ip = socket.inet_ntoa(self.__recvall(4))
elif resp[3:4] == chr(0x03).encode():
resp = resp + self.recv(1)
ip = self.__recvall(ord(resp[4:5]))
else:
self.close()
raise GeneralProxyError((1,_generalerrors[1]))
boundport = struct.unpack(">H", self.__recvall(2))[0]
return ip
def getproxysockname(self):
"""getsockname() -> address info
Returns the bound IP address and port number at the proxy.
"""
return self.__proxysockname
def getproxypeername(self):
"""getproxypeername() -> address info
Returns the IP and port number of the proxy.
"""
return _orgsocket.getpeername(self)
def getpeername(self):
"""getpeername() -> address info
Returns the IP address and port number of the destination
machine (note: getproxypeername returns the proxy)
"""
return self.__proxypeername
def getproxytype(self):
return self.__proxy[0]
def __negotiatesocks4(self,destaddr,destport):
"""__negotiatesocks4(self,destaddr,destport)
Negotiates a connection through a SOCKS4 server.
"""
# Check if the destination address provided is an IP address
rmtrslv = False
try:
ipaddr = socket.inet_aton(destaddr)
except socket.error:
# It's a DNS name. Check where it should be resolved.
if self.__proxy[3]:
ipaddr = struct.pack("BBBB", 0x00, 0x00, 0x00, 0x01)
rmtrslv = True
else:
ipaddr = socket.inet_aton(socket.gethostbyname(destaddr))
# Construct the request packet
req = struct.pack(">BBH", 0x04, 0x01, destport) + ipaddr
# The username parameter is considered userid for SOCKS4
if self.__proxy[4] != None:
req = req + self.__proxy[4]
req = req + chr(0x00).encode()
# DNS name if remote resolving is required
# NOTE: This is actually an extension to the SOCKS4 protocol
# called SOCKS4A and may not be supported in all cases.
if rmtrslv:
req = req + destaddr + chr(0x00).encode()
self.sendall(req)
# Get the response from the server
resp = self.__recvall(8)
if resp[0:1] != chr(0x00).encode():
# Bad data
self.close()
raise GeneralProxyError((1,_generalerrors[1]))
if resp[1:2] != chr(0x5A).encode():
# Server returned an error
self.close()
if ord(resp[1:2]) in (91, 92, 93):
self.close()
raise Socks4Error((ord(resp[1:2]), _socks4errors[ord(resp[1:2]) - 90]))
else:
raise Socks4Error((94, _socks4errors[4]))
# Get the bound address/port
self.__proxysockname = (socket.inet_ntoa(resp[4:]), struct.unpack(">H", resp[2:4])[0])
if rmtrslv != None:
self.__proxypeername = (socket.inet_ntoa(ipaddr), destport)
else:
self.__proxypeername = (destaddr, destport)
def __negotiatehttp(self, destaddr, destport):
"""__negotiatehttp(self,destaddr,destport)
Negotiates a connection through an HTTP server.
"""
# If we need to resolve locally, we do this now
if not self.__proxy[3]:
addr = socket.gethostbyname(destaddr)
else:
addr = destaddr
self.sendall(("CONNECT " + addr + ":" + str(destport) + " HTTP/1.1\r\n" + "Host: " + destaddr + "\r\n\r\n").encode())
# We read the response until we get the string "\r\n\r\n"
resp = self.recv(1)
while resp.find("\r\n\r\n".encode()) == -1:
resp = resp + self.recv(1)
# We just need the first line to check if the connection
# was successful
statusline = resp.splitlines()[0].split(" ".encode(), 2)
if statusline[0] not in ("HTTP/1.0".encode(), "HTTP/1.1".encode()):
self.close()
raise GeneralProxyError((1, _generalerrors[1]))
try:
statuscode = int(statusline[1])
except ValueError:
self.close()
raise GeneralProxyError((1, _generalerrors[1]))
if statuscode != 200:
self.close()
raise HTTPError((statuscode, statusline[2]))
self.__proxysockname = ("0.0.0.0", 0)
self.__proxypeername = (addr, destport)
def connect(self, destpair):
"""connect(self, despair)
Connects to the specified destination through a proxy.
destpar - A tuple of the IP/DNS address and the port number.
(identical to socket's connect).
To select the proxy server use setproxy().
"""
# Do a minimal input check first
if (not type(destpair) in (list,tuple)) or (len(destpair) < 2) or (type(destpair[0]) != type('')) or (type(destpair[1]) != int):
raise GeneralProxyError((5, _generalerrors[5]))
if self.__proxy[0] == PROXY_TYPE_SOCKS5:
if self.__proxy[2] != None:
portnum = self.__proxy[2]
else:
portnum = 1080
try:
_orgsocket.connect(self, (self.__proxy[1], portnum))
except socket.error as e:
# ENETUNREACH, WSAENETUNREACH
if e[0] in [101, 10051]:
raise GeneralProxyError((7, _generalerrors[7]))
# ECONNREFUSED, WSAECONNREFUSED
if e[0] in [111, 10061]:
raise GeneralProxyError((8, _generalerrors[8]))
# EHOSTUNREACH, WSAEHOSTUNREACH
if e[0] in [113, 10065]:
raise GeneralProxyError((9, _generalerrors[9]))
raise
self.__negotiatesocks5()
self.__connectsocks5(destpair[0], destpair[1])
elif self.__proxy[0] == PROXY_TYPE_SOCKS4:
if self.__proxy[2] != None:
portnum = self.__proxy[2]
else:
portnum = 1080
_orgsocket.connect(self,(self.__proxy[1], portnum))
self.__negotiatesocks4(destpair[0], destpair[1])
elif self.__proxy[0] == PROXY_TYPE_HTTP:
if self.__proxy[2] != None:
portnum = self.__proxy[2]
else:
portnum = 8080
try:
_orgsocket.connect(self,(self.__proxy[1], portnum))
except socket.error as e:
# ENETUNREACH, WSAENETUNREACH
if e[0] in [101, 10051]:
raise GeneralProxyError((7, _generalerrors[7]))
# ECONNREFUSED, WSAECONNREFUSED
if e[0] in [111, 10061]:
raise GeneralProxyError((8, _generalerrors[8]))
# EHOSTUNREACH, WSAEHOSTUNREACH
if e[0] in [113, 10065]:
raise GeneralProxyError((9, _generalerrors[9]))
raise
self.__negotiatehttp(destpair[0], destpair[1])
elif self.__proxy[0] == None:
_orgsocket.connect(self, (destpair[0], destpair[1]))
else:
raise GeneralProxyError((4, _generalerrors[4]))
def resolve(self, host):
if self.__proxy[0] == PROXY_TYPE_SOCKS5:
if self.__proxy[2] != None:
portnum = self.__proxy[2]
else:
portnum = 1080
_orgsocket.connect(self, (self.__proxy[1], portnum))
self.__negotiatesocks5()
return self.__resolvesocks5(host)
else:
return None

View File

@ -9,9 +9,6 @@ extPort = None
# for Tor hidden service # for Tor hidden service
socksIP = None socksIP = None
# Network protocols availability, initialised below
networkProtocolAvailability = None
appdata = '' # holds the location of the application data storage directory appdata = '' # holds the location of the application data storage directory
# Set to 1 by the doCleanShutdown function. # Set to 1 by the doCleanShutdown function.
@ -54,14 +51,6 @@ discoveredPeers = {}
Peer = collections.namedtuple('Peer', ['host', 'port']) Peer = collections.namedtuple('Peer', ['host', 'port'])
def resetNetworkProtocolAvailability():
global networkProtocolAvailability
networkProtocolAvailability = {'IPv4': None, 'IPv6': None, 'onion': None}
resetNetworkProtocolAvailability()
dandelion = 0 dandelion = 0
testmode = False testmode = False

View File

@ -1,21 +1,26 @@
"""
src/storage/filesystem.py
=========================
"""
from binascii import hexlify, unhexlify from binascii import hexlify, unhexlify
from os import listdir, makedirs, path, remove, rmdir from os import listdir, makedirs, path, remove, rmdir
import string import string
from threading import RLock from threading import RLock
import time import time
import traceback
from paths import lookupAppdataFolder from paths import lookupAppdataFolder
from storage import InventoryStorage, InventoryItem from storage import InventoryStorage, InventoryItem
class FilesystemInventory(InventoryStorage):
class FilesystemInventory(InventoryStorage): # pylint: disable=too-many-ancestors, abstract-method
"""Module for using filesystem (directory with files) for inventory storage"""
topDir = "inventory" topDir = "inventory"
objectDir = "objects" objectDir = "objects"
metadataFilename = "metadata" metadataFilename = "metadata"
dataFilename = "data" dataFilename = "data"
def __init__(self): def __init__(self):
super(self.__class__, self).__init__() super(FilesystemInventory, self).__init__()
self.baseDir = path.join(lookupAppdataFolder(), FilesystemInventory.topDir) self.baseDir = path.join(lookupAppdataFolder(), FilesystemInventory.topDir)
for createDir in [self.baseDir, path.join(self.baseDir, "objects")]: for createDir in [self.baseDir, path.join(self.baseDir, "objects")]:
if path.exists(createDir): if path.exists(createDir):
@ -23,72 +28,101 @@ class FilesystemInventory(InventoryStorage):
raise IOError("%s exists but it's not a directory" % (createDir)) raise IOError("%s exists but it's not a directory" % (createDir))
else: else:
makedirs(createDir) makedirs(createDir)
self.lock = RLock() # Guarantees that two receiveDataThreads don't receive and process the same message concurrently (probably sent by a malicious individual) # Guarantees that two receiveDataThreads don't receive and process the same message
# concurrently (probably sent by a malicious individual)
self.lock = RLock()
self._inventory = {} self._inventory = {}
self._load() self._load()
def __contains__(self, hash): def __contains__(self, hashval):
retval = False retval = False
for streamDict in self._inventory.values(): for streamDict in self._inventory.values():
if hash in streamDict: if hashval in streamDict:
return True return True
return False return False
def __getitem__(self, hash): def __getitem__(self, hashval):
for streamDict in self._inventory.values(): for streamDict in self._inventory.values():
try: try:
retval = streamDict[hash] retval = streamDict[hashval]
except KeyError: except KeyError:
continue continue
if retval.payload is None: if retval.payload is None:
retval = InventoryItem(retval.type, retval.stream, self.getData(hash), retval.expires, retval.tag) retval = InventoryItem(retval.type, retval.stream, self.getData(hashval), retval.expires, retval.tag)
return retval return retval
raise KeyError(hash) raise KeyError(hashval)
def __setitem__(self, hash, value): def __setitem__(self, hashval, value):
with self.lock: with self.lock:
value = InventoryItem(*value) value = InventoryItem(*value)
try: try:
makedirs(path.join(self.baseDir, FilesystemInventory.objectDir, hexlify(hash))) makedirs(path.join(self.baseDir, FilesystemInventory.objectDir, hexlify(hashval)))
except OSError: except OSError:
pass pass
try: try:
with open(path.join(self.baseDir, FilesystemInventory.objectDir, hexlify(hash), FilesystemInventory.metadataFilename), 'w') as f: with open(
path.join(
self.baseDir,
FilesystemInventory.objectDir,
hexlify(hashval),
FilesystemInventory.metadataFilename,
),
"w",
) as f:
f.write("%s,%s,%s,%s," % (value.type, value.stream, value.expires, hexlify(value.tag))) f.write("%s,%s,%s,%s," % (value.type, value.stream, value.expires, hexlify(value.tag)))
with open(path.join(self.baseDir, FilesystemInventory.objectDir, hexlify(hash), FilesystemInventory.dataFilename), 'w') as f: with open(
path.join(
self.baseDir,
FilesystemInventory.objectDir,
hexlify(hashval),
FilesystemInventory.dataFilename,
),
"w",
) as f:
f.write(value.payload) f.write(value.payload)
except IOError: except IOError:
raise KeyError raise KeyError
try: try:
self._inventory[value.stream][hash] = value self._inventory[value.stream][hashval] = value
except KeyError: except KeyError:
self._inventory[value.stream] = {} self._inventory[value.stream] = {}
self._inventory[value.stream][hash] = value self._inventory[value.stream][hashval] = value
def delHashId(self, hash): def delHashId(self, hashval):
for stream in self._inventory.keys(): """Remove object from inventory"""
for stream in self._inventory:
try: try:
del self._inventory[stream][hash] del self._inventory[stream][hashval]
except KeyError: except KeyError:
pass pass
with self.lock: with self.lock:
try: try:
remove(path.join(self.baseDir, FilesystemInventory.objectDir, hexlify(hash), FilesystemInventory.metadataFilename)) remove(
path.join(
self.baseDir,
FilesystemInventory.objectDir,
hexlify(hashval),
FilesystemInventory.metadataFilename))
except IOError: except IOError:
pass pass
try: try:
remove(path.join(self.baseDir, FilesystemInventory.objectDir, hexlify(hash), FilesystemInventory.dataFilename)) remove(
path.join(
self.baseDir,
FilesystemInventory.objectDir,
hexlify(hashval),
FilesystemInventory.dataFilename))
except IOError: except IOError:
pass pass
try: try:
rmdir(path.join(self.baseDir, FilesystemInventory.objectDir, hexlify(hash))) rmdir(path.join(self.baseDir, FilesystemInventory.objectDir, hexlify(hashval)))
except IOError: except IOError:
pass pass
def __iter__(self): def __iter__(self):
elems = [] elems = []
for streamDict in self._inventory.values(): for streamDict in self._inventory.values():
elems.extend (streamDict.keys()) elems.extend(streamDict.keys())
return elems.__iter__() return elems.__iter__()
def __len__(self): def __len__(self):
@ -103,39 +137,61 @@ class FilesystemInventory(InventoryStorage):
try: try:
objectType, streamNumber, expiresTime, tag = self.getMetadata(hashId) objectType, streamNumber, expiresTime, tag = self.getMetadata(hashId)
try: try:
newInventory[streamNumber][hashId] = InventoryItem(objectType, streamNumber, None, expiresTime, tag) newInventory[streamNumber][hashId] = InventoryItem(
objectType, streamNumber, None, expiresTime, tag)
except KeyError: except KeyError:
newInventory[streamNumber] = {} newInventory[streamNumber] = {}
newInventory[streamNumber][hashId] = InventoryItem(objectType, streamNumber, None, expiresTime, tag) newInventory[streamNumber][hashId] = InventoryItem(
objectType, streamNumber, None, expiresTime, tag)
except KeyError: except KeyError:
print "error loading %s" % (hexlify(hashId)) print "error loading %s" % (hexlify(hashId))
pass
self._inventory = newInventory self._inventory = newInventory
# for i, v in self._inventory.items(): # for i, v in self._inventory.items():
# print "loaded stream: %s, %i items" % (i, len(v)) # print "loaded stream: %s, %i items" % (i, len(v))
def stream_list(self): def stream_list(self):
"""Return list of streams"""
return self._inventory.keys() return self._inventory.keys()
def object_list(self): def object_list(self):
"""Return inventory vectors (hashes) from a directory"""
return [unhexlify(x) for x in listdir(path.join(self.baseDir, FilesystemInventory.objectDir))] return [unhexlify(x) for x in listdir(path.join(self.baseDir, FilesystemInventory.objectDir))]
def getData(self, hashId): def getData(self, hashId):
"""Get object data"""
try: try:
with open(path.join(self.baseDir, FilesystemInventory.objectDir, hexlify(hashId), FilesystemInventory.dataFilename), 'r') as f: with open(
path.join(
self.baseDir,
FilesystemInventory.objectDir,
hexlify(hashId),
FilesystemInventory.dataFilename,
),
"r",
) as f:
return f.read() return f.read()
except IOError: except IOError:
raise AttributeError raise AttributeError
def getMetadata(self, hashId): def getMetadata(self, hashId):
"""Get object metadata"""
try: try:
with open(path.join(self.baseDir, FilesystemInventory.objectDir, hexlify(hashId), FilesystemInventory.metadataFilename), 'r') as f: with open(
path.join(
self.baseDir,
FilesystemInventory.objectDir,
hexlify(hashId),
FilesystemInventory.metadataFilename,
),
"r",
) as f:
objectType, streamNumber, expiresTime, tag, undef = string.split(f.read(), ",", 4) objectType, streamNumber, expiresTime, tag, undef = string.split(f.read(), ",", 4)
return [int(objectType), int(streamNumber), int(expiresTime), unhexlify(tag)] return [int(objectType), int(streamNumber), int(expiresTime), unhexlify(tag)]
except IOError: except IOError:
raise KeyError raise KeyError
def by_type_and_tag(self, objectType, tag): def by_type_and_tag(self, objectType, tag):
"""Get a list of objects filtered by object type and tag"""
retval = [] retval = []
for stream, streamDict in self._inventory: for stream, streamDict in self._inventory:
for hashId, item in streamDict: for hashId, item in streamDict:
@ -149,12 +205,14 @@ class FilesystemInventory(InventoryStorage):
return retval return retval
def hashes_by_stream(self, stream): def hashes_by_stream(self, stream):
"""Return inventory vectors (hashes) for a stream"""
try: try:
return self._inventory[stream].keys() return self._inventory[stream].keys()
except KeyError: except KeyError:
return [] return []
def unexpired_hashes_by_stream(self, stream): def unexpired_hashes_by_stream(self, stream):
"""Return unexpired hashes in the inventory for a particular stream"""
t = int(time.time()) t = int(time.time())
try: try:
return [x for x, value in self._inventory[stream].items() if value.expires > t] return [x for x, value in self._inventory[stream].items() if value.expires > t]
@ -162,9 +220,11 @@ class FilesystemInventory(InventoryStorage):
return [] return []
def flush(self): def flush(self):
"""Flush the inventory and create a new, empty one"""
self._load() self._load()
def clean(self): def clean(self):
"""Clean out old items from the inventory"""
minTime = int(time.time()) - (60 * 60 * 30) minTime = int(time.time()) - (60 * 60 * 30)
deletes = [] deletes = []
for stream, streamDict in self._inventory.items(): for stream, streamDict in self._inventory.items():

View File

@ -1,45 +1,59 @@
import collections """
from threading import current_thread, enumerate as threadingEnumerate, RLock src/storage/sqlite.py
import Queue =========================
"""
import sqlite3 import sqlite3
import time import time
from threading import RLock
from helper_sql import * from helper_sql import sqlQuery, SqlBulkExecute, sqlExecute
from storage import InventoryStorage, InventoryItem from storage import InventoryStorage, InventoryItem
class SqliteInventory(InventoryStorage):
def __init__(self):
super(self.__class__, self).__init__()
self._inventory = {} #of objects (like msg payloads and pubkey payloads) Does not include protocol headers (the first 24 bytes of each packet).
self._objects = {} # cache for existing objects, used for quick lookups if we have an object. This is used for example whenever we receive an inv message from a peer to check to see what items are new to us. We don't delete things out of it; instead, the singleCleaner thread clears and refills it.
self.lock = RLock() # Guarantees that two receiveDataThreads don't receive and process the same message concurrently (probably sent by a malicious individual)
def __contains__(self, hash): class SqliteInventory(InventoryStorage): # pylint: disable=too-many-ancestors
"""Inventory using SQLite"""
def __init__(self):
super(SqliteInventory, self).__init__()
# of objects (like msg payloads and pubkey payloads)
# Does not include protocol headers (the first 24 bytes of each packet).
self._inventory = {}
# cache for existing objects, used for quick lookups if we have an object.
# This is used for example whenever we receive an inv message from a peer
# to check to see what items are new to us.
# We don't delete things out of it; instead, the singleCleaner thread clears and refills it.
self._objects = {}
# Guarantees that two receiveDataThreads don't receive and process the same message concurrently
# (probably sent by a malicious individual)
self.lock = RLock()
def __contains__(self, hash_):
with self.lock: with self.lock:
if hash in self._objects: if hash_ in self._objects:
return True return True
rows = sqlQuery('SELECT streamnumber FROM inventory WHERE hash=?', sqlite3.Binary(hash)) rows = sqlQuery('SELECT streamnumber FROM inventory WHERE hash=?', sqlite3.Binary(hash_))
if not rows: if not rows:
return False return False
self._objects[hash] = rows[0][0] self._objects[hash_] = rows[0][0]
return True return True
def __getitem__(self, hash): def __getitem__(self, hash_):
with self.lock: with self.lock:
if hash in self._inventory: if hash_ in self._inventory:
return self._inventory[hash] return self._inventory[hash_]
rows = sqlQuery('SELECT objecttype, streamnumber, payload, expirestime, tag FROM inventory WHERE hash=?', sqlite3.Binary(hash)) rows = sqlQuery(
'SELECT objecttype, streamnumber, payload, expirestime, tag FROM inventory WHERE hash=?',
sqlite3.Binary(hash_))
if not rows: if not rows:
raise KeyError(hash) raise KeyError(hash_)
return InventoryItem(*rows[0]) return InventoryItem(*rows[0])
def __setitem__(self, hash, value): def __setitem__(self, hash_, value):
with self.lock: with self.lock:
value = InventoryItem(*value) value = InventoryItem(*value)
self._inventory[hash] = value self._inventory[hash_] = value
self._objects[hash] = value.stream self._objects[hash_] = value.stream
def __delitem__(self, hash): def __delitem__(self, hash_):
raise NotImplementedError raise NotImplementedError
def __iter__(self): def __iter__(self):
@ -55,18 +69,22 @@ class SqliteInventory(InventoryStorage):
def by_type_and_tag(self, objectType, tag): def by_type_and_tag(self, objectType, tag):
with self.lock: with self.lock:
values = [value for value in self._inventory.values() if value.type == objectType and value.tag == tag] values = [value for value in self._inventory.values() if value.type == objectType and value.tag == tag]
values += (InventoryItem(*value) for value in sqlQuery('SELECT objecttype, streamnumber, payload, expirestime, tag FROM inventory WHERE objecttype=? AND tag=?', objectType, sqlite3.Binary(tag))) values += (InventoryItem(*value) for value in sqlQuery(
'SELECT objecttype, streamnumber, payload, expirestime, tag \
FROM inventory WHERE objecttype=? AND tag=?', objectType, sqlite3.Binary(tag)))
return values return values
def unexpired_hashes_by_stream(self, stream): def unexpired_hashes_by_stream(self, stream):
with self.lock: with self.lock:
t = int(time.time()) t = int(time.time())
hashes = [x for x, value in self._inventory.items() if value.stream == stream and value.expires > t] hashes = [x for x, value in self._inventory.items() if value.stream == stream and value.expires > t]
hashes += (str(payload) for payload, in sqlQuery('SELECT hash FROM inventory WHERE streamnumber=? AND expirestime>?', stream, t)) hashes += (str(payload) for payload, in sqlQuery(
'SELECT hash FROM inventory WHERE streamnumber=? AND expirestime>?', stream, t))
return hashes return hashes
def flush(self): def flush(self):
with self.lock: # If you use both the inventoryLock and the sqlLock, always use the inventoryLock OUTSIDE of the sqlLock. with self.lock:
# If you use both the inventoryLock and the sqlLock, always use the inventoryLock OUTSIDE of the sqlLock.
with SqlBulkExecute() as sql: with SqlBulkExecute() as sql:
for objectHash, value in self._inventory.items(): for objectHash, value in self._inventory.items():
sql.execute('INSERT INTO inventory VALUES (?, ?, ?, ?, ?, ?)', sqlite3.Binary(objectHash), *value) sql.execute('INSERT INTO inventory VALUES (?, ?, ?, ?, ?, ?)', sqlite3.Binary(objectHash), *value)
@ -74,8 +92,7 @@ class SqliteInventory(InventoryStorage):
def clean(self): def clean(self):
with self.lock: with self.lock:
sqlExecute('DELETE FROM inventory WHERE expirestime<?',int(time.time()) - (60 * 60 * 3)) sqlExecute('DELETE FROM inventory WHERE expirestime<?', int(time.time()) - (60 * 60 * 3))
self._objects.clear() self._objects.clear()
for objectHash, value in self._inventory.items(): for objectHash, value in self._inventory.items():
self._objects[objectHash] = value.stream self._objects[objectHash] = value.stream

View File

@ -1,27 +1,33 @@
"""
src/storage/storage.py
======================
"""
import collections import collections
InventoryItem = collections.namedtuple('InventoryItem', 'type stream payload expires tag') InventoryItem = collections.namedtuple('InventoryItem', 'type stream payload expires tag')
class Storage(object): class Storage(object):
"""Base class for storing inventory (extendable for other items to store)"""
pass pass
# def __init__(self):
# super(self.__class__, self).__init__()
class InventoryStorage(Storage, collections.MutableMapping): class InventoryStorage(Storage, collections.MutableMapping):
"""Module used for inventory storage"""
def __init__(self): def __init__(self):
# super(self.__class__, self).__init__() # pylint: disable=super-init-not-called
self.numberOfInventoryLookupsPerformed = 0 self.numberOfInventoryLookupsPerformed = 0
def __contains__(self, hash): def __contains__(self, _):
raise NotImplementedError raise NotImplementedError
def __getitem__(self, hash): def __getitem__(self, _):
raise NotImplementedError raise NotImplementedError
def __setitem__(self, hash, value): def __setitem__(self, _, value):
raise NotImplementedError raise NotImplementedError
def __delitem__(self, hash): def __delitem__(self, _):
raise NotImplementedError raise NotImplementedError
def __iter__(self): def __iter__(self):
@ -31,18 +37,24 @@ class InventoryStorage(Storage, collections.MutableMapping):
raise NotImplementedError raise NotImplementedError
def by_type_and_tag(self, objectType, tag): def by_type_and_tag(self, objectType, tag):
"""Return objects filtered by object type and tag"""
raise NotImplementedError raise NotImplementedError
def unexpired_hashes_by_stream(self, stream): def unexpired_hashes_by_stream(self, stream):
"""Return unexpired inventory vectors filtered by stream"""
raise NotImplementedError raise NotImplementedError
def flush(self): def flush(self):
"""Flush cache"""
raise NotImplementedError raise NotImplementedError
def clean(self): def clean(self):
"""Free memory / perform garbage collection"""
raise NotImplementedError raise NotImplementedError
class MailboxStorage(Storage, collections.MutableMapping):
class MailboxStorage(Storage, collections.MutableMapping): # pylint: disable=abstract-method
"""Method for storing mails"""
def __init__(self): def __init__(self):
# super(self.__class__, self).__init__() # pylint: disable=super-init-not-called
pass pass

View File

@ -13,15 +13,19 @@ import unittest
import knownnodes import knownnodes
import state import state
from bmconfigparser import BMConfigParser
from helper_msgcoding import MsgEncode, MsgDecode from helper_msgcoding import MsgEncode, MsgDecode
from network import asyncore_pollchoose as asyncore from network import asyncore_pollchoose as asyncore
from network.tcp import TCPConnection from network.connectionpool import BMConnectionPool
from network.tcp import Socks4aBMConnection, Socks5BMConnection, TCPConnection
from queues import excQueue from queues import excQueue
knownnodes_file = os.path.join(state.appdata, 'knownnodes.dat') knownnodes_file = os.path.join(state.appdata, 'knownnodes.dat')
program = None
def pickle_knownnodes(): def pickle_knownnodes():
"""Generate old style pickled knownnodes.dat"""
now = time.time() now = time.time()
with open(knownnodes_file, 'wb') as dst: with open(knownnodes_file, 'wb') as dst:
pickle.dump({ pickle.dump({
@ -37,6 +41,7 @@ def pickle_knownnodes():
def cleanup(): def cleanup():
"""Cleanup application files"""
os.remove(knownnodes_file) os.remove(knownnodes_file)
@ -80,8 +85,10 @@ class TestCore(unittest.TestCase):
' with no subject!' % e ' with no subject!' % e
) )
@unittest.skip('Bad environment for asyncore.loop')
def test_tcpconnection(self): def test_tcpconnection(self):
"""initial fill script from network.tcp""" """initial fill script from network.tcp"""
BMConfigParser().set('bitmessagesettings', 'dontconnect', 'true')
try: try:
for peer in (state.Peer("127.0.0.1", 8448),): for peer in (state.Peer("127.0.0.1", 8448),):
direct = TCPConnection(peer) direct = TCPConnection(peer)
@ -91,10 +98,18 @@ class TestCore(unittest.TestCase):
except: except:
self.fail('Exception in test loop') self.fail('Exception in test loop')
def _wipe_knownnodes(self): @staticmethod
def _wipe_knownnodes():
with knownnodes.knownNodesLock: with knownnodes.knownNodesLock:
knownnodes.knownNodes = {stream: {} for stream in range(1, 4)} knownnodes.knownNodes = {stream: {} for stream in range(1, 4)}
@staticmethod
def _outdate_knownnodes():
with knownnodes.knownNodesLock:
for nodes in knownnodes.knownNodes.itervalues():
for node in nodes.itervalues():
node['lastseen'] -= 2419205 # older than 28 days
def test_knownnodes_pickle(self): def test_knownnodes_pickle(self):
"""ensure that 3 nodes was imported for each stream""" """ensure that 3 nodes was imported for each stream"""
pickle_knownnodes() pickle_knownnodes()
@ -117,11 +132,10 @@ class TestCore(unittest.TestCase):
def test_0_cleaner(self): def test_0_cleaner(self):
"""test knownnodes starvation leading to IndexError in Asyncore""" """test knownnodes starvation leading to IndexError in Asyncore"""
for nodes in knownnodes.knownNodes.itervalues(): self._outdate_knownnodes()
for node in nodes.itervalues():
node['lastseen'] -= 2419205 # older than 28 days
# time.sleep(303) # singleCleaner wakes up every 5 min # time.sleep(303) # singleCleaner wakes up every 5 min
knownnodes.cleanupKnownNodes() knownnodes.cleanupKnownNodes()
self.assertTrue(knownnodes.knownNodes[1])
while True: while True:
try: try:
thread, exc = excQueue.get(block=False) thread, exc = excQueue.get(block=False)
@ -130,9 +144,49 @@ class TestCore(unittest.TestCase):
if thread == 'Asyncore' and isinstance(exc, IndexError): if thread == 'Asyncore' and isinstance(exc, IndexError):
self.fail("IndexError because of empty knownNodes!") self.fail("IndexError because of empty knownNodes!")
def _initiate_bootstrap(self):
BMConfigParser().set('bitmessagesettings', 'dontconnect', 'true')
self._outdate_knownnodes()
knownnodes.addKnownNode(1, state.Peer('127.0.0.1', 8444), is_self=True)
knownnodes.cleanupKnownNodes()
time.sleep(2)
def run(): def _check_bootstrap(self):
_started = time.time()
BMConfigParser().remove_option('bitmessagesettings', 'dontconnect')
proxy_type = BMConfigParser().safeGet(
'bitmessagesettings', 'socksproxytype')
if proxy_type == 'SOCKS5':
connection_base = Socks5BMConnection
elif proxy_type == 'SOCKS4a':
connection_base = Socks4aBMConnection
else:
connection_base = TCPConnection
for _ in range(180):
time.sleep(1)
for peer, con in BMConnectionPool().outboundConnections.iteritems():
if not peer.host.startswith('bootstrap'):
self.assertIsInstance(con, connection_base)
self.assertNotEqual(peer.host, '127.0.0.1')
return
else: # pylint: disable=useless-else-on-loop
self.fail(
'Failed to connect during %s sec' % (time.time() - _started))
def test_bootstrap(self):
"""test bootstrapping"""
self._initiate_bootstrap()
self._check_bootstrap()
self._initiate_bootstrap()
BMConfigParser().set('bitmessagesettings', 'socksproxytype', 'stem')
program.start_proxyconfig(BMConfigParser())
self._check_bootstrap()
def run(prog):
"""Starts all tests defined in this module""" """Starts all tests defined in this module"""
global program # pylint: disable=global-statement
program = prog
loader = unittest.TestLoader() loader = unittest.TestLoader()
loader.sortTestMethodsUsing = None loader.sortTestMethodsUsing = None
suite = loader.loadTestsFromTestCase(TestCore) suite = loader.loadTestsFromTestCase(TestCore)

View File

@ -31,7 +31,7 @@ class TestAPIShutdown(TestAPIProto, TestProcessShutdown):
"""Separate test case for API command 'shutdown'""" """Separate test case for API command 'shutdown'"""
def test_shutdown(self): def test_shutdown(self):
"""Shutdown the pybitmessage""" """Shutdown the pybitmessage"""
self.assertEquals(self.api.shutdown(), 'done') self.assertEqual(self.api.shutdown(), 'done')
for _ in range(5): for _ in range(5):
if not self.process.is_running(): if not self.process.is_running():
break break

View File

@ -26,6 +26,7 @@ class TestConfig(unittest.TestCase):
False False
) )
# no arg for default # no arg for default
# pylint: disable=too-many-function-args
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
BMConfigParser().safeGetBoolean( BMConfigParser().safeGetBoolean(
'nonexistent', 'nonexistent', True) 'nonexistent', 'nonexistent', True)
@ -47,9 +48,9 @@ class TestProcessConfig(TestProcessProto):
config = BMConfigParser() config = BMConfigParser()
config.read(os.path.join(self.home, 'keys.dat')) config.read(os.path.join(self.home, 'keys.dat'))
self.assertEquals(config.safeGetInt( self.assertEqual(config.safeGetInt(
'bitmessagesettings', 'settingsversion'), 10) 'bitmessagesettings', 'settingsversion'), 10)
self.assertEquals(config.safeGetInt( self.assertEqual(config.safeGetInt(
'bitmessagesettings', 'port'), 8444) 'bitmessagesettings', 'port'), 8444)
# don't connect # don't connect
self.assertTrue(config.safeGetBoolean( self.assertTrue(config.safeGetBoolean(
@ -59,7 +60,7 @@ class TestProcessConfig(TestProcessProto):
'bitmessagesettings', 'apienabled')) 'bitmessagesettings', 'apienabled'))
# extralowdifficulty is false # extralowdifficulty is false
self.assertEquals(config.safeGetInt( self.assertEqual(config.safeGetInt(
'bitmessagesettings', 'defaultnoncetrialsperbyte'), 1000) 'bitmessagesettings', 'defaultnoncetrialsperbyte'), 1000)
self.assertEquals(config.safeGetInt( self.assertEqual(config.safeGetInt(
'bitmessagesettings', 'defaultpayloadlengthextrabytes'), 1000) 'bitmessagesettings', 'defaultpayloadlengthextrabytes'), 1000)