Merge branch 'v0.6' of https://github.com/surbhicis/PyBitmessage into UiChanges
This commit is contained in:
commit
a3bbcdd795
|
@ -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
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
python_prctl
|
python_prctl
|
||||||
psutil
|
psutil
|
||||||
pycrypto
|
pycrypto
|
||||||
|
stem
|
||||||
|
|
1
setup.py
1
setup.py
|
@ -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'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -1,40 +1,40 @@
|
||||||
|
"""
|
||||||
|
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
|
||||||
#
|
#
|
||||||
# This has only been tested on Arch Linux and Linux Mint
|
# This has only been tested on Arch Linux and Linux Mint
|
||||||
# Dependencies:
|
# Dependencies:
|
||||||
# * from python2-pip
|
# * from python2-pip
|
||||||
# * 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,51 +261,63 @@ 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)
|
||||||
n = log.count('\n')
|
n = log.count('\n')
|
||||||
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):
|
||||||
if chr(c) in '12345678':
|
if chr(c) in '12345678':
|
||||||
global menutab
|
global menutab
|
||||||
menutab = int(chr(c))
|
menutab = int(chr(c))
|
||||||
|
@ -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,29 +375,31 @@ 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:
|
||||||
toaddr = fromaddr
|
toaddr = fromaddr
|
||||||
|
|
||||||
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 != []:
|
||||||
body = "\n\n------------------------------------------------------\n"
|
body = "\n\n------------------------------------------------------\n"
|
||||||
for row in ret:
|
for row in ret:
|
||||||
body, = row
|
body, = row
|
||||||
|
|
||||||
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,21 +995,24 @@ 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"
|
||||||
what = "%%"
|
what = "%%"
|
||||||
ret = sqlQuery("""SELECT msgid, toaddress, fromaddress, subject, received, read
|
ret = sqlQuery("""SELECT msgid, toaddress, fromaddress, subject, received, read
|
||||||
|
@ -847,7 +1022,7 @@ def loadInbox():
|
||||||
for row in ret:
|
for row in ret:
|
||||||
msgid, toaddr, fromaddr, subject, received, read = row
|
msgid, toaddr, fromaddr, subject, received, read = row
|
||||||
subject = ascii(shared.fixPotentiallyInvalidUTF8Data(subject))
|
subject = ascii(shared.fixPotentiallyInvalidUTF8Data(subject))
|
||||||
|
|
||||||
# Set label for to address
|
# Set label for to address
|
||||||
try:
|
try:
|
||||||
if toaddr == BROADCAST_STR:
|
if toaddr == BROADCAST_STR:
|
||||||
|
@ -859,17 +1034,17 @@ def loadInbox():
|
||||||
if tolabel == "":
|
if tolabel == "":
|
||||||
tolabel = toaddr
|
tolabel = toaddr
|
||||||
tolabel = shared.fixPotentiallyInvalidUTF8Data(tolabel)
|
tolabel = shared.fixPotentiallyInvalidUTF8Data(tolabel)
|
||||||
|
|
||||||
# Set label for from address
|
# Set label for from address
|
||||||
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:
|
||||||
|
@ -877,16 +1052,19 @@ def loadInbox():
|
||||||
if fromlabel == "":
|
if fromlabel == "":
|
||||||
fromlabel = fromaddr
|
fromlabel = fromaddr
|
||||||
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"
|
||||||
what = "%%"
|
what = "%%"
|
||||||
ret = sqlQuery("""SELECT toaddress, fromaddress, subject, status, ackdata, lastactiontime
|
ret = sqlQuery("""SELECT toaddress, fromaddress, subject, status, ackdata, lastactiontime
|
||||||
|
@ -896,7 +1074,7 @@ def loadSent():
|
||||||
for row in ret:
|
for row in ret:
|
||||||
toaddr, fromaddr, subject, status, ackdata, lastactiontime = row
|
toaddr, fromaddr, subject, status, ackdata, lastactiontime = row
|
||||||
subject = ascii(shared.fixPotentiallyInvalidUTF8Data(subject))
|
subject = ascii(shared.fixPotentiallyInvalidUTF8Data(subject))
|
||||||
|
|
||||||
# Set label for to address
|
# Set label for to address
|
||||||
tolabel = ""
|
tolabel = ""
|
||||||
qr = sqlQuery("SELECT label FROM addressbook WHERE address=?", toaddr)
|
qr = sqlQuery("SELECT label FROM addressbook WHERE address=?", toaddr)
|
||||||
|
@ -913,14 +1091,14 @@ def loadSent():
|
||||||
tolabel = BMConfigParser().get(toaddr, "label")
|
tolabel = BMConfigParser().get(toaddr, "label")
|
||||||
if tolabel == "":
|
if tolabel == "":
|
||||||
tolabel = toaddr
|
tolabel = toaddr
|
||||||
|
|
||||||
# Set label for from address
|
# Set label for from address
|
||||||
fromlabel = ""
|
fromlabel = ""
|
||||||
if BMConfigParser().has_section(fromaddr):
|
if BMConfigParser().has_section(fromaddr):
|
||||||
fromlabel = BMConfigParser().get(fromaddr, "label")
|
fromlabel = BMConfigParser().get(fromaddr, "label")
|
||||||
if fromlabel == "":
|
if fromlabel == "":
|
||||||
fromlabel = fromaddr
|
fromlabel = fromaddr
|
||||||
|
|
||||||
# Set status string
|
# Set status string
|
||||||
if status == "awaitingpubkey":
|
if status == "awaitingpubkey":
|
||||||
statstr = "Waiting for their public key. Will request it again soon"
|
statstr = "Waiting for their public key. Will request it again soon"
|
||||||
|
@ -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,31 +1130,47 @@ 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")
|
||||||
for row in ret:
|
for row in ret:
|
||||||
label, addr = row
|
label, addr = row
|
||||||
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,51 +1181,53 @@ 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()
|
||||||
loadSent()
|
loadSent()
|
||||||
loadAddrBook()
|
loadAddrBook()
|
||||||
loadSubscriptions()
|
loadSubscriptions()
|
||||||
loadBlackWhiteList()
|
loadBlackWhiteList()
|
||||||
|
|
||||||
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)
|
||||||
curses.curs_set(0)
|
curses.curs_set(0)
|
||||||
stdscr.timeout(1000)
|
stdscr.timeout(1000)
|
||||||
|
|
||||||
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()
|
||||||
for addressInKeysFile in configSections:
|
for addressInKeysFile in configSections:
|
||||||
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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,26 +628,13 @@ 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
|
||||||
|
|
||||||
# switch back to this when replying
|
# switch back to this when replying
|
||||||
self.replyFromTab = None
|
self.replyFromTab = None
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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.'
|
|
||||||
)
|
|
|
@ -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]
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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 = (
|
||||||
|
|
|
@ -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"""
|
||||||
|
|
|
@ -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"""
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
src/plugins/plugin.py
|
||||||
|
===================================
|
||||||
|
"""
|
||||||
import pkg_resources
|
import pkg_resources
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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!
|
|
|
@ -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.
|
|
201
src/socks/README
201
src/socks/README
|
@ -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.
|
|
|
@ -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
|
|
11
src/state.py
11
src/state.py
|
@ -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
|
||||||
|
|
|
@ -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,44 +137,66 @@ 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:
|
||||||
if item.type == objectType and item.tag == tag:
|
if item.type == objectType and item.tag == tag:
|
||||||
try:
|
try:
|
||||||
if item.payload is None:
|
if item.payload is None:
|
||||||
item.payload = self.getData(hashId)
|
item.payload = self.getData(hashId)
|
||||||
except IOError:
|
except IOError:
|
||||||
|
@ -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():
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Reference in New Issue
Block a user