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:
|
||||
- build-essential
|
||||
- libcap-dev
|
||||
- tor
|
||||
install:
|
||||
- pip install -r requirements.txt
|
||||
- ln -s src pybitmessage # tests environment
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
python_prctl
|
||||
psutil
|
||||
pycrypto
|
||||
stem
|
||||
|
|
1
setup.py
1
setup.py
|
@ -70,7 +70,6 @@ if __name__ == "__main__":
|
|||
'pybitmessage.network',
|
||||
'pybitmessage.plugins',
|
||||
'pybitmessage.pyelliptic',
|
||||
'pybitmessage.socks',
|
||||
'pybitmessage.storage'
|
||||
]
|
||||
|
||||
|
|
|
@ -1,40 +1,40 @@
|
|||
"""
|
||||
src/bitmessagecurses/__init__.py
|
||||
================================
|
||||
"""
|
||||
|
||||
# Copyright (c) 2014 Luke Montalvo <lukemontalvo@gmail.com>
|
||||
# This file adds a alternative commandline interface, feel free to critique and fork
|
||||
#
|
||||
#
|
||||
# This has only been tested on Arch Linux and Linux Mint
|
||||
# Dependencies:
|
||||
# * from python2-pip
|
||||
# * python2-pythondialog
|
||||
# * dialog
|
||||
|
||||
import ConfigParser
|
||||
import curses
|
||||
import os
|
||||
import sys
|
||||
import StringIO
|
||||
from textwrap import *
|
||||
|
||||
import time
|
||||
from time import strftime, localtime
|
||||
from textwrap import fill
|
||||
from threading import Timer
|
||||
|
||||
import curses
|
||||
import dialog
|
||||
from dialog import Dialog
|
||||
from helper_sql import *
|
||||
from helper_ackPayload import genAckPayload
|
||||
|
||||
from addresses import *
|
||||
import ConfigParser
|
||||
from addresses import addBMIfNotPresent, decodeAddress
|
||||
from bmconfigparser import BMConfigParser
|
||||
from dialog import Dialog
|
||||
from helper_ackPayload import genAckPayload
|
||||
from helper_sql import sqlExecute, sqlQuery
|
||||
from inventory import Inventory
|
||||
import l10n
|
||||
import network.stats
|
||||
from pyelliptic.openssl import OpenSSL
|
||||
import queues
|
||||
import shared
|
||||
import shutdown
|
||||
import network.stats
|
||||
|
||||
|
||||
quit = False
|
||||
quit = False # pylint: disable=redefined-builtin
|
||||
menutab = 1
|
||||
menu = ["Inbox", "Send", "Sent", "Your Identities", "Subscriptions", "Address Book", "Blacklist", "Network Status"]
|
||||
naptime = 100
|
||||
|
@ -60,156 +60,189 @@ bwtype = "black"
|
|||
|
||||
BROADCAST_STR = "[Broadcast subscribers]"
|
||||
|
||||
class printLog:
|
||||
|
||||
class printLog: # pylint: disable=no-self-use, no-init, old-style-class
|
||||
"""Printing logs"""
|
||||
|
||||
def write(self, output):
|
||||
# pylint: disable=global-statement
|
||||
global log
|
||||
log += output
|
||||
|
||||
def flush(self):
|
||||
pass
|
||||
class errLog:
|
||||
|
||||
|
||||
class errLog: # pylint: disable=no-self-use, no-init, old-style-class
|
||||
"""Error logs"""
|
||||
def write(self, output):
|
||||
# pylint: disable=global-statement
|
||||
global log
|
||||
log += "!"+output
|
||||
log += "!" + output
|
||||
|
||||
def flush(self):
|
||||
pass
|
||||
|
||||
|
||||
printlog = printLog()
|
||||
errlog = errLog()
|
||||
|
||||
|
||||
def cpair(a):
|
||||
"""Color pairs"""
|
||||
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)
|
||||
return r
|
||||
|
||||
|
||||
def ascii(s):
|
||||
"""ASCII values"""
|
||||
r = ""
|
||||
for c in s:
|
||||
if ord(c) in range(128):
|
||||
r += c
|
||||
return r
|
||||
|
||||
|
||||
def drawmenu(stdscr):
|
||||
"""Creating menu's"""
|
||||
menustr = " "
|
||||
for i in range(0, len(menu)):
|
||||
if menutab == i+1:
|
||||
for i, _ in enumerate(menu):
|
||||
if menutab == i + 1:
|
||||
menustr = menustr[:-1]
|
||||
menustr += "["
|
||||
menustr += str(i+1)+menu[i]
|
||||
if menutab == i+1:
|
||||
menustr += str(i + 1) + menu[i]
|
||||
if menutab == i + 1:
|
||||
menustr += "] "
|
||||
elif i != len(menu)-1:
|
||||
elif i != len(menu) - 1:
|
||||
menustr += " "
|
||||
stdscr.addstr(2, 5, menustr, curses.A_UNDERLINE)
|
||||
|
||||
|
||||
def set_background_title(d, title):
|
||||
"""Setting background title"""
|
||||
try:
|
||||
d.set_background_title(title)
|
||||
except:
|
||||
d.add_persistent_args(("--backtitle", title))
|
||||
|
||||
|
||||
def scrollbox(d, text, height=None, width=None):
|
||||
"""Setting scroll box"""
|
||||
try:
|
||||
d.scrollbox(text, height, width, exit_label = "Continue")
|
||||
d.scrollbox(text, height, width, exit_label="Continue")
|
||||
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():
|
||||
global inventorydata
|
||||
"""Reset the Inventory Lookups"""
|
||||
global inventorydata # pylint: disable=global-statement
|
||||
inventorydata = Inventory().numberOfInventoryLookupsPerformed
|
||||
Inventory().numberOfInventoryLookupsPerformed = 0
|
||||
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, 40, "From", curses.A_BOLD)
|
||||
stdscr.addstr(3, 80, "Subject", curses.A_BOLD)
|
||||
stdscr.addstr(3, 120, "Time Received", curses.A_BOLD)
|
||||
stdscr.hline(4, 5, '-', 121)
|
||||
for i, item in enumerate(inbox[max(min(len(inbox)-curses.LINES+6, inboxcur-5), 0):]):
|
||||
if 6+i < curses.LINES:
|
||||
for i, item in enumerate(inbox[max(min(len(inbox) - curses.LINES + 6, inboxcur - 5), 0):]):
|
||||
if 6 + i < curses.LINES:
|
||||
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
|
||||
if item[7] == False: # If not read, highlight
|
||||
if item[7] is False: # If not read, highlight
|
||||
a = a | curses.A_BOLD
|
||||
stdscr.addstr(5+i, 5, item[1][:34], a)
|
||||
stdscr.addstr(5+i, 40, item[3][:39], a)
|
||||
stdscr.addstr(5+i, 80, item[5][:39], a)
|
||||
stdscr.addstr(5+i, 120, item[6][:39], a)
|
||||
elif menutab == 3: # Sent
|
||||
stdscr.addstr(5 + i, 5, item[1][:34], a)
|
||||
stdscr.addstr(5 + i, 40, item[3][:39], a)
|
||||
stdscr.addstr(5 + i, 80, item[5][:39], a)
|
||||
stdscr.addstr(5 + i, 120, item[6][:39], a)
|
||||
elif menutab == 3: # Sent
|
||||
stdscr.addstr(3, 5, "To", curses.A_BOLD)
|
||||
stdscr.addstr(3, 40, "From", curses.A_BOLD)
|
||||
stdscr.addstr(3, 80, "Subject", curses.A_BOLD)
|
||||
stdscr.addstr(3, 120, "Status", curses.A_BOLD)
|
||||
stdscr.hline(4, 5, '-', 121)
|
||||
for i, item in enumerate(sentbox[max(min(len(sentbox)-curses.LINES+6, sentcur-5), 0):]):
|
||||
if 6+i < curses.LINES:
|
||||
for i, item in enumerate(sentbox[max(min(len(sentbox) - curses.LINES + 6, sentcur - 5), 0):]):
|
||||
if 6 + i < curses.LINES:
|
||||
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
|
||||
stdscr.addstr(5+i, 5, item[0][:34], a)
|
||||
stdscr.addstr(5+i, 40, item[2][:39], a)
|
||||
stdscr.addstr(5+i, 80, item[4][:39], a)
|
||||
stdscr.addstr(5+i, 120, item[5][:39], a)
|
||||
elif menutab == 2 or menutab == 4: # Send or Identities
|
||||
stdscr.addstr(5 + i, 5, item[0][:34], a)
|
||||
stdscr.addstr(5 + i, 40, item[2][:39], a)
|
||||
stdscr.addstr(5 + i, 80, item[4][:39], a)
|
||||
stdscr.addstr(5 + i, 120, item[5][:39], a)
|
||||
elif menutab == 2 or menutab == 4: # Send or Identities
|
||||
stdscr.addstr(3, 5, "Label", curses.A_BOLD)
|
||||
stdscr.addstr(3, 40, "Address", curses.A_BOLD)
|
||||
stdscr.addstr(3, 80, "Stream", curses.A_BOLD)
|
||||
stdscr.hline(4, 5, '-', 81)
|
||||
for i, item in enumerate(addresses[max(min(len(addresses)-curses.LINES+6, addrcur-5), 0):]):
|
||||
if 6+i < curses.LINES:
|
||||
for i, item in enumerate(addresses[max(min(len(addresses) - curses.LINES + 6, addrcur - 5), 0):]):
|
||||
if 6 + i < curses.LINES:
|
||||
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
|
||||
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
|
||||
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, 80, str(1)[:39], a)
|
||||
elif menutab == 5: # Subscriptions
|
||||
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, 80, str(1)[:39], a)
|
||||
elif menutab == 5: # Subscriptions
|
||||
stdscr.addstr(3, 5, "Label", curses.A_BOLD)
|
||||
stdscr.addstr(3, 80, "Address", curses.A_BOLD)
|
||||
stdscr.addstr(3, 120, "Enabled", curses.A_BOLD)
|
||||
stdscr.hline(4, 5, '-', 121)
|
||||
for i, item in enumerate(subscriptions[max(min(len(subscriptions)-curses.LINES+6, subcur-5), 0):]):
|
||||
if 6+i < curses.LINES:
|
||||
for i, item in enumerate(subscriptions[max(min(len(subscriptions) - curses.LINES + 6, subcur - 5), 0):]):
|
||||
if 6 + i < curses.LINES:
|
||||
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
|
||||
if item[2] == True: # Embolden enabled subscriptions
|
||||
if item[2]: # Embolden enabled subscriptions
|
||||
a = a | curses.A_BOLD
|
||||
stdscr.addstr(5+i, 5, item[0][:74], a)
|
||||
stdscr.addstr(5+i, 80, item[1][:39], a)
|
||||
stdscr.addstr(5+i, 120, str(item[2]), a)
|
||||
elif menutab == 6: # Address book
|
||||
stdscr.addstr(5 + i, 5, item[0][:74], a)
|
||||
stdscr.addstr(5 + i, 80, item[1][:39], a)
|
||||
stdscr.addstr(5 + i, 120, str(item[2]), a)
|
||||
elif menutab == 6: # Address book
|
||||
stdscr.addstr(3, 5, "Label", curses.A_BOLD)
|
||||
stdscr.addstr(3, 40, "Address", curses.A_BOLD)
|
||||
stdscr.hline(4, 5, '-', 41)
|
||||
for i, item in enumerate(addrbook[max(min(len(addrbook)-curses.LINES+6, abookcur-5), 0):]):
|
||||
if 6+i < curses.LINES:
|
||||
for i, item in enumerate(addrbook[max(min(len(addrbook) - curses.LINES + 6, abookcur - 5), 0):]):
|
||||
if 6 + i < curses.LINES:
|
||||
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
|
||||
stdscr.addstr(5+i, 5, item[0][:34], a)
|
||||
stdscr.addstr(5+i, 40, item[1][:39], a)
|
||||
elif menutab == 7: # Blacklist
|
||||
stdscr.addstr(3, 5, "Type: "+bwtype)
|
||||
stdscr.addstr(5 + i, 5, item[0][:34], a)
|
||||
stdscr.addstr(5 + i, 40, item[1][:39], a)
|
||||
elif menutab == 7: # Blacklist
|
||||
stdscr.addstr(3, 5, "Type: " + bwtype)
|
||||
stdscr.addstr(4, 5, "Label", curses.A_BOLD)
|
||||
stdscr.addstr(4, 80, "Address", curses.A_BOLD)
|
||||
stdscr.addstr(4, 120, "Enabled", curses.A_BOLD)
|
||||
stdscr.hline(5, 5, '-', 121)
|
||||
for i, item in enumerate(blacklist[max(min(len(blacklist)-curses.LINES+6, blackcur-5), 0):]):
|
||||
if 7+i < curses.LINES:
|
||||
for i, item in enumerate(blacklist[max(min(len(blacklist) - curses.LINES + 6, blackcur - 5), 0):]):
|
||||
if 7 + i < curses.LINES:
|
||||
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
|
||||
if item[2] == True: # Embolden enabled subscriptions
|
||||
if item[2]: # Embolden enabled subscriptions
|
||||
a = a | curses.A_BOLD
|
||||
stdscr.addstr(6+i, 5, item[0][:74], a)
|
||||
stdscr.addstr(6+i, 80, item[1][:39], a)
|
||||
stdscr.addstr(6+i, 120, str(item[2]), a)
|
||||
elif menutab == 8: # Network status
|
||||
stdscr.addstr(6 + i, 5, item[0][:74], a)
|
||||
stdscr.addstr(6 + i, 80, item[1][:39], a)
|
||||
stdscr.addstr(6 + i, 120, str(item[2]), a)
|
||||
elif menutab == 8: # Network status
|
||||
# Connection data
|
||||
connected_hosts = network.stats.connectedHostsList()
|
||||
stdscr.addstr(
|
||||
|
@ -228,51 +261,63 @@ def drawtab(stdscr):
|
|||
for i, item in enumerate(streamcount):
|
||||
if i < 4:
|
||||
if i == 0:
|
||||
stdscr.addstr(8+i, 6, "?")
|
||||
stdscr.addstr(8 + i, 6, "?")
|
||||
else:
|
||||
stdscr.addstr(8+i, 6, str(i))
|
||||
stdscr.addstr(8+i, 18, str(item).ljust(2))
|
||||
|
||||
stdscr.addstr(8 + i, 6, str(i))
|
||||
stdscr.addstr(8 + i, 18, str(item).ljust(2))
|
||||
|
||||
# Uptime and processing data
|
||||
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(8, 40, "Processed "+str(shared.numberOfBroadcastsProcessed).ljust(4)+" broadcast messages.")
|
||||
stdscr.addstr(9, 40, "Processed "+str(shared.numberOfPubkeysProcessed).ljust(4)+" public keys.")
|
||||
|
||||
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(8, 40, "Processed " + str(
|
||||
shared.numberOfBroadcastsProcessed).ljust(4) + " broadcast messages.")
|
||||
stdscr.addstr(9, 40, "Processed " + str(
|
||||
shared.numberOfPubkeysProcessed).ljust(4) + " public keys.")
|
||||
|
||||
# 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
|
||||
stdscr.addstr(13, 6, "Log", curses.A_BOLD)
|
||||
n = log.count('\n')
|
||||
if n > 0:
|
||||
l = log.split('\n')
|
||||
if n > 512:
|
||||
del l[:(n-256)]
|
||||
del l[:(n - 256)]
|
||||
logpad.erase()
|
||||
n = len(l)
|
||||
for i, item in enumerate(l):
|
||||
a = 0
|
||||
if len(item) > 0 and item[0] == '!':
|
||||
if item and item[0] == '!':
|
||||
a = curses.color_pair(1)
|
||||
item = item[1:]
|
||||
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()
|
||||
|
||||
|
||||
def redraw(stdscr):
|
||||
"""Redraw menu"""
|
||||
stdscr.erase()
|
||||
stdscr.border()
|
||||
drawmenu(stdscr)
|
||||
stdscr.refresh()
|
||||
|
||||
|
||||
def dialogreset(stdscr):
|
||||
"""Resetting dialogue"""
|
||||
stdscr.clear()
|
||||
stdscr.keypad(1)
|
||||
curses.curs_set(0)
|
||||
|
||||
|
||||
# pylint: disable=too-many-branches, too-many-statements
|
||||
def handlech(c, stdscr):
|
||||
# pylint: disable=redefined-outer-name, too-many-nested-blocks, too-many-locals, global-statement
|
||||
if c != curses.ERR:
|
||||
global inboxcur, addrcur, sentcur, subcur, abookcur, blackcur
|
||||
if c in range(256):
|
||||
if c in range(256):
|
||||
if chr(c) in '12345678':
|
||||
global menutab
|
||||
menutab = int(chr(c))
|
||||
|
@ -284,17 +329,27 @@ def handlech(c, stdscr):
|
|||
d = Dialog(dialog="dialog")
|
||||
if menutab == 1:
|
||||
set_background_title(d, "Inbox Message Dialog Box")
|
||||
r, t = d.menu("Do what with \""+inbox[inboxcur][5]+"\" from \""+inbox[inboxcur][3]+"\"?",
|
||||
choices=[("1", "View message"),
|
||||
r, t = d.menu(
|
||||
"Do what with \"" + inbox[inboxcur][5] + "\" from \"" + inbox[inboxcur][3] + "\"?",
|
||||
choices=[
|
||||
("1", "View message"),
|
||||
("2", "Mark message as unread"),
|
||||
("3", "Reply"),
|
||||
("4", "Add sender to Address Book"),
|
||||
("5", "Save message as text file"),
|
||||
("6", "Move to trash")])
|
||||
if r == d.DIALOG_OK:
|
||||
if t == "1": # View
|
||||
set_background_title(d, "\""+inbox[inboxcur][5]+"\" from \""+inbox[inboxcur][3]+"\" to \""+inbox[inboxcur][1]+"\"")
|
||||
data = ""
|
||||
if t == "1": # View
|
||||
set_background_title(
|
||||
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])
|
||||
if ret != []:
|
||||
for row in ret:
|
||||
|
@ -302,16 +357,16 @@ def handlech(c, stdscr):
|
|||
data = shared.fixPotentiallyInvalidUTF8Data(data)
|
||||
msg = ""
|
||||
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)
|
||||
sqlExecute("UPDATE inbox SET read=1 WHERE msgid=?", inbox[inboxcur][0])
|
||||
inbox[inboxcur][7] = 1
|
||||
else:
|
||||
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])
|
||||
inbox[inboxcur][7] = 0
|
||||
elif t == "3": # Reply
|
||||
elif t == "3": # Reply
|
||||
curses.curs_set(1)
|
||||
m = inbox[inboxcur]
|
||||
fromaddr = m[4]
|
||||
|
@ -320,29 +375,31 @@ def handlech(c, stdscr):
|
|||
if fromaddr == item[2] and item[3] != 0:
|
||||
ischan = True
|
||||
break
|
||||
if not addresses[i][1]:
|
||||
scrollbox(d, unicode("Sending address disabled, please either enable it or choose a different address."))
|
||||
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."))
|
||||
return
|
||||
toaddr = m[2]
|
||||
if ischan:
|
||||
toaddr = fromaddr
|
||||
|
||||
|
||||
subject = m[5]
|
||||
if not m[5][:4] == "Re: ":
|
||||
subject = "Re: "+m[5]
|
||||
subject = "Re: " + m[5]
|
||||
body = ""
|
||||
ret = sqlQuery("SELECT message FROM inbox WHERE msgid=?", m[0])
|
||||
if ret != []:
|
||||
body = "\n\n------------------------------------------------------\n"
|
||||
for row in ret:
|
||||
body, = row
|
||||
|
||||
|
||||
sendMessage(fromaddr, toaddr, ischan, subject, body, True)
|
||||
dialogreset(stdscr)
|
||||
elif t == "4": # Add to Address Book
|
||||
elif t == "4": # Add to Address Book
|
||||
addr = inbox[inboxcur][4]
|
||||
if addr not in [item[1] for i,item in enumerate(addrbook)]:
|
||||
r, t = d.inputbox("Label for address \""+addr+"\"")
|
||||
if addr not in [item[1] for i, item in enumerate(addrbook)]:
|
||||
r, t = d.inputbox("Label for address \"" + addr + "\"")
|
||||
if r == d.DIALOG_OK:
|
||||
label = t
|
||||
sqlExecute("INSERT INTO addressbook VALUES (?,?)", label, addr)
|
||||
|
@ -352,61 +409,85 @@ def handlech(c, stdscr):
|
|||
addrbook.reverse()
|
||||
else:
|
||||
scrollbox(d, unicode("The selected address is already in the Address Book."))
|
||||
elif t == "5": # Save message
|
||||
set_background_title(d, "Save \""+inbox[inboxcur][5]+"\" as text file")
|
||||
r, t = d.inputbox("Filename", init=inbox[inboxcur][5]+".txt")
|
||||
elif t == "5": # Save message
|
||||
set_background_title(d, "Save \"" + inbox[inboxcur][5] + "\" as text file")
|
||||
r, t = d.inputbox("Filename", init=inbox[inboxcur][5] + ".txt")
|
||||
if r == d.DIALOG_OK:
|
||||
msg = ""
|
||||
ret = sqlQuery("SELECT message FROM inbox WHERE msgid=?", inbox[inboxcur][0])
|
||||
if ret != []:
|
||||
for row in ret:
|
||||
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.close()
|
||||
else:
|
||||
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])
|
||||
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:
|
||||
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]
|
||||
sendMessage(addresses[addrcur][2], a)
|
||||
elif menutab == 3:
|
||||
set_background_title(d, "Sent Messages Dialog Box")
|
||||
r, t = d.menu("Do what with \""+sentbox[sentcur][4]+"\" to \""+sentbox[sentcur][0]+"\"?",
|
||||
choices=[("1", "View message"),
|
||||
r, t = d.menu(
|
||||
"Do what with \"" + sentbox[sentcur][4] + "\" to \"" + sentbox[sentcur][0] + "\"?",
|
||||
choices=[
|
||||
("1", "View message"),
|
||||
("2", "Move to trash")])
|
||||
if r == d.DIALOG_OK:
|
||||
if t == "1": # View
|
||||
set_background_title(d, "\""+sentbox[sentcur][4]+"\" from \""+sentbox[sentcur][3]+"\" to \""+sentbox[sentcur][1]+"\"")
|
||||
if t == "1": # View
|
||||
set_background_title(
|
||||
d,
|
||||
"\"" +
|
||||
sentbox[sentcur][4] +
|
||||
"\" from \"" +
|
||||
sentbox[sentcur][3] +
|
||||
"\" to \"" +
|
||||
sentbox[sentcur][1] +
|
||||
"\"")
|
||||
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 != []:
|
||||
for row in ret:
|
||||
data, = row
|
||||
data = shared.fixPotentiallyInvalidUTF8Data(data)
|
||||
msg = ""
|
||||
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)
|
||||
else:
|
||||
scrollbox(d, unicode("Could not fetch message."))
|
||||
elif t == "2": # Move to trash
|
||||
sqlExecute("UPDATE sent SET folder='trash' WHERE subject=? AND ackdata=?", sentbox[sentcur][4], sentbox[sentcur][6])
|
||||
elif t == "2": # Move to trash
|
||||
sqlExecute(
|
||||
"UPDATE sent SET folder='trash' WHERE subject=? AND ackdata=?",
|
||||
sentbox[sentcur][4],
|
||||
sentbox[sentcur][6])
|
||||
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:
|
||||
set_background_title(d, "Your Identities Dialog Box")
|
||||
if len(addresses) <= addrcur:
|
||||
r, t = d.menu("Do what with addresses?",
|
||||
choices=[("1", "Create new address")])
|
||||
r, t = d.menu(
|
||||
"Do what with addresses?",
|
||||
choices=[
|
||||
("1", "Create new address")])
|
||||
else:
|
||||
r, t = d.menu("Do what with \""+addresses[addrcur][0]+"\" : \""+addresses[addrcur][2]+"\"?",
|
||||
choices=[("1", "Create new address"),
|
||||
r, t = d.menu(
|
||||
"Do what with \"" + addresses[addrcur][0] + "\" : \"" + addresses[addrcur][2] + "\"?",
|
||||
choices=[
|
||||
("1", "Create new address"),
|
||||
("2", "Send a message from this address"),
|
||||
("3", "Rename"),
|
||||
("4", "Enable"),
|
||||
|
@ -414,31 +495,41 @@ def handlech(c, stdscr):
|
|||
("6", "Delete"),
|
||||
("7", "Special address behavior")])
|
||||
if r == d.DIALOG_OK:
|
||||
if t == "1": # Create new address
|
||||
if t == "1": # Create new address
|
||||
set_background_title(d, "Create new address")
|
||||
scrollbox(d, unicode("Here you may generate as many addresses as you like.\n"
|
||||
"Indeed, creating and abandoning addresses is encouraged.\n"
|
||||
"Deterministic addresses have several pros and cons:\n"
|
||||
"\nPros:\n"
|
||||
" * You can recreate your addresses on any computer from memory\n"
|
||||
" * You need not worry about backing up your keys.dat file as long as you \n can remember your passphrase\n"
|
||||
"Cons:\n"
|
||||
" * You must remember (or write down) your passphrase in order to recreate \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"),
|
||||
scrollbox(
|
||||
d, unicode(
|
||||
"Here you may generate as many addresses as you like.\n"
|
||||
"Indeed, creating and abandoning addresses is encouraged.\n"
|
||||
"Deterministic addresses have several pros and cons:\n"
|
||||
"\nPros:\n"
|
||||
" * You can recreate your addresses on any computer from memory\n"
|
||||
" * You need not worry about backing up your keys.dat file as long as you"
|
||||
" \n can remember your passphrase\n"
|
||||
"Cons:\n"
|
||||
" * You must remember (or write down) your passphrase in order to recreate"
|
||||
" \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")])
|
||||
if r == d.DIALOG_OK:
|
||||
if t == "1":
|
||||
set_background_title(d, "Randomly generate address")
|
||||
r, t = d.inputbox("Label (not shown to anyone except you)")
|
||||
label = ""
|
||||
if r == d.DIALOG_OK and len(t) > 0:
|
||||
if r == d.DIALOG_OK and t:
|
||||
label = t
|
||||
r, t = d.menu("Choose a stream",
|
||||
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)")])
|
||||
r, t = d.menu(
|
||||
"Choose a stream",
|
||||
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 t == "1":
|
||||
stream = 1
|
||||
|
@ -450,42 +541,69 @@ def handlech(c, stdscr):
|
|||
if r == d.DIALOG_OK:
|
||||
stream = decodeAddress(addrs[int(t)][1])[2]
|
||||
shorten = False
|
||||
r, t = d.checklist("Miscellaneous options",
|
||||
choices=[("1", "Spend time shortening the address", 1 if shorten else 0)])
|
||||
r, t = d.checklist(
|
||||
"Miscellaneous options",
|
||||
choices=[(
|
||||
"1",
|
||||
"Spend time shortening the address",
|
||||
1 if shorten else 0)])
|
||||
if r == d.DIALOG_OK and "1" in t:
|
||||
shorten = True
|
||||
queues.addressGeneratorQueue.put(("createRandomAddress", 4, stream, label, 1, "", shorten))
|
||||
queues.addressGeneratorQueue.put((
|
||||
"createRandomAddress",
|
||||
4,
|
||||
stream,
|
||||
label,
|
||||
1,
|
||||
"",
|
||||
shorten))
|
||||
elif t == "2":
|
||||
set_background_title(d, "Make deterministic addresses")
|
||||
r, t = d.passwordform("Enter passphrase",
|
||||
[("Passphrase", 1, 1, "", 2, 1, 64, 128),
|
||||
("Confirm passphrase", 3, 1, "", 4, 1, 64, 128)],
|
||||
r, t = d.passwordform(
|
||||
"Enter passphrase",
|
||||
[
|
||||
("Passphrase", 1, 1, "", 2, 1, 64, 128),
|
||||
("Confirm passphrase", 3, 1, "", 4, 1, 64, 128)],
|
||||
form_height=4, insecure=True)
|
||||
if r == d.DIALOG_OK:
|
||||
if t[0] == t[1]:
|
||||
passphrase = t[0]
|
||||
r, t = d.rangebox("Number of addresses to generate",
|
||||
width=48, min=1, max=99, init=8)
|
||||
r, t = d.rangebox(
|
||||
"Number of addresses to generate",
|
||||
width=48,
|
||||
min=1,
|
||||
max=99,
|
||||
init=8)
|
||||
if r == d.DIALOG_OK:
|
||||
number = t
|
||||
stream = 1
|
||||
shorten = False
|
||||
r, t = d.checklist("Miscellaneous options",
|
||||
choices=[("1", "Spend time shortening the address", 1 if shorten else 0)])
|
||||
r, t = d.checklist(
|
||||
"Miscellaneous options",
|
||||
choices=[(
|
||||
"1",
|
||||
"Spend time shortening the address",
|
||||
1 if shorten else 0)])
|
||||
if r == d.DIALOG_OK and "1" in t:
|
||||
shorten = True
|
||||
scrollbox(d, unicode("In addition to your passphrase, be sure to remember the 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))
|
||||
scrollbox(
|
||||
d, unicode(
|
||||
"In addition to your passphrase, be sure to remember the"
|
||||
" 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:
|
||||
scrollbox(d, unicode("Passphrases do not match"))
|
||||
elif t == "2": # Send a message
|
||||
elif t == "2": # Send a message
|
||||
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]
|
||||
sendMessage(addresses[addrcur][2], a)
|
||||
elif t == "3": # Rename address label
|
||||
elif t == "3": # Rename address label
|
||||
a = addresses[addrcur][2]
|
||||
label = addresses[addrcur][0]
|
||||
r, t = d.inputbox("New address label", init=label)
|
||||
|
@ -495,72 +613,79 @@ def handlech(c, stdscr):
|
|||
# Write config
|
||||
BMConfigParser().save()
|
||||
addresses[addrcur][0] = label
|
||||
elif t == "4": # Enable address
|
||||
elif t == "4": # Enable address
|
||||
a = addresses[addrcur][2]
|
||||
BMConfigParser().set(a, "enabled", "true") # Set config
|
||||
BMConfigParser().set(a, "enabled", "true") # Set config
|
||||
# Write config
|
||||
BMConfigParser().save()
|
||||
# Change color
|
||||
if BMConfigParser().safeGetBoolean(a, 'chan'):
|
||||
addresses[addrcur][3] = 9 # orange
|
||||
addresses[addrcur][3] = 9 # orange
|
||||
elif BMConfigParser().safeGetBoolean(a, 'mailinglist'):
|
||||
addresses[addrcur][3] = 5 # magenta
|
||||
addresses[addrcur][3] = 5 # magenta
|
||||
else:
|
||||
addresses[addrcur][3] = 0 # black
|
||||
addresses[addrcur][3] = 0 # black
|
||||
addresses[addrcur][1] = True
|
||||
shared.reloadMyAddressHashes() # Reload address hashes
|
||||
elif t == "5": # Disable address
|
||||
shared.reloadMyAddressHashes() # Reload address hashes
|
||||
elif t == "5": # Disable address
|
||||
a = addresses[addrcur][2]
|
||||
BMConfigParser().set(a, "enabled", "false") # Set config
|
||||
addresses[addrcur][3] = 8 # Set color to gray
|
||||
BMConfigParser().set(a, "enabled", "false") # Set config
|
||||
addresses[addrcur][3] = 8 # Set color to gray
|
||||
# Write config
|
||||
BMConfigParser().save()
|
||||
addresses[addrcur][1] = False
|
||||
shared.reloadMyAddressHashes() # Reload address hashes
|
||||
elif t == "6": # Delete address
|
||||
shared.reloadMyAddressHashes() # Reload address hashes
|
||||
elif t == "6": # Delete address
|
||||
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":
|
||||
BMConfigParser().remove_section(addresses[addrcur][2])
|
||||
BMConfigParser().save()
|
||||
del addresses[addrcur]
|
||||
elif t == "7": # Special address behavior
|
||||
BMConfigParser().remove_section(addresses[addrcur][2])
|
||||
BMConfigParser().save()
|
||||
del addresses[addrcur]
|
||||
elif t == "7": # Special address behavior
|
||||
a = addresses[addrcur][2]
|
||||
set_background_title(d, "Special address behavior")
|
||||
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:
|
||||
m = BMConfigParser().safeGetBoolean(a, "mailinglist")
|
||||
r, t = d.radiolist("Select address behavior",
|
||||
choices=[("1", "Behave as a normal address", not m),
|
||||
r, t = d.radiolist(
|
||||
"Select address behavior",
|
||||
choices=[
|
||||
("1", "Behave as a normal address", not m),
|
||||
("2", "Behave as a pseudo-mailing-list address", m)])
|
||||
if r == d.DIALOG_OK:
|
||||
if t == "1" and m == True:
|
||||
if t == "1" and m:
|
||||
BMConfigParser().set(a, "mailinglist", "false")
|
||||
if addresses[addrcur][1]:
|
||||
addresses[addrcur][3] = 0 # Set color to black
|
||||
addresses[addrcur][3] = 0 # Set color to black
|
||||
else:
|
||||
addresses[addrcur][3] = 8 # Set color to gray
|
||||
elif t == "2" and m == False:
|
||||
addresses[addrcur][3] = 8 # Set color to gray
|
||||
elif t == "2" and m is False:
|
||||
try:
|
||||
mn = BMConfigParser().get(a, "mailinglistname")
|
||||
except ConfigParser.NoOptionError:
|
||||
mn = ""
|
||||
mn = ""
|
||||
r, t = d.inputbox("Mailing list name", init=mn)
|
||||
if r == d.DIALOG_OK:
|
||||
mn = t
|
||||
BMConfigParser().set(a, "mailinglist", "true")
|
||||
BMConfigParser().set(a, "mailinglistname", mn)
|
||||
addresses[addrcur][3] = 6 # Set color to magenta
|
||||
addresses[addrcur][3] = 6 # Set color to magenta
|
||||
# Write config
|
||||
BMConfigParser().save()
|
||||
elif menutab == 5:
|
||||
set_background_title(d, "Subscriptions Dialog Box")
|
||||
if len(subscriptions) <= subcur:
|
||||
r, t = d.menu("Do what with subscription to \""+subscriptions[subcur][0]+"\"?",
|
||||
choices=[("1", "Add new subscription")])
|
||||
r, t = d.menu(
|
||||
"Do what with subscription to \"" + subscriptions[subcur][0] + "\"?",
|
||||
choices=[
|
||||
("1", "Add new subscription")])
|
||||
else:
|
||||
r, t = d.menu("Do what with subscription to \""+subscriptions[subcur][0]+"\"?",
|
||||
choices=[("1", "Add new subscription"),
|
||||
r, t = d.menu(
|
||||
"Do what with subscription to \"" + subscriptions[subcur][0] + "\"?",
|
||||
choices=[
|
||||
("1", "Add new subscription"),
|
||||
("2", "Delete this subscription"),
|
||||
("3", "Enable"),
|
||||
("4", "Disable")])
|
||||
|
@ -581,27 +706,39 @@ def handlech(c, stdscr):
|
|||
sqlExecute("INSERT INTO subscriptions VALUES (?,?,?)", label, addr, True)
|
||||
shared.reloadBroadcastSendersForWhichImWatching()
|
||||
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":
|
||||
sqlExecute("DELETE FROM subscriptions WHERE label=? AND address=?", subscriptions[subcur][0], subscriptions[subcur][1])
|
||||
shared.reloadBroadcastSendersForWhichImWatching()
|
||||
del subscriptions[subcur]
|
||||
sqlExecute(
|
||||
"DELETE FROM subscriptions WHERE label=? AND address=?",
|
||||
subscriptions[subcur][0],
|
||||
subscriptions[subcur][1])
|
||||
shared.reloadBroadcastSendersForWhichImWatching()
|
||||
del subscriptions[subcur]
|
||||
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()
|
||||
subscriptions[subcur][2] = True
|
||||
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()
|
||||
subscriptions[subcur][2] = False
|
||||
elif menutab == 6:
|
||||
set_background_title(d, "Address Book Dialog Box")
|
||||
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")])
|
||||
else:
|
||||
r, t = d.menu("Do what with \""+addrbook[abookcur][0]+"\" : \""+addrbook[abookcur][1]+"\"",
|
||||
choices=[("1", "Send a message to this address"),
|
||||
r, t = d.menu(
|
||||
"Do what with \"" + addrbook[abookcur][0] + "\" : \"" + addrbook[abookcur][1] + "\"",
|
||||
choices=[
|
||||
("1", "Send a message to this address"),
|
||||
("2", "Subscribe to this address"),
|
||||
("3", "Add new address to Address Book"),
|
||||
("4", "Delete this address")])
|
||||
|
@ -623,8 +760,8 @@ def handlech(c, stdscr):
|
|||
r, t = d.inputbox("Input new address")
|
||||
if r == d.DIALOG_OK:
|
||||
addr = t
|
||||
if addr not in [item[1] for i,item in enumerate(addrbook)]:
|
||||
r, t = d.inputbox("Label for address \""+addr+"\"")
|
||||
if addr not in [item[1] for i, item in enumerate(addrbook)]:
|
||||
r, t = d.inputbox("Label for address \"" + addr + "\"")
|
||||
if r == d.DIALOG_OK:
|
||||
sqlExecute("INSERT INTO addressbook VALUES (?,?)", t, addr)
|
||||
# Prepend entry
|
||||
|
@ -636,25 +773,39 @@ def handlech(c, stdscr):
|
|||
elif t == "4":
|
||||
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":
|
||||
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]
|
||||
elif menutab == 7:
|
||||
set_background_title(d, "Blacklist Dialog Box")
|
||||
r, t = d.menu("Do what with \""+blacklist[blackcur][0]+"\" : \""+blacklist[blackcur][1]+"\"?",
|
||||
choices=[("1", "Delete"),
|
||||
r, t = d.menu(
|
||||
"Do what with \"" + blacklist[blackcur][0] + "\" : \"" + blacklist[blackcur][1] + "\"?",
|
||||
choices=[
|
||||
("1", "Delete"),
|
||||
("2", "Enable"),
|
||||
("3", "Disable")])
|
||||
if r == d.DIALOG_OK:
|
||||
if t == "1":
|
||||
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":
|
||||
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]
|
||||
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
|
||||
elif t== "3":
|
||||
sqlExecute("UPDATE blacklist SET enabled=0 WHERE label=? AND address=?", blacklist[blackcur][0], blacklist[blackcur][1])
|
||||
elif t == "3":
|
||||
sqlExecute(
|
||||
"UPDATE blacklist SET enabled=0 WHERE label=? AND address=?",
|
||||
blacklist[blackcur][0],
|
||||
blacklist[blackcur][1])
|
||||
blacklist[blackcur][2] = False
|
||||
dialogreset(stdscr)
|
||||
else:
|
||||
|
@ -672,17 +823,17 @@ def handlech(c, stdscr):
|
|||
if menutab == 7 and blackcur > 0:
|
||||
blackcur -= 1
|
||||
elif c == curses.KEY_DOWN:
|
||||
if menutab == 1 and inboxcur < len(inbox)-1:
|
||||
if menutab == 1 and inboxcur < len(inbox) - 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
|
||||
if menutab == 3 and sentcur < len(sentbox)-1:
|
||||
if menutab == 3 and sentcur < len(sentbox) - 1:
|
||||
sentcur += 1
|
||||
if menutab == 5 and subcur < len(subscriptions)-1:
|
||||
if menutab == 5 and subcur < len(subscriptions) - 1:
|
||||
subcur += 1
|
||||
if menutab == 6 and abookcur < len(addrbook)-1:
|
||||
if menutab == 6 and abookcur < len(addrbook) - 1:
|
||||
abookcur += 1
|
||||
if menutab == 7 and blackcur < len(blacklist)-1:
|
||||
if menutab == 7 and blackcur < len(blacklist) - 1:
|
||||
blackcur += 1
|
||||
elif c == curses.KEY_HOME:
|
||||
if menutab == 1:
|
||||
|
@ -699,38 +850,47 @@ def handlech(c, stdscr):
|
|||
blackcur = 0
|
||||
elif c == curses.KEY_END:
|
||||
if menutab == 1:
|
||||
inboxcur = len(inbox)-1
|
||||
inboxcur = len(inbox) - 1
|
||||
if menutab == 2 or menutab == 4:
|
||||
addrcur = len(addresses)-1
|
||||
addrcur = len(addresses) - 1
|
||||
if menutab == 3:
|
||||
sentcur = len(sentbox)-1
|
||||
sentcur = len(sentbox) - 1
|
||||
if menutab == 5:
|
||||
subcur = len(subscriptions)-1
|
||||
subcur = len(subscriptions) - 1
|
||||
if menutab == 6:
|
||||
abookcur = len(addrbook)-1
|
||||
abookcur = len(addrbook) - 1
|
||||
if menutab == 7:
|
||||
blackcur = len(blackcur)-1
|
||||
blackcur = len(blackcur) - 1
|
||||
redraw(stdscr)
|
||||
|
||||
|
||||
# pylint: disable=too-many-locals, too-many-arguments
|
||||
def sendMessage(sender="", recv="", broadcast=None, subject="", body="", reply=False):
|
||||
"""Method for message sending"""
|
||||
if sender == "":
|
||||
return
|
||||
d = Dialog(dialog="dialog")
|
||||
set_background_title(d, "Send a message")
|
||||
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:
|
||||
global menutab
|
||||
global menutab # pylint: disable=global-statement
|
||||
menutab = 6
|
||||
return
|
||||
recv = t
|
||||
if broadcast == None and sender != recv:
|
||||
r, t = d.radiolist("How to send the message?",
|
||||
choices=[("1", "Send to one or more specific people", 1),
|
||||
if broadcast is None and sender != recv:
|
||||
r, t = d.radiolist(
|
||||
"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)])
|
||||
if r != d.DIALOG_OK:
|
||||
return
|
||||
broadcast = False
|
||||
if t == "2": # Broadcast
|
||||
if t == "2": # Broadcast
|
||||
broadcast = True
|
||||
if subject == "" or reply:
|
||||
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 = []
|
||||
for i, item in enumerate(recv.replace(",", ";").split(";")):
|
||||
recvlist.append(item.strip())
|
||||
list(set(recvlist)) # Remove exact duplicates
|
||||
list(set(recvlist)) # Remove exact duplicates
|
||||
for addr in recvlist:
|
||||
if addr != "":
|
||||
# pylint: disable=redefined-outer-name
|
||||
status, version, stream, ripe = decodeAddress(addr)
|
||||
if status != "success":
|
||||
set_background_title(d, "Recipient address error")
|
||||
|
@ -762,13 +923,17 @@ def sendMessage(sender="", recv="", broadcast=None, subject="", body="", reply=F
|
|||
elif status == "invalidcharacters":
|
||||
err += "The address contains invalid characters."
|
||||
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":
|
||||
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":
|
||||
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":
|
||||
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:
|
||||
err += "It is unknown what is wrong with the address."
|
||||
scrollbox(d, unicode(err))
|
||||
|
@ -776,17 +941,24 @@ def sendMessage(sender="", recv="", broadcast=None, subject="", body="", reply=F
|
|||
addr = addBMIfNotPresent(addr)
|
||||
if version > 4 or version <= 1:
|
||||
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
|
||||
if stream > 1 or stream == 0:
|
||||
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
|
||||
if not network.stats.connectedHostsList():
|
||||
set_background_title(d, "Not connected warning")
|
||||
scrollbox(d, unicode("Because you are not currently connected to the network, "))
|
||||
stealthLevel = BMConfigParser().safeGetInt('bitmessagesettings', 'ackstealthlevel')
|
||||
ackdata = genAckPayload(streamNumber, stealthLevel)
|
||||
ackdata = genAckPayload(decodeAddress(addr)[2], stealthLevel)
|
||||
sqlExecute(
|
||||
"INSERT INTO sent VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
|
||||
"",
|
||||
|
@ -796,22 +968,22 @@ def sendMessage(sender="", recv="", broadcast=None, subject="", body="", reply=F
|
|||
subject,
|
||||
body,
|
||||
ackdata,
|
||||
int(time.time()), # sentTime (this will never change)
|
||||
int(time.time()), # lastActionTime
|
||||
0, # sleepTill time. This will get set when the POW gets done.
|
||||
int(time.time()), # sentTime (this will never change)
|
||||
int(time.time()), # lastActionTime
|
||||
0, # sleepTill time. This will get set when the POW gets done.
|
||||
"msgqueued",
|
||||
0, # retryNumber
|
||||
0, # retryNumber
|
||||
"sent",
|
||||
2, # encodingType
|
||||
2, # encodingType
|
||||
BMConfigParser().getint('bitmessagesettings', 'ttl'))
|
||||
queues.workerQueue.put(("sendmessage", addr))
|
||||
else: # Broadcast
|
||||
else: # Broadcast
|
||||
if recv == "":
|
||||
set_background_title(d, "Empty sender error")
|
||||
scrollbox(d, unicode("You must specify an address to send the message from."))
|
||||
else:
|
||||
# dummy ackdata, no need for stealth
|
||||
ackdata = genAckPayload(streamNumber, 0)
|
||||
ackdata = genAckPayload(decodeAddress(addr)[2], 0)
|
||||
recv = BROADCAST_STR
|
||||
ripe = ""
|
||||
sqlExecute(
|
||||
|
@ -823,21 +995,24 @@ def sendMessage(sender="", recv="", broadcast=None, subject="", body="", reply=F
|
|||
subject,
|
||||
body,
|
||||
ackdata,
|
||||
int(time.time()), # sentTime (this will never change)
|
||||
int(time.time()), # lastActionTime
|
||||
0, # sleepTill time. This will get set when the POW gets done.
|
||||
int(time.time()), # sentTime (this will never change)
|
||||
int(time.time()), # lastActionTime
|
||||
0, # sleepTill time. This will get set when the POW gets done.
|
||||
"broadcastqueued",
|
||||
0, # retryNumber
|
||||
"sent", # folder
|
||||
2, # encodingType
|
||||
0, # retryNumber
|
||||
"sent", # folder
|
||||
2, # encodingType
|
||||
BMConfigParser().getint('bitmessagesettings', 'ttl'))
|
||||
queues.workerQueue.put(('sendbroadcast', ''))
|
||||
|
||||
|
||||
# pylint: disable=redefined-outer-name, too-many-locals
|
||||
def loadInbox():
|
||||
"""Load the list of messages"""
|
||||
sys.stdout = sys.__stdout__
|
||||
print("Loading inbox messages...")
|
||||
print "Loading inbox messages..."
|
||||
sys.stdout = printlog
|
||||
|
||||
|
||||
where = "toaddress || fromaddress || subject || message"
|
||||
what = "%%"
|
||||
ret = sqlQuery("""SELECT msgid, toaddress, fromaddress, subject, received, read
|
||||
|
@ -847,7 +1022,7 @@ def loadInbox():
|
|||
for row in ret:
|
||||
msgid, toaddr, fromaddr, subject, received, read = row
|
||||
subject = ascii(shared.fixPotentiallyInvalidUTF8Data(subject))
|
||||
|
||||
|
||||
# Set label for to address
|
||||
try:
|
||||
if toaddr == BROADCAST_STR:
|
||||
|
@ -859,17 +1034,17 @@ def loadInbox():
|
|||
if tolabel == "":
|
||||
tolabel = toaddr
|
||||
tolabel = shared.fixPotentiallyInvalidUTF8Data(tolabel)
|
||||
|
||||
|
||||
# Set label for from address
|
||||
fromlabel = ""
|
||||
if BMConfigParser().has_section(fromaddr):
|
||||
fromlabel = BMConfigParser().get(fromaddr, "label")
|
||||
if fromlabel == "": # Check Address Book
|
||||
if fromlabel == "": # Check Address Book
|
||||
qr = sqlQuery("SELECT label FROM addressbook WHERE address=?", fromaddr)
|
||||
if qr != []:
|
||||
for r in qr:
|
||||
fromlabel, = r
|
||||
if fromlabel == "": # Check Subscriptions
|
||||
if fromlabel == "": # Check Subscriptions
|
||||
qr = sqlQuery("SELECT label FROM subscriptions WHERE address=?", fromaddr)
|
||||
if qr != []:
|
||||
for r in qr:
|
||||
|
@ -877,16 +1052,19 @@ def loadInbox():
|
|||
if fromlabel == "":
|
||||
fromlabel = fromaddr
|
||||
fromlabel = shared.fixPotentiallyInvalidUTF8Data(fromlabel)
|
||||
|
||||
|
||||
# Load into array
|
||||
inbox.append([msgid, tolabel, toaddr, fromlabel, fromaddr, subject,
|
||||
l10n.formatTimestamp(received, False), read])
|
||||
inbox.append([msgid, tolabel, toaddr, fromlabel, fromaddr, subject, l10n.formatTimestamp(
|
||||
received, False), read])
|
||||
inbox.reverse()
|
||||
|
||||
|
||||
def loadSent():
|
||||
"""Load the messages that sent"""
|
||||
sys.stdout = sys.__stdout__
|
||||
print("Loading sent messages...")
|
||||
print "Loading sent messages..."
|
||||
sys.stdout = printlog
|
||||
|
||||
|
||||
where = "toaddress || fromaddress || subject || message"
|
||||
what = "%%"
|
||||
ret = sqlQuery("""SELECT toaddress, fromaddress, subject, status, ackdata, lastactiontime
|
||||
|
@ -896,7 +1074,7 @@ def loadSent():
|
|||
for row in ret:
|
||||
toaddr, fromaddr, subject, status, ackdata, lastactiontime = row
|
||||
subject = ascii(shared.fixPotentiallyInvalidUTF8Data(subject))
|
||||
|
||||
|
||||
# Set label for to address
|
||||
tolabel = ""
|
||||
qr = sqlQuery("SELECT label FROM addressbook WHERE address=?", toaddr)
|
||||
|
@ -913,14 +1091,14 @@ def loadSent():
|
|||
tolabel = BMConfigParser().get(toaddr, "label")
|
||||
if tolabel == "":
|
||||
tolabel = toaddr
|
||||
|
||||
|
||||
# Set label for from address
|
||||
fromlabel = ""
|
||||
if BMConfigParser().has_section(fromaddr):
|
||||
fromlabel = BMConfigParser().get(fromaddr, "label")
|
||||
if fromlabel == "":
|
||||
fromlabel = fromaddr
|
||||
|
||||
|
||||
# Set status string
|
||||
if status == "awaitingpubkey":
|
||||
statstr = "Waiting for their public key. Will request it again soon"
|
||||
|
@ -930,20 +1108,20 @@ def loadSent():
|
|||
statstr = "Message queued"
|
||||
elif status == "msgsent":
|
||||
t = l10n.formatTimestamp(lastactiontime, False)
|
||||
statstr = "Message sent at "+t+".Waiting for acknowledgement."
|
||||
statstr = "Message sent at " + t + ".Waiting for acknowledgement."
|
||||
elif status == "msgsentnoackexpected":
|
||||
t = l10n.formatTimestamp(lastactiontime, False)
|
||||
statstr = "Message sent at "+t+"."
|
||||
statstr = "Message sent at " + t + "."
|
||||
elif status == "doingmsgpow":
|
||||
statstr = "The proof of work required to send the message has been queued."
|
||||
elif status == "ackreceived":
|
||||
t = l10n.formatTimestamp(lastactiontime, False)
|
||||
statstr = "Acknowledgment of the message received at "+t+"."
|
||||
statstr = "Acknowledgment of the message received at " + t + "."
|
||||
elif status == "broadcastqueued":
|
||||
statstr = "Broadcast queued."
|
||||
elif status == "broadcastsent":
|
||||
t = l10n.formatTimestamp(lastactiontime, False)
|
||||
statstr = "Broadcast sent at "+t+"."
|
||||
statstr = "Broadcast sent at " + t + "."
|
||||
elif status == "forcepow":
|
||||
statstr = "Forced difficulty override. Message will start sending soon."
|
||||
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."
|
||||
else:
|
||||
t = l10n.formatTimestamp(lastactiontime, False)
|
||||
statstr = "Unknown status "+status+" at "+t+"."
|
||||
|
||||
statstr = "Unknown status " + status + " at " + t + "."
|
||||
|
||||
# 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)])
|
||||
sentbox.reverse()
|
||||
|
||||
|
||||
def loadAddrBook():
|
||||
"""Load address book"""
|
||||
sys.stdout = sys.__stdout__
|
||||
print("Loading address book...")
|
||||
print "Loading address book..."
|
||||
sys.stdout = printlog
|
||||
|
||||
|
||||
ret = sqlQuery("SELECT label, address FROM addressbook")
|
||||
for row in ret:
|
||||
label, addr = row
|
||||
label = shared.fixPotentiallyInvalidUTF8Data(label)
|
||||
addrbook.append([label, addr])
|
||||
addrbook.reverse()
|
||||
|
||||
|
||||
def loadSubscriptions():
|
||||
"""Load subscription functionality"""
|
||||
ret = sqlQuery("SELECT label, address, enabled FROM subscriptions")
|
||||
for row in ret:
|
||||
label, address, enabled = row
|
||||
subscriptions.append([label, address, enabled])
|
||||
subscriptions.reverse()
|
||||
|
||||
|
||||
def loadBlackWhiteList():
|
||||
global bwtype
|
||||
"""load black/white list"""
|
||||
global bwtype # pylint: disable=global-statement
|
||||
bwtype = BMConfigParser().get("bitmessagesettings", "blackwhitelist")
|
||||
if bwtype == "black":
|
||||
ret = sqlQuery("SELECT label, address, enabled FROM blacklist")
|
||||
|
@ -987,51 +1181,53 @@ def loadBlackWhiteList():
|
|||
blacklist.append([label, address, enabled])
|
||||
blacklist.reverse()
|
||||
|
||||
|
||||
def runwrapper():
|
||||
sys.stdout = printlog
|
||||
#sys.stderr = errlog
|
||||
|
||||
# sys.stderr = errlog
|
||||
|
||||
# Load messages from database
|
||||
loadInbox()
|
||||
loadSent()
|
||||
loadAddrBook()
|
||||
loadSubscriptions()
|
||||
loadBlackWhiteList()
|
||||
|
||||
|
||||
stdscr = curses.initscr()
|
||||
|
||||
global logpad
|
||||
|
||||
global logpad # pylint: disable=global-statement
|
||||
logpad = curses.newpad(1024, curses.COLS)
|
||||
|
||||
|
||||
stdscr.nodelay(0)
|
||||
curses.curs_set(0)
|
||||
stdscr.timeout(1000)
|
||||
|
||||
|
||||
curses.wrapper(run)
|
||||
doShutdown()
|
||||
|
||||
|
||||
def run(stdscr):
|
||||
# Schedule inventory lookup data
|
||||
resetlookups()
|
||||
|
||||
|
||||
# Init color pairs
|
||||
if curses.has_colors():
|
||||
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(3, curses.COLOR_YELLOW, curses.COLOR_BLACK) # yellow
|
||||
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(6, curses.COLOR_CYAN, curses.COLOR_BLACK) # cyan
|
||||
curses.init_pair(7, curses.COLOR_WHITE, curses.COLOR_BLACK) # white
|
||||
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(3, curses.COLOR_YELLOW, curses.COLOR_BLACK) # yellow
|
||||
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(6, curses.COLOR_CYAN, curses.COLOR_BLACK) # cyan
|
||||
curses.init_pair(7, curses.COLOR_WHITE, curses.COLOR_BLACK) # white
|
||||
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_color(9, 844, 465, 0) # orange
|
||||
curses.init_color(9, 844, 465, 0) # orange
|
||||
curses.init_pair(9, 9, 0)
|
||||
else:
|
||||
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(8, curses.COLOR_WHITE, curses.COLOR_BLACK) # grayish
|
||||
curses.init_pair(9, curses.COLOR_YELLOW, curses.COLOR_BLACK) # orangish
|
||||
|
||||
# Init list of address in 'Your Identities' tab
|
||||
configSections = BMConfigParser().addresses()
|
||||
for addressInKeysFile in configSections:
|
||||
|
@ -1039,27 +1235,29 @@ def run(stdscr):
|
|||
addresses.append([BMConfigParser().get(addressInKeysFile, "label"), isEnabled, addressInKeysFile])
|
||||
# Set address color
|
||||
if not isEnabled:
|
||||
addresses[len(addresses)-1].append(8) # gray
|
||||
addresses[len(addresses) - 1].append(8) # gray
|
||||
elif BMConfigParser().safeGetBoolean(addressInKeysFile, 'chan'):
|
||||
addresses[len(addresses)-1].append(9) # orange
|
||||
addresses[len(addresses) - 1].append(9) # orange
|
||||
elif BMConfigParser().safeGetBoolean(addressInKeysFile, 'mailinglist'):
|
||||
addresses[len(addresses)-1].append(5) # magenta
|
||||
addresses[len(addresses) - 1].append(5) # magenta
|
||||
else:
|
||||
addresses[len(addresses)-1].append(0) # black
|
||||
addresses[len(addresses) - 1].append(0) # black
|
||||
addresses.reverse()
|
||||
|
||||
|
||||
stdscr.clear()
|
||||
redraw(stdscr)
|
||||
while quit == False:
|
||||
while quit is False:
|
||||
drawtab(stdscr)
|
||||
handlech(stdscr.getch(), stdscr)
|
||||
|
||||
|
||||
def doShutdown():
|
||||
"""Shutting the app down"""
|
||||
sys.stdout = sys.__stdout__
|
||||
print("Shutting down...")
|
||||
print "Shutting down..."
|
||||
sys.stdout = printlog
|
||||
shutdown.doCleanShutdown()
|
||||
sys.stdout = sys.__stdout__
|
||||
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
|
||||
try:
|
||||
proxyconfig_start = time.time()
|
||||
get_plugin('proxyconfig', name=proxy_type)(config)
|
||||
if not get_plugin('proxyconfig', name=proxy_type)(config):
|
||||
raise TypeError
|
||||
except TypeError:
|
||||
logger.error(
|
||||
'Failed to run proxy config plugin %s',
|
||||
|
@ -418,7 +419,7 @@ class Main:
|
|||
self.stop()
|
||||
elif not state.enableGUI:
|
||||
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
|
||||
self.stop()
|
||||
test_core.cleanup()
|
||||
|
|
|
@ -23,7 +23,6 @@ from addresses import decodeAddress, addBMIfNotPresent
|
|||
import shared
|
||||
from bitmessageui import Ui_MainWindow
|
||||
from bmconfigparser import BMConfigParser
|
||||
import defaults
|
||||
import namecoin
|
||||
from messageview import MessageView
|
||||
from migrationwizard import Ui_MigrationWizard
|
||||
|
@ -31,15 +30,12 @@ from foldertree import (
|
|||
AccountMixin, Ui_FolderWidget, Ui_AddressWidget, Ui_SubscriptionWidget,
|
||||
MessageList_AddressWidget, MessageList_SubjectWidget,
|
||||
Ui_AddressBookWidgetItemLabel, Ui_AddressBookWidgetItemAddress)
|
||||
from settings import Ui_settingsDialog
|
||||
import settingsmixin
|
||||
import support
|
||||
import debug
|
||||
from helper_ackPayload import genAckPayload
|
||||
from helper_sql import sqlQuery, sqlExecute, sqlExecuteChunked, sqlStoredProcedure
|
||||
import helper_search
|
||||
import l10n
|
||||
import openclpow
|
||||
from utils import str_broadcast_subscribers, avatarize
|
||||
from account import (
|
||||
getSortedAccounts, getSortedSubscriptions, accountClass, BMAccount,
|
||||
|
@ -47,16 +43,15 @@ from account import (
|
|||
import dialogs
|
||||
from network.stats import pendingDownload, pendingUpload
|
||||
from uisignaler import UISignaler
|
||||
import knownnodes
|
||||
import paths
|
||||
from proofofwork import getPowType
|
||||
import queues
|
||||
import shutdown
|
||||
import state
|
||||
from statusbar import BMStatusBar
|
||||
from network.asyncore_pollchoose import set_rates
|
||||
import sound
|
||||
|
||||
# This is needed for tray icon
|
||||
import bitmessage_icons_rc # noqa:F401 pylint: disable=unused-import
|
||||
|
||||
try:
|
||||
from plugins.plugin import get_plugin, get_plugins
|
||||
|
@ -64,49 +59,6 @@ except ImportError:
|
|||
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
|
||||
def powQueueSize():
|
||||
"""Returns the size of queues.workerQueue including current unfinished work"""
|
||||
|
@ -122,9 +74,6 @@ def powQueueSize():
|
|||
|
||||
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
|
||||
maxSoundFrequencySec = 60
|
||||
|
||||
|
@ -132,6 +81,58 @@ class MyForm(settingsmixin.SMainWindow):
|
|||
REPLY_TYPE_CHAN = 1
|
||||
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):
|
||||
QtCore.QObject.connect(self.ui.actionExit, QtCore.SIGNAL(
|
||||
"triggered()"), self.quit)
|
||||
|
@ -605,6 +606,13 @@ class MyForm(settingsmixin.SMainWindow):
|
|||
self.ui = Ui_MainWindow()
|
||||
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
|
||||
# have any.
|
||||
for addressInKeysFile in getSortedAccounts():
|
||||
|
@ -620,26 +628,13 @@ class MyForm(settingsmixin.SMainWindow):
|
|||
BMConfigParser().remove_section(addressInKeysFile)
|
||||
BMConfigParser().save()
|
||||
|
||||
# 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)
|
||||
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
|
||||
self.updateStartOnLogon()
|
||||
|
||||
self.change_translation()
|
||||
|
||||
# e.g. for editing labels
|
||||
self.recurDepth = 0
|
||||
|
||||
|
||||
# switch back to this when replying
|
||||
self.replyFromTab = None
|
||||
|
||||
|
@ -786,6 +781,9 @@ class MyForm(settingsmixin.SMainWindow):
|
|||
self.ui.treeWidgetSubscriptions.keyPressEvent = self.treeWidgetKeyPressEvent
|
||||
self.ui.treeWidgetChans.keyPressEvent = self.treeWidgetKeyPressEvent
|
||||
|
||||
# Key press in addressbook
|
||||
self.ui.tableWidgetAddressBook.keyPressEvent = self.addressbookKeyPressEvent
|
||||
|
||||
# Key press in messagelist
|
||||
self.ui.tableWidgetInbox.keyPressEvent = self.tableWidgetKeyPressEvent
|
||||
self.ui.tableWidgetInboxSubscriptions.keyPressEvent = self.tableWidgetKeyPressEvent
|
||||
|
@ -828,6 +826,28 @@ class MyForm(settingsmixin.SMainWindow):
|
|||
finally:
|
||||
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):
|
||||
TTL = int(sliderPosition ** 3.199 + 3600)
|
||||
self.updateHumanFriendlyTTLDescription(TTL)
|
||||
|
@ -1433,6 +1453,15 @@ class MyForm(settingsmixin.SMainWindow):
|
|||
def treeWidgetKeyPressEvent(self, event):
|
||||
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
|
||||
def tableWidgetKeyPressEvent(self, event):
|
||||
return self.handleKeyPress(event, self.getCurrentMessagelist())
|
||||
|
@ -1441,11 +1470,12 @@ class MyForm(settingsmixin.SMainWindow):
|
|||
def textEditKeyPressEvent(self, event):
|
||||
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()
|
||||
folder = self.getCurrentFolder()
|
||||
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":
|
||||
self.on_action_SentTrash()
|
||||
else:
|
||||
|
@ -1481,17 +1511,17 @@ class MyForm(settingsmixin.SMainWindow):
|
|||
self.ui.lineEditTo.setFocus()
|
||||
event.ignore()
|
||||
elif event.key() == QtCore.Qt.Key_F:
|
||||
searchline = self.getCurrentSearchLine(retObj = True)
|
||||
searchline = self.getCurrentSearchLine(retObj=True)
|
||||
if searchline:
|
||||
searchline.setFocus()
|
||||
event.ignore()
|
||||
if not event.isAccepted():
|
||||
return
|
||||
if isinstance (focus, MessageView):
|
||||
if isinstance(focus, MessageView):
|
||||
return MessageView.keyPressEvent(focus, event)
|
||||
elif isinstance (focus, QtGui.QTableWidget):
|
||||
elif isinstance(focus, QtGui.QTableWidget):
|
||||
return QtGui.QTableWidget.keyPressEvent(focus, event)
|
||||
elif isinstance (focus, QtGui.QTreeWidget):
|
||||
elif isinstance(focus, QtGui.QTreeWidget):
|
||||
return QtGui.QTreeWidget.keyPressEvent(focus, event)
|
||||
|
||||
# menu button 'manage keys'
|
||||
|
@ -1622,7 +1652,6 @@ class MyForm(settingsmixin.SMainWindow):
|
|||
# The window state has just been changed to
|
||||
# Normal/Maximised/FullScreen
|
||||
pass
|
||||
# QtGui.QWidget.changeEvent(self, event)
|
||||
|
||||
def __icon_activated(self, reason):
|
||||
if reason == QtGui.QSystemTrayIcon.Trigger:
|
||||
|
@ -2434,225 +2463,7 @@ class MyForm(settingsmixin.SMainWindow):
|
|||
dialogs.AboutDialog(self).exec_()
|
||||
|
||||
def click_actionSettings(self):
|
||||
self.settingsDialogInstance = settingsDialog(self)
|
||||
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
|
||||
dialogs.SettingsDialog(self, firstrun=self._firstrun).exec_()
|
||||
|
||||
def on_action_Send(self):
|
||||
"""Send message to current selected address"""
|
||||
|
@ -3393,8 +3204,7 @@ class MyForm(settingsmixin.SMainWindow):
|
|||
0].row()
|
||||
item = self.ui.tableWidgetAddressBook.item(currentRow, 0)
|
||||
sqlExecute(
|
||||
'DELETE FROM addressbook WHERE label=? AND address=?',
|
||||
item.label, item.address)
|
||||
'DELETE FROM addressbook WHERE address=?', item.address)
|
||||
self.ui.tableWidgetAddressBook.removeRow(currentRow)
|
||||
self.rerenderMessagelistFromLabels()
|
||||
self.rerenderMessagelistToLabels()
|
||||
|
@ -4253,237 +4063,6 @@ class MyForm(settingsmixin.SMainWindow):
|
|||
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
|
||||
# correctly (rather than alphabetically), we need to overload the <
|
||||
# operator and use this class instead of QTableWidgetItem.
|
||||
|
@ -4558,7 +4137,6 @@ def init():
|
|||
def run():
|
||||
global myapp
|
||||
app = init()
|
||||
change_translation(l10n.getTranslationLanguage())
|
||||
app.setStyleSheet("QStatusBar::item { border: 0px solid black }")
|
||||
myapp = MyForm()
|
||||
|
||||
|
|
|
@ -5,22 +5,25 @@ src/bitmessageqt/dialogs.py
|
|||
|
||||
from PyQt4 import QtGui
|
||||
|
||||
from version import softwareVersion
|
||||
|
||||
import paths
|
||||
import widgets
|
||||
from address_dialogs import (
|
||||
AddAddressDialog, EmailGatewayDialog, NewAddressDialog, NewSubscriptionDialog, RegenerateAddressesDialog,
|
||||
AddAddressDialog, EmailGatewayDialog, NewAddressDialog,
|
||||
NewSubscriptionDialog, RegenerateAddressesDialog,
|
||||
SpecialAddressBehaviorDialog
|
||||
)
|
||||
from newchandialog import NewChanDialog
|
||||
from retranslateui import RetranslateMixin
|
||||
from settings import SettingsDialog
|
||||
from tr import _translate
|
||||
from version import softwareVersion
|
||||
|
||||
|
||||
__all__ = [
|
||||
"NewChanDialog", "AddAddressDialog", "NewAddressDialog",
|
||||
"NewSubscriptionDialog", "RegenerateAddressesDialog",
|
||||
"SpecialAddressBehaviorDialog", "EmailGatewayDialog"
|
||||
"SpecialAddressBehaviorDialog", "EmailGatewayDialog",
|
||||
"SettingsDialog"
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -1,630 +1,546 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# pylint: disable=too-many-instance-attributes,too-many-locals,too-many-statements,attribute-defined-outside-init
|
||||
"""
|
||||
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
|
||||
import os
|
||||
import sys
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
from . import bitmessage_icons_rc # pylint: disable=unused-import
|
||||
from .languagebox import LanguageBox
|
||||
|
||||
try:
|
||||
_fromUtf8 = QtCore.QString.fromUtf8
|
||||
except AttributeError:
|
||||
def _fromUtf8(s):
|
||||
return s
|
||||
|
||||
try:
|
||||
_encoding = QtGui.QApplication.UnicodeUTF8
|
||||
|
||||
def _translate(context, text, disambig):
|
||||
return QtGui.QApplication.translate(context, text, disambig, _encoding)
|
||||
except AttributeError:
|
||||
def _translate(context, text, disambig):
|
||||
return QtGui.QApplication.translate(context, text, disambig)
|
||||
import debug
|
||||
import defaults
|
||||
import knownnodes
|
||||
import namecoin
|
||||
import openclpow
|
||||
import paths
|
||||
import queues
|
||||
import shared
|
||||
import state
|
||||
import tempfile
|
||||
import widgets
|
||||
from bmconfigparser import BMConfigParser
|
||||
from helper_sql import sqlExecute, sqlStoredProcedure
|
||||
from network.asyncore_pollchoose import set_rates
|
||||
from tr import _translate
|
||||
|
||||
|
||||
class Ui_settingsDialog(object):
|
||||
"""Encapsulate a UI settings dialog object"""
|
||||
class SettingsDialog(QtGui.QDialog):
|
||||
"""The "Settings" dialog"""
|
||||
def __init__(self, parent=None, firstrun=False):
|
||||
super(SettingsDialog, self).__init__(parent)
|
||||
widgets.load('settings.ui', self)
|
||||
|
||||
def setupUi(self, settingsDialog):
|
||||
"""Set up the UI"""
|
||||
self.parent = parent
|
||||
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(
|
||||
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.groupBox_2 = QtGui.QGroupBox(self.tabNetworkSettings)
|
||||
self.groupBox_2.setObjectName(_fromUtf8("groupBox_2"))
|
||||
self.gridLayout_2 = QtGui.QGridLayout(self.groupBox_2)
|
||||
self.gridLayout_2.setObjectName(_fromUtf8("gridLayout_2"))
|
||||
self.label_2 = QtGui.QLabel(self.groupBox_2)
|
||||
self.label_2.setObjectName(_fromUtf8("label_2"))
|
||||
self.gridLayout_2.addWidget(self.label_2, 0, 0, 1, 1)
|
||||
self.label_3 = QtGui.QLabel(self.groupBox_2)
|
||||
self.label_3.setObjectName(_fromUtf8("label_3"))
|
||||
self.gridLayout_2.addWidget(self.label_3, 1, 1, 1, 1)
|
||||
self.lineEditSocksHostname = QtGui.QLineEdit(self.groupBox_2)
|
||||
self.lineEditSocksHostname.setObjectName(_fromUtf8("lineEditSocksHostname"))
|
||||
self.lineEditSocksHostname.setPlaceholderText(_fromUtf8("127.0.0.1"))
|
||||
self.gridLayout_2.addWidget(self.lineEditSocksHostname, 1, 2, 1, 2)
|
||||
self.label_4 = QtGui.QLabel(self.groupBox_2)
|
||||
self.label_4.setObjectName(_fromUtf8("label_4"))
|
||||
self.gridLayout_2.addWidget(self.label_4, 1, 4, 1, 1)
|
||||
self.lineEditSocksPort = QtGui.QLineEdit(self.groupBox_2)
|
||||
self.lineEditSocksPort.setObjectName(_fromUtf8("lineEditSocksPort"))
|
||||
if platform in ['darwin', 'win32', 'win64']:
|
||||
self.lineEditSocksPort.setPlaceholderText(_fromUtf8("9150"))
|
||||
|
||||
self.adjust_from_config(self.config)
|
||||
if firstrun:
|
||||
# switch to "Network Settings" tab if user selected
|
||||
# "Let me configure special network settings first" on first run
|
||||
self.tabWidgetSettings.setCurrentIndex(
|
||||
self.tabWidgetSettings.indexOf(self.tabNetworkSettings)
|
||||
)
|
||||
QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self))
|
||||
|
||||
def adjust_from_config(self, config):
|
||||
"""Adjust all widgets state according to config settings"""
|
||||
# pylint: disable=too-many-branches,too-many-statements
|
||||
self.checkBoxStartOnLogon.setChecked(
|
||||
config.getboolean('bitmessagesettings', 'startonlogon'))
|
||||
self.checkBoxMinimizeToTray.setChecked(
|
||||
config.getboolean('bitmessagesettings', 'minimizetotray'))
|
||||
self.checkBoxTrayOnClose.setChecked(
|
||||
config.safeGetBoolean('bitmessagesettings', 'trayonclose'))
|
||||
self.checkBoxHideTrayConnectionNotifications.setChecked(
|
||||
config.getboolean("bitmessagesettings", "hidetrayconnectionnotifications"))
|
||||
self.checkBoxShowTrayNotifications.setChecked(
|
||||
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:
|
||||
self.lineEditSocksPort.setPlaceholderText(_fromUtf8("9050"))
|
||||
self.gridLayout_2.addWidget(self.lineEditSocksPort, 1, 5, 1, 1)
|
||||
self.checkBoxAuthentication = QtGui.QCheckBox(self.groupBox_2)
|
||||
self.checkBoxAuthentication.setObjectName(_fromUtf8("checkBoxAuthentication"))
|
||||
self.gridLayout_2.addWidget(self.checkBoxAuthentication, 2, 1, 1, 1)
|
||||
self.label_5 = QtGui.QLabel(self.groupBox_2)
|
||||
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)
|
||||
try:
|
||||
tempfile.NamedTemporaryFile(
|
||||
dir=paths.lookupExeFolder(), delete=True
|
||||
).close() # should autodelete
|
||||
except:
|
||||
self.checkBoxPortableMode.setDisabled(True)
|
||||
|
||||
self.retranslateUi(settingsDialog)
|
||||
self.tabWidgetSettings.setCurrentIndex(0)
|
||||
QtCore.QObject.connect( # pylint: disable=no-member
|
||||
self.buttonBox, QtCore.SIGNAL(_fromUtf8("accepted()")), settingsDialog.accept)
|
||||
QtCore.QObject.connect( # pylint: disable=no-member
|
||||
self.buttonBox, QtCore.SIGNAL(_fromUtf8("rejected()")), settingsDialog.reject)
|
||||
QtCore.QObject.connect( # pylint: disable=no-member
|
||||
self.checkBoxAuthentication,
|
||||
QtCore.SIGNAL(
|
||||
_fromUtf8("toggled(bool)")),
|
||||
self.lineEditSocksUsername.setEnabled)
|
||||
QtCore.QObject.connect( # pylint: disable=no-member
|
||||
self.checkBoxAuthentication,
|
||||
QtCore.SIGNAL(
|
||||
_fromUtf8("toggled(bool)")),
|
||||
self.lineEditSocksPassword.setEnabled)
|
||||
QtCore.QMetaObject.connectSlotsByName(settingsDialog)
|
||||
settingsDialog.setTabOrder(self.tabWidgetSettings, self.checkBoxStartOnLogon)
|
||||
settingsDialog.setTabOrder(self.checkBoxStartOnLogon, self.checkBoxStartInTray)
|
||||
settingsDialog.setTabOrder(self.checkBoxStartInTray, self.checkBoxMinimizeToTray)
|
||||
settingsDialog.setTabOrder(self.checkBoxMinimizeToTray, self.lineEditTCPPort)
|
||||
settingsDialog.setTabOrder(self.lineEditTCPPort, self.comboBoxProxyType)
|
||||
settingsDialog.setTabOrder(self.comboBoxProxyType, self.lineEditSocksHostname)
|
||||
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)
|
||||
if 'darwin' in sys.platform:
|
||||
self.checkBoxStartOnLogon.setDisabled(True)
|
||||
self.checkBoxStartOnLogon.setText(_translate(
|
||||
"MainWindow", "Start-on-login not yet supported on your OS."))
|
||||
self.checkBoxMinimizeToTray.setDisabled(True)
|
||||
self.checkBoxMinimizeToTray.setText(_translate(
|
||||
"MainWindow", "Minimize-to-tray not yet supported on your OS."))
|
||||
self.checkBoxShowTrayNotifications.setDisabled(True)
|
||||
self.checkBoxShowTrayNotifications.setText(_translate(
|
||||
"MainWindow", "Tray notifications not yet supported on your OS."))
|
||||
elif 'linux' in sys.platform:
|
||||
self.checkBoxStartOnLogon.setDisabled(True)
|
||||
self.checkBoxStartOnLogon.setText(_translate(
|
||||
"MainWindow", "Start-on-login not yet supported on your OS."))
|
||||
# On the Network settings tab:
|
||||
self.lineEditTCPPort.setText(str(
|
||||
config.get('bitmessagesettings', 'port')))
|
||||
self.checkBoxUPnP.setChecked(
|
||||
config.safeGetBoolean('bitmessagesettings', 'upnp'))
|
||||
self.checkBoxAuthentication.setChecked(
|
||||
config.getboolean('bitmessagesettings', 'socksauthentication'))
|
||||
self.checkBoxSocksListen.setChecked(
|
||||
config.getboolean('bitmessagesettings', 'sockslisten'))
|
||||
|
||||
def retranslateUi(self, settingsDialog):
|
||||
"""Re-translate the UI into the supported languages"""
|
||||
proxy_type = config.safeGet(
|
||||
'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.checkBoxStartOnLogon.setText(_translate("settingsDialog", "Start Bitmessage on user login", None))
|
||||
self.groupBoxTray.setTitle(_translate("settingsDialog", "Tray", None))
|
||||
self.checkBoxStartInTray.setText(
|
||||
_translate(
|
||||
"settingsDialog",
|
||||
"Start Bitmessage in the tray (don\'t show main window)",
|
||||
None))
|
||||
self.checkBoxMinimizeToTray.setText(_translate("settingsDialog", "Minimize to tray", None))
|
||||
self.checkBoxTrayOnClose.setText(_translate("settingsDialog", "Close to tray", None))
|
||||
self.checkBoxHideTrayConnectionNotifications.setText(
|
||||
_translate("settingsDialog", "Hide connection notifications", None))
|
||||
self.checkBoxShowTrayNotifications.setText(
|
||||
_translate(
|
||||
"settingsDialog",
|
||||
"Show notification when message received",
|
||||
None))
|
||||
self.checkBoxPortableMode.setText(_translate("settingsDialog", "Run in Portable Mode", None))
|
||||
self.PortableModeDescription.setText(
|
||||
_translate(
|
||||
"settingsDialog",
|
||||
"In Portable Mode, messages and config files are stored in the same directory as the"
|
||||
" program rather than the normal application-data folder. This makes it convenient to"
|
||||
" run Bitmessage from a USB thumb drive.",
|
||||
None))
|
||||
self.checkBoxWillinglySendToMobile.setText(
|
||||
_translate(
|
||||
"settingsDialog",
|
||||
"Willingly include unencrypted destination address when sending to a mobile device",
|
||||
None))
|
||||
self.checkBoxUseIdenticons.setText(_translate("settingsDialog", "Use Identicons", None))
|
||||
self.checkBoxReplyBelow.setText(_translate("settingsDialog", "Reply below Quote", None))
|
||||
self.groupBox.setTitle(_translate("settingsDialog", "Interface Language", None))
|
||||
self.languageComboBox.setItemText(0, _translate("settingsDialog", "System Settings", "system"))
|
||||
self.tabWidgetSettings.setTabText(
|
||||
self.tabWidgetSettings.indexOf(
|
||||
self.tabUserInterface),
|
||||
_translate(
|
||||
"settingsDialog", "User Interface", None))
|
||||
self.groupBox1.setTitle(_translate("settingsDialog", "Listening port", None))
|
||||
self.label.setText(_translate("settingsDialog", "Listen for connections on port:", None))
|
||||
self.labelUPnP.setText(_translate("settingsDialog", "UPnP:", None))
|
||||
self.groupBox_3.setTitle(_translate("settingsDialog", "Bandwidth limit", None))
|
||||
self.label_24.setText(_translate("settingsDialog", "Maximum download rate (kB/s): [0: unlimited]", None))
|
||||
self.label_25.setText(_translate("settingsDialog", "Maximum upload rate (kB/s): [0: unlimited]", None))
|
||||
self.label_26.setText(_translate("settingsDialog", "Maximum outbound connections: [0: none]", None))
|
||||
self.groupBox_2.setTitle(_translate("settingsDialog", "Proxy server / Tor", None))
|
||||
self.label_2.setText(_translate("settingsDialog", "Type:", None))
|
||||
self.label_3.setText(_translate("settingsDialog", "Server hostname:", None))
|
||||
self.label_4.setText(_translate("settingsDialog", "Port:", None))
|
||||
self.checkBoxAuthentication.setText(_translate("settingsDialog", "Authentication", None))
|
||||
self.label_5.setText(_translate("settingsDialog", "Username:", None))
|
||||
self.label_6.setText(_translate("settingsDialog", "Pass:", None))
|
||||
self.checkBoxSocksListen.setText(
|
||||
_translate(
|
||||
"settingsDialog",
|
||||
"Listen for incoming connections when using proxy",
|
||||
None))
|
||||
self.comboBoxProxyType.setItemText(0, _translate("settingsDialog", "none", None))
|
||||
self.comboBoxProxyType.setItemText(1, _translate("settingsDialog", "SOCKS4a", None))
|
||||
self.comboBoxProxyType.setItemText(2, _translate("settingsDialog", "SOCKS5", None))
|
||||
self.tabWidgetSettings.setTabText(
|
||||
self.tabWidgetSettings.indexOf(
|
||||
self.tabNetworkSettings),
|
||||
_translate(
|
||||
"settingsDialog", "Network Settings", None))
|
||||
self.label_9.setText(_translate("settingsDialog", "Total difficulty:", None))
|
||||
self.label_10.setText(
|
||||
_translate(
|
||||
"settingsDialog",
|
||||
"The \'Total difficulty\' affects the absolute amount of work the sender must complete."
|
||||
" Doubling this value doubles the amount of work.",
|
||||
None))
|
||||
self.label_11.setText(_translate("settingsDialog", "Small message difficulty:", None))
|
||||
self.label_8.setText(_translate(
|
||||
"settingsDialog",
|
||||
"When someone sends you a message, their computer must first complete some work. The difficulty of this"
|
||||
" work, by default, is 1. You may raise this default for new addresses you create by changing the values"
|
||||
" here. Any new addresses you create will require senders to meet the higher difficulty. There is one"
|
||||
" exception: if you add a friend or acquaintance to your address book, Bitmessage will automatically"
|
||||
" notify them when you next send a message that they need only complete the minimum amount of"
|
||||
" work: difficulty 1. ",
|
||||
None))
|
||||
self.label_12.setText(
|
||||
_translate(
|
||||
"settingsDialog",
|
||||
"The \'Small message difficulty\' mostly only affects the difficulty of sending small messages."
|
||||
" Doubling this value makes it almost twice as difficult to send a small message but doesn\'t really"
|
||||
" affect large messages.",
|
||||
None))
|
||||
self.tabWidgetSettings.setTabText(
|
||||
self.tabWidgetSettings.indexOf(
|
||||
self.tabDemandedDifficulty),
|
||||
_translate(
|
||||
"settingsDialog", "Demanded difficulty", None))
|
||||
self.label_15.setText(
|
||||
_translate(
|
||||
"settingsDialog",
|
||||
"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.",
|
||||
None))
|
||||
self.label_13.setText(_translate("settingsDialog", "Maximum acceptable total difficulty:", None))
|
||||
self.label_14.setText(_translate("settingsDialog", "Maximum acceptable small message difficulty:", None))
|
||||
self.tabWidgetSettings.setTabText(
|
||||
self.tabWidgetSettings.indexOf(
|
||||
self.tabMaxAcceptableDifficulty),
|
||||
_translate(
|
||||
"settingsDialog", "Max acceptable difficulty", None))
|
||||
self.labelOpenCL.setText(_translate("settingsDialog", "Hardware GPU acceleration (OpenCL):", None))
|
||||
self.label_16.setText(_translate(
|
||||
"settingsDialog",
|
||||
"<html><head/><body><p>Bitmessage can utilize a different Bitcoin-based program called Namecoin to make"
|
||||
" addresses human-friendly. For example, instead of having to tell your friend your long Bitmessage"
|
||||
" address, you can simply tell him to send a message to <span style=\" font-style:italic;\">test."
|
||||
" </span></p><p>(Getting your own Bitmessage address into Namecoin is still rather difficult).</p>"
|
||||
"<p>Bitmessage can use either namecoind directly or a running nmcontrol instance.</p></body></html>",
|
||||
None))
|
||||
self.label_17.setText(_translate("settingsDialog", "Host:", None))
|
||||
self.label_18.setText(_translate("settingsDialog", "Port:", None))
|
||||
self.labelNamecoinUser.setText(_translate("settingsDialog", "Username:", None))
|
||||
self.labelNamecoinPassword.setText(_translate("settingsDialog", "Password:", None))
|
||||
self.pushButtonNamecoinTest.setText(_translate("settingsDialog", "Test", None))
|
||||
self.label_21.setText(_translate("settingsDialog", "Connect to:", None))
|
||||
self.radioButtonNamecoinNamecoind.setText(_translate("settingsDialog", "Namecoind", None))
|
||||
self.radioButtonNamecoinNmcontrol.setText(_translate("settingsDialog", "NMControl", None))
|
||||
self.tabWidgetSettings.setTabText(
|
||||
self.tabWidgetSettings.indexOf(
|
||||
self.tabNamecoin),
|
||||
_translate(
|
||||
"settingsDialog", "Namecoin integration", None))
|
||||
self.label_7.setText(_translate(
|
||||
"settingsDialog",
|
||||
"<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"
|
||||
" exponential backoff forever; messages will be resent after 5, 10, 20 days ect. until the receiver"
|
||||
" acknowledges them. Here you may change that behavior by having Bitmessage give up after a certain"
|
||||
" number of days or months.</p><p>Leave these input fields blank for the default behavior."
|
||||
" </p></body></html>",
|
||||
None))
|
||||
self.label_19.setText(_translate("settingsDialog", "Give up after", None))
|
||||
self.label_20.setText(_translate("settingsDialog", "and", None))
|
||||
self.label_22.setText(_translate("settingsDialog", "days", None))
|
||||
self.label_23.setText(_translate("settingsDialog", "months.", None))
|
||||
self.tabWidgetSettings.setTabText(
|
||||
self.tabWidgetSettings.indexOf(
|
||||
self.tabResendsExpire),
|
||||
_translate(
|
||||
"settingsDialog", "Resends Expire", None))
|
||||
self.lineEditSocksHostname.setText(
|
||||
config.get('bitmessagesettings', 'sockshostname'))
|
||||
self.lineEditSocksPort.setText(str(
|
||||
config.get('bitmessagesettings', 'socksport')))
|
||||
self.lineEditSocksUsername.setText(
|
||||
config.get('bitmessagesettings', 'socksusername'))
|
||||
self.lineEditSocksPassword.setText(
|
||||
config.get('bitmessagesettings', 'sockspassword'))
|
||||
|
||||
self.lineEditMaxDownloadRate.setText(str(
|
||||
config.get('bitmessagesettings', 'maxdownloadrate')))
|
||||
self.lineEditMaxUploadRate.setText(str(
|
||||
config.get('bitmessagesettings', 'maxuploadrate')))
|
||||
self.lineEditMaxOutboundConnections.setText(str(
|
||||
config.get('bitmessagesettings', 'maxoutboundconnections')))
|
||||
|
||||
# Demanded difficulty tab
|
||||
self.lineEditTotalDifficulty.setText(str((float(
|
||||
config.getint(
|
||||
'bitmessagesettings', 'defaultnoncetrialsperbyte')
|
||||
) / defaults.networkDefaultProofOfWorkNonceTrialsPerByte)))
|
||||
self.lineEditSmallMessageDifficulty.setText(str((float(
|
||||
config.getint(
|
||||
'bitmessagesettings', 'defaultpayloadlengthextrabytes')
|
||||
) / defaults.networkDefaultPayloadLengthExtraBytes)))
|
||||
|
||||
# Max acceptable difficulty tab
|
||||
self.lineEditMaxAcceptableTotalDifficulty.setText(str((float(
|
||||
config.getint(
|
||||
'bitmessagesettings', 'maxacceptablenoncetrialsperbyte')
|
||||
) / defaults.networkDefaultProofOfWorkNonceTrialsPerByte)))
|
||||
self.lineEditMaxAcceptableSmallMessageDifficulty.setText(str((float(
|
||||
config.getint(
|
||||
'bitmessagesettings', 'maxacceptablepayloadlengthextrabytes')
|
||||
) / defaults.networkDefaultPayloadLengthExtraBytes)))
|
||||
|
||||
# OpenCL
|
||||
self.comboBoxOpenCL.setEnabled(openclpow.openclAvailable())
|
||||
self.comboBoxOpenCL.clear()
|
||||
self.comboBoxOpenCL.addItem("None")
|
||||
self.comboBoxOpenCL.addItems(openclpow.vendors)
|
||||
self.comboBoxOpenCL.setCurrentIndex(0)
|
||||
for i in range(self.comboBoxOpenCL.count()):
|
||||
if self.comboBoxOpenCL.itemText(i) == config.safeGet(
|
||||
'bitmessagesettings', 'opencl'):
|
||||
self.comboBoxOpenCL.setCurrentIndex(i)
|
||||
break
|
||||
|
||||
# Namecoin integration tab
|
||||
nmctype = config.get('bitmessagesettings', 'namecoinrpctype')
|
||||
self.lineEditNamecoinHost.setText(
|
||||
config.get('bitmessagesettings', 'namecoinrpchost'))
|
||||
self.lineEditNamecoinPort.setText(str(
|
||||
config.get('bitmessagesettings', 'namecoinrpcport')))
|
||||
self.lineEditNamecoinUser.setText(
|
||||
config.get('bitmessagesettings', 'namecoinrpcuser'))
|
||||
self.lineEditNamecoinPassword.setText(
|
||||
config.get('bitmessagesettings', 'namecoinrpcpassword'))
|
||||
|
||||
if nmctype == "namecoind":
|
||||
self.radioButtonNamecoinNamecoind.setChecked(True)
|
||||
elif nmctype == "nmcontrol":
|
||||
self.radioButtonNamecoinNmcontrol.setChecked(True)
|
||||
self.lineEditNamecoinUser.setEnabled(False)
|
||||
self.labelNamecoinUser.setEnabled(False)
|
||||
self.lineEditNamecoinPassword.setEnabled(False)
|
||||
self.labelNamecoinPassword.setEnabled(False)
|
||||
else:
|
||||
assert False
|
||||
|
||||
# Message Resend tab
|
||||
self.lineEditDays.setText(str(
|
||||
config.get('bitmessagesettings', 'stopresendingafterxdays')))
|
||||
self.lineEditMonths.setText(str(
|
||||
config.get('bitmessagesettings', 'stopresendingafterxmonths')))
|
||||
|
||||
def comboBoxProxyTypeChanged(self, comboBoxIndex):
|
||||
"""A callback for currentIndexChanged event of comboBoxProxyType"""
|
||||
if comboBoxIndex == 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 comboBoxIndex in (1, 2):
|
||||
self.lineEditSocksHostname.setEnabled(True)
|
||||
self.lineEditSocksPort.setEnabled(True)
|
||||
self.checkBoxAuthentication.setEnabled(True)
|
||||
self.checkBoxSocksListen.setEnabled(True)
|
||||
if self.checkBoxAuthentication.isChecked():
|
||||
self.lineEditSocksUsername.setEnabled(True)
|
||||
self.lineEditSocksPassword.setEnabled(True)
|
||||
|
||||
def getNamecoinType(self):
|
||||
"""
|
||||
Check status of namecoin integration radio buttons
|
||||
and translate it to a string as in the options.
|
||||
"""
|
||||
if self.radioButtonNamecoinNamecoind.isChecked():
|
||||
return "namecoind"
|
||||
if self.radioButtonNamecoinNmcontrol.isChecked():
|
||||
return "nmcontrol"
|
||||
assert False
|
||||
|
||||
# Namecoin connection type was changed.
|
||||
def namecoinTypeChanged(self, checked): # pylint: disable=unused-argument
|
||||
"""A callback for toggled event of radioButtonNamecoinNamecoind"""
|
||||
nmctype = self.getNamecoinType()
|
||||
assert nmctype == "namecoind" or nmctype == "nmcontrol"
|
||||
|
||||
isNamecoind = (nmctype == "namecoind")
|
||||
self.lineEditNamecoinUser.setEnabled(isNamecoind)
|
||||
self.labelNamecoinUser.setEnabled(isNamecoind)
|
||||
self.lineEditNamecoinPassword.setEnabled(isNamecoind)
|
||||
self.labelNamecoinPassword.setEnabled(isNamecoind)
|
||||
|
||||
if isNamecoind:
|
||||
self.lineEditNamecoinPort.setText(defaults.namecoinDefaultRpcPort)
|
||||
else:
|
||||
self.lineEditNamecoinPort.setText("9000")
|
||||
|
||||
def click_pushButtonNamecoinTest(self):
|
||||
"""Test the namecoin settings specified in the settings dialog."""
|
||||
self.labelNamecoinTestResult.setText(
|
||||
_translate("MainWindow", "Testing..."))
|
||||
nc = namecoin.namecoinConnection({
|
||||
'type': self.getNamecoinType(),
|
||||
'host': str(self.lineEditNamecoinHost.text().toUtf8()),
|
||||
'port': str(self.lineEditNamecoinPort.text().toUtf8()),
|
||||
'user': str(self.lineEditNamecoinUser.text().toUtf8()),
|
||||
'password': str(self.lineEditNamecoinPassword.text().toUtf8())
|
||||
})
|
||||
status, text = nc.test()
|
||||
self.labelNamecoinTestResult.setText(text)
|
||||
if status == 'success':
|
||||
self.parent.namecoin = nc
|
||||
|
||||
def accept(self):
|
||||
"""A callback for accepted event of buttonBox (OK button pressed)"""
|
||||
# pylint: disable=too-many-branches,too-many-statements
|
||||
super(SettingsDialog, self).accept()
|
||||
if self.firstrun:
|
||||
self.config.remove_option('bitmessagesettings', 'dontconnect')
|
||||
self.config.set('bitmessagesettings', 'startonlogon', str(
|
||||
self.checkBoxStartOnLogon.isChecked()))
|
||||
self.config.set('bitmessagesettings', 'minimizetotray', str(
|
||||
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>
|
||||
</attribute>
|
||||
<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">
|
||||
<widget class="QCheckBox" name="checkBoxStartOnLogon">
|
||||
<property name="text">
|
||||
|
@ -44,20 +56,43 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="checkBoxStartInTray">
|
||||
<property name="text">
|
||||
<string>Start Bitmessage in the tray (don't show main window)</string>
|
||||
<item row="1" column="0">
|
||||
<widget class="QGroupBox" name="groupBoxTray">
|
||||
<property name="title">
|
||||
<string>Tray</string>
|
||||
</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>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="checkBoxMinimizeToTray">
|
||||
<widget class="QCheckBox" name="checkBoxHideTrayConnectionNotifications">
|
||||
<property name="text">
|
||||
<string>Minimize to tray</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
<string>Hide connection notifications</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -117,90 +152,15 @@
|
|||
<property name="title">
|
||||
<string>Interface Language</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QComboBox" name="languageComboBox">
|
||||
<layout class="QVBoxLayout">
|
||||
<item>
|
||||
<widget class="LanguageBox" name="languageComboBox">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</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>
|
||||
</item>
|
||||
</layout>
|
||||
|
@ -213,6 +173,18 @@
|
|||
<string>Network Settings</string>
|
||||
</attribute>
|
||||
<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">
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
|
@ -220,26 +192,13 @@
|
|||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<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">
|
||||
<property name="text">
|
||||
<string>Listen for connections on port:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="lineEditTCPPort">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
|
@ -249,6 +208,26 @@
|
|||
</property>
|
||||
</widget>
|
||||
</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>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -466,6 +445,18 @@
|
|||
<string>Demanded difficulty</string>
|
||||
</attribute>
|
||||
<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">
|
||||
<widget class="QLabel" name="label_9">
|
||||
<property name="text">
|
||||
|
@ -594,6 +585,18 @@
|
|||
<string>Max acceptable difficulty</string>
|
||||
</attribute>
|
||||
<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">
|
||||
<widget class="QLabel" name="label_15">
|
||||
<property name="text">
|
||||
|
@ -698,6 +701,33 @@
|
|||
</property>
|
||||
</spacer>
|
||||
</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>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tabNamecoin">
|
||||
|
@ -705,6 +735,18 @@
|
|||
<string>Namecoin integration</string>
|
||||
</attribute>
|
||||
<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">
|
||||
<spacer name="horizontalSpacer_6">
|
||||
<property name="orientation">
|
||||
|
@ -888,6 +930,18 @@
|
|||
<string>Resends Expire</string>
|
||||
</attribute>
|
||||
<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">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
|
@ -912,91 +966,69 @@
|
|||
</spacer>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QWidget" name="widget" native="true">
|
||||
<widget class="QGroupBox">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>231</width>
|
||||
<height>75</height>
|
||||
</size>
|
||||
</property>
|
||||
<layout class="QGridLayout">
|
||||
<item row="0" column="0">
|
||||
<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">
|
||||
<string>Give up after</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<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">
|
||||
<string>and</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit" name="lineEditDays">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>113</x>
|
||||
<y>20</y>
|
||||
<width>51</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="lineEditDays">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>55</width>
|
||||
<height>100</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit" name="lineEditMonths">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>113</x>
|
||||
<y>40</y>
|
||||
<width>51</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="lineEditMonths">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>55</width>
|
||||
<height>100</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label_22">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>169</x>
|
||||
<y>23</y>
|
||||
<width>61</width>
|
||||
<height>16</height>
|
||||
</rect>
|
||||
</property>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QLabel">
|
||||
<property name="text">
|
||||
<string>days</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label_23">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>170</x>
|
||||
<y>41</y>
|
||||
<width>71</width>
|
||||
<height>16</height>
|
||||
</rect>
|
||||
</property>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QLabel">
|
||||
<property name="text">
|
||||
<string>months.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
|
@ -1017,7 +1049,14 @@
|
|||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>LanguageBox</class>
|
||||
<extends>QComboBox</extends>
|
||||
<header>bitmessageqt.languagebox</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>tabWidgetSettings</tabstop>
|
||||
<tabstop>checkBoxStartOnLogon</tabstop>
|
||||
|
@ -1101,5 +1140,53 @@
|
|||
</hint>
|
||||
</hints>
|
||||
</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>
|
||||
</ui>
|
||||
|
|
|
@ -46,6 +46,8 @@ class BMConfigParser(ConfigParser.SafeConfigParser):
|
|||
"""Singleton class inherited from ConfigParser.SafeConfigParser
|
||||
with additional methods specific to bitmessage config."""
|
||||
|
||||
_temp = {}
|
||||
|
||||
def set(self, section, option, value=None):
|
||||
if self._optcre is self.OPTCRE or value:
|
||||
if not isinstance(value, basestring):
|
||||
|
@ -59,6 +61,10 @@ class BMConfigParser(ConfigParser.SafeConfigParser):
|
|||
if section == "bitmessagesettings" and option == "timeformat":
|
||||
return ConfigParser.ConfigParser.get(
|
||||
self, section, option, raw, variables)
|
||||
try:
|
||||
return self._temp[section][option]
|
||||
except KeyError:
|
||||
pass
|
||||
return ConfigParser.ConfigParser.get(
|
||||
self, section, option, True, variables)
|
||||
except ConfigParser.InterpolationError:
|
||||
|
@ -70,6 +76,13 @@ class BMConfigParser(ConfigParser.SafeConfigParser):
|
|||
except (KeyError, ValueError, AttributeError):
|
||||
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):
|
||||
try:
|
||||
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
|
||||
from bmconfigparser import BMConfigParser
|
||||
from debug import logger
|
||||
from helper_bootstrap import dns
|
||||
|
||||
knownNodesLock = threading.Lock()
|
||||
knownNodes = {stream: {} for stream in range(1, 4)}
|
||||
|
@ -35,10 +34,6 @@ DEFAULT_NODES = (
|
|||
state.Peer('178.11.46.221', 8444)
|
||||
)
|
||||
|
||||
DEFAULT_NODES_ONION = (
|
||||
state.Peer('quzwelsuziwqgpt2.onion', 8444),
|
||||
)
|
||||
|
||||
|
||||
def json_serialize_knownnodes(output):
|
||||
"""
|
||||
|
@ -67,8 +62,7 @@ def json_deserialize_knownnodes(source):
|
|||
|
||||
if (
|
||||
not (knownNodesActual or info.get('self')) and
|
||||
peer not in DEFAULT_NODES and
|
||||
peer not in DEFAULT_NODES_ONION
|
||||
peer not in DEFAULT_NODES
|
||||
):
|
||||
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
|
||||
for peer in DEFAULT_NODES_ONION if onion else DEFAULT_NODES:
|
||||
for peer in DEFAULT_NODES:
|
||||
addKnownNode(1, peer, past)
|
||||
saveKnownNodes()
|
||||
|
||||
|
@ -177,39 +171,37 @@ def trimKnownNodes(recAddrStream=1):
|
|||
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():
|
||||
"""
|
||||
Cleanup knownnodes: remove old nodes and nodes with low rating
|
||||
"""
|
||||
now = int(time.time())
|
||||
needToWriteKnownNodesToDisk = False
|
||||
dns_done = False
|
||||
spawnConnections = not BMConfigParser().safeGetBoolean(
|
||||
'bitmessagesettings', 'dontconnect'
|
||||
) and BMConfigParser().safeGetBoolean(
|
||||
'bitmessagesettings', 'sendoutgoingconnections')
|
||||
|
||||
with knownNodesLock:
|
||||
for stream in knownNodes:
|
||||
if stream not in state.streamsInWhichIAmParticipating:
|
||||
continue
|
||||
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:
|
||||
if len(knownNodes[stream]) <= 1: # leave at least one node
|
||||
break
|
||||
try:
|
||||
# scrap old nodes
|
||||
if (now - knownNodes[stream][node]["lastseen"] >
|
||||
2419200): # 28 days
|
||||
age = now - knownNodes[stream][node]["lastseen"]
|
||||
# scrap old nodes (age > 28 days)
|
||||
if age > 2419200:
|
||||
needToWriteKnownNodesToDisk = True
|
||||
del knownNodes[stream][node]
|
||||
continue
|
||||
# scrap old nodes with low rating
|
||||
if (now - knownNodes[stream][node]["lastseen"] > 10800 and
|
||||
knownNodes[stream][node]["rating"] <=
|
||||
# scrap old nodes (age > 3 hours) with low rating
|
||||
if (age > 10800 and knownNodes[stream][node]["rating"] <=
|
||||
knownNodesForgetRating):
|
||||
needToWriteKnownNodesToDisk = True
|
||||
del knownNodes[stream][node]
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
"""
|
||||
src/messagetypes/__init__.py
|
||||
============================
|
||||
"""
|
||||
from importlib import import_module
|
||||
from os import path, listdir
|
||||
from string import lower
|
||||
|
@ -6,12 +10,15 @@ from debug import logger
|
|||
import messagetypes
|
||||
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__)}
|
||||
|
||||
|
||||
def constructObject(data):
|
||||
"""Constructing an object"""
|
||||
whitelist = ["message"]
|
||||
if data[""] not in whitelist:
|
||||
return None
|
||||
|
@ -32,6 +39,7 @@ def constructObject(data):
|
|||
else:
|
||||
return returnObj
|
||||
|
||||
|
||||
if paths.frozen is not None:
|
||||
import messagetypes.message
|
||||
import messagetypes.vote
|
||||
|
|
|
@ -1,24 +1,30 @@
|
|||
"""
|
||||
src/messagetypes/message.py
|
||||
===========================
|
||||
"""
|
||||
from debug import logger
|
||||
from messagetypes import MsgBase
|
||||
|
||||
|
||||
class Message(MsgBase):
|
||||
def __init__(self):
|
||||
return
|
||||
"""Encapsulate a message"""
|
||||
# pylint: disable=attribute-defined-outside-init
|
||||
|
||||
def decode(self, data):
|
||||
"""Decode a message"""
|
||||
# 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')
|
||||
else:
|
||||
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')
|
||||
else:
|
||||
self.body = unicode(str(data["body"]), 'utf-8', 'replace')
|
||||
|
||||
def encode(self, data):
|
||||
super(Message, self).encode()
|
||||
"""Encode a message"""
|
||||
super(Message, self).__init__()
|
||||
try:
|
||||
self.data["subject"] = data["subject"]
|
||||
self.data["body"] = data["body"]
|
||||
|
@ -27,5 +33,6 @@ class Message(MsgBase):
|
|||
return self.data
|
||||
|
||||
def process(self):
|
||||
"""Process a message"""
|
||||
logger.debug("Subject: %i bytes", len(self.subject))
|
||||
logger.debug("Body: %i bytes", len(self.body))
|
||||
|
|
|
@ -1,23 +1,31 @@
|
|||
"""
|
||||
src/messagetypes/vote.py
|
||||
========================
|
||||
"""
|
||||
from debug import logger
|
||||
from messagetypes import MsgBase
|
||||
|
||||
|
||||
class Vote(MsgBase):
|
||||
def __init__(self):
|
||||
return
|
||||
"""Module used to vote"""
|
||||
|
||||
def decode(self, data):
|
||||
"""decode a vote"""
|
||||
# pylint: disable=attribute-defined-outside-init
|
||||
self.msgid = data["msgid"]
|
||||
self.vote = data["vote"]
|
||||
|
||||
def encode(self, data):
|
||||
super(Vote, self).encode()
|
||||
"""Encode a vote"""
|
||||
super(Vote, self).__init__()
|
||||
try:
|
||||
self.data["msgid"] = data["msgid"]
|
||||
self.data["vote"] = data["vote"]
|
||||
except KeyError as e:
|
||||
logger.error("Missing key %s", e.name)
|
||||
logger.error("Missing key %s", e)
|
||||
return self.data
|
||||
|
||||
def process(self):
|
||||
"""Encode a vote"""
|
||||
logger.debug("msgid: %s", self.msgid)
|
||||
logger.debug("vote: %s", self.vote)
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# pylint: disable=too-many-branches
|
||||
import random # nosec
|
||||
|
||||
import knownnodes
|
||||
|
@ -38,7 +39,10 @@ def chooseConnection(stream):
|
|||
for _ in range(50):
|
||||
peer = random.choice(knownnodes.knownNodes[stream].keys())
|
||||
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:
|
||||
logger.warning('Error in %s', peer)
|
||||
rating = 0
|
||||
|
@ -46,7 +50,8 @@ def chooseConnection(stream):
|
|||
# onion addresses have a higher priority when SOCKS
|
||||
if peer.host.endswith('.onion') and rating > 0:
|
||||
rating = 1
|
||||
else:
|
||||
# TODO: need better check
|
||||
elif not peer.host.startswith('bootstrap'):
|
||||
encodedAddr = protocol.encodeHost(peer.host)
|
||||
# don't connect to local IPs when using SOCKS
|
||||
if not protocol.checkIPAddress(encodedAddr, False):
|
||||
|
|
|
@ -8,7 +8,6 @@ import socket
|
|||
import time
|
||||
|
||||
import asyncore_pollchoose as asyncore
|
||||
import helper_bootstrap
|
||||
import helper_random
|
||||
import knownnodes
|
||||
import protocol
|
||||
|
@ -19,7 +18,8 @@ from debug import logger
|
|||
from proxy import Proxy
|
||||
from singleton import Singleton
|
||||
from tcp import (
|
||||
TCPServer, Socks5BMConnection, Socks4aBMConnection, TCPConnection)
|
||||
bootstrap, Socks4aBMConnection, Socks5BMConnection,
|
||||
TCPConnection, TCPServer)
|
||||
from udp import UDPSocket
|
||||
|
||||
|
||||
|
@ -160,7 +160,35 @@ class BMConnectionPool(object):
|
|||
udpSocket = UDPSocket(host=bind, announcing=True)
|
||||
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"""
|
||||
# defaults to empty loop if outbound connections are maxed
|
||||
spawnConnections = False
|
||||
|
@ -185,7 +213,8 @@ class BMConnectionPool(object):
|
|||
# pylint: disable=too-many-nested-blocks
|
||||
if spawnConnections:
|
||||
if not knownnodes.knownNodesActual:
|
||||
helper_bootstrap.dns()
|
||||
self.startBootstrappers()
|
||||
knownnodes.knownNodesActual = True
|
||||
if not self.bootstrapped:
|
||||
self.bootstrapped = True
|
||||
Proxy.proxy = (
|
||||
|
|
|
@ -8,6 +8,7 @@ src/network/socks5.py
|
|||
import socket
|
||||
import struct
|
||||
|
||||
import state
|
||||
from proxy import GeneralProxyError, Proxy, ProxyError
|
||||
|
||||
|
||||
|
@ -160,9 +161,6 @@ class Socks5(Proxy):
|
|||
|
||||
class Socks5Connection(Socks5):
|
||||
"""Child socks5 class used for making outbound connections."""
|
||||
def __init__(self, address):
|
||||
Socks5.__init__(self, address=address)
|
||||
|
||||
def state_auth_done(self):
|
||||
"""Request connection to be made"""
|
||||
# Now we can request the actual connection
|
||||
|
@ -172,9 +170,9 @@ class Socks5Connection(Socks5):
|
|||
try:
|
||||
self.ipaddr = socket.inet_aton(self.destination[0])
|
||||
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.
|
||||
if Proxy._remote_dns: # pylint: disable=protected-access
|
||||
if self._remote_dns:
|
||||
# Resolve remotely
|
||||
self.ipaddr = None
|
||||
self.append_write_buf(chr(0x03).encode() + chr(
|
||||
|
@ -202,7 +200,7 @@ class Socks5Resolver(Socks5):
|
|||
def __init__(self, host):
|
||||
self.host = host
|
||||
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):
|
||||
"""Perform resolving"""
|
||||
|
|
|
@ -73,11 +73,14 @@ class TCPConnection(BMProto, TLSDispatcher):
|
|||
logger.debug(
|
||||
'Connecting to %s:%i',
|
||||
self.destination.host, self.destination.port)
|
||||
encodedAddr = protocol.encodeHost(self.destination.host)
|
||||
self.local = all([
|
||||
protocol.checkIPAddress(encodedAddr, True),
|
||||
not protocol.checkSocksIP(self.destination.host)
|
||||
])
|
||||
try:
|
||||
self.local = (
|
||||
protocol.checkIPAddress(
|
||||
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
|
||||
self.bm_proto_reset()
|
||||
self.set_state("bm_header", expectBytes=protocol.Header.size)
|
||||
|
@ -322,6 +325,39 @@ class Socks4aBMConnection(Socks4aConnection, TCPConnection):
|
|||
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):
|
||||
"""TCP connection server for Bitmessage protocol"""
|
||||
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
src/plugins/indicator_libmessaging.py
|
||||
=====================================
|
||||
"""
|
||||
|
||||
import gi
|
||||
gi.require_version('MessagingMenu', '1.0') # noqa:E402
|
||||
|
@ -9,6 +13,7 @@ from pybitmessage.tr import _translate
|
|||
|
||||
|
||||
class IndicatorLibmessaging(object):
|
||||
"""Plugin for libmessage indicator"""
|
||||
def __init__(self, form):
|
||||
try:
|
||||
self.app = MessagingMenu.App(desktop_id='pybitmessage.desktop')
|
||||
|
@ -32,15 +37,18 @@ class IndicatorLibmessaging(object):
|
|||
if self.app:
|
||||
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.new_message_item if source == 'messages'
|
||||
else self.new_broadcast_item
|
||||
)
|
||||
|
||||
# show the number of unread messages and subscriptions
|
||||
# on the messaging menu
|
||||
def show_unread(self, draw_attention=False):
|
||||
"""
|
||||
show the number of unread messages and subscriptions
|
||||
on the messaging menu
|
||||
"""
|
||||
for source, count in zip(
|
||||
('messages', 'subscriptions'),
|
||||
self.form.getUnread()
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
src/plugins/menu_qrcode.py
|
||||
==========================
|
||||
|
||||
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
|
||||
class Image(qrcode.image.base.BaseImage):
|
||||
class Image(qrcode.image.base.BaseImage): # pylint: disable=abstract-method
|
||||
"""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.width = width
|
||||
self.box_size = box_size
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
src/plugins/notification_notify2.py
|
||||
===================================
|
||||
"""
|
||||
|
||||
import gi
|
||||
gi.require_version('Notify', '0.7')
|
||||
|
@ -6,10 +10,13 @@ from gi.repository import Notify
|
|||
|
||||
Notify.init('pybitmessage')
|
||||
|
||||
|
||||
def connect_plugin(title, subtitle, category, label, icon):
|
||||
"""Plugin for notify2"""
|
||||
if not icon:
|
||||
icon = 'mail-message-new' if category == 2 else 'pybitmessage'
|
||||
connect_plugin.notification.update(title, subtitle, icon)
|
||||
connect_plugin.notification.show()
|
||||
|
||||
|
||||
connect_plugin.notification = Notify.Notification.new("Init", "Init")
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
src/plugins/plugin.py
|
||||
===================================
|
||||
"""
|
||||
import pkg_resources
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
src/plugins/proxyconfig_stem.py
|
||||
===================================
|
||||
"""
|
||||
import os
|
||||
import logging
|
||||
import random # noseq
|
||||
|
@ -8,6 +11,7 @@ import tempfile
|
|||
import stem
|
||||
import stem.control
|
||||
import stem.process
|
||||
import stem.version
|
||||
|
||||
|
||||
class DebugLogger(object):
|
||||
|
@ -28,14 +32,14 @@ class DebugLogger(object):
|
|||
# Plugin's debug or unexpected log line from tor
|
||||
self._logger.debug(line)
|
||||
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"""
|
||||
logwrite = DebugLogger()
|
||||
if config.safeGet('bitmessagesettings', 'sockshostname') not in (
|
||||
'localhost', '127.0.0.1', ''
|
||||
'localhost', '127.0.0.1', ''
|
||||
):
|
||||
# remote proxy is choosen for outbound connections,
|
||||
# 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.
|
||||
try:
|
||||
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:
|
||||
if not attempt:
|
||||
try:
|
||||
stem.version.get_system_tor_version()
|
||||
except IOError:
|
||||
return
|
||||
continue
|
||||
else:
|
||||
logwrite('Started tor on port %s' % port)
|
||||
|
@ -108,3 +118,5 @@ def connect_plugin(config):
|
|||
onionhostname, 'keytype', response.private_key_type)
|
||||
config.save()
|
||||
config.set('bitmessagesettings', 'socksproxytype', 'SOCKS5')
|
||||
|
||||
return True
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
src/plugins/proxyconfig_stem.py
|
||||
===================================
|
||||
"""
|
||||
|
||||
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:
|
||||
_canberra.play(0, pycanberra.CA_PROP_EVENT_ID, _theme[category], None)
|
||||
except (KeyError, pycanberra.CanberraException):
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
src/plugins/sound_gstreamer.py
|
||||
===================================
|
||||
"""
|
||||
import gi
|
||||
gi.require_version('Gst', '1.0')
|
||||
from gi.repository import Gst # noqa: E402
|
||||
|
@ -9,6 +12,7 @@ _player = Gst.ElementFactory.make("playbin", "player")
|
|||
|
||||
|
||||
def connect_plugin(sound_file):
|
||||
"""Entry point for sound file"""
|
||||
_player.set_state(Gst.State.NULL)
|
||||
_player.set_property("uri", "file://" + sound_file)
|
||||
_player.set_state(Gst.State.PLAYING)
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
src/plugins/sound_playfile.py
|
||||
===================================
|
||||
"""
|
||||
|
||||
try:
|
||||
import winsound
|
||||
|
||||
def connect_plugin(sound_file):
|
||||
"""Plugin's entry point"""
|
||||
winsound.PlaySound(sound_file, winsound.SND_FILENAME)
|
||||
except ImportError:
|
||||
import os
|
||||
|
@ -18,7 +22,8 @@ except ImportError:
|
|||
args, stdout=FNULL, stderr=subprocess.STDOUT, close_fds=True)
|
||||
|
||||
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]
|
||||
try:
|
||||
|
|
|
@ -264,7 +264,10 @@ def assembleVersionMessage(remoteHost, remotePort, participatingStreams, server=
|
|||
else:
|
||||
# use first 16 bytes if host data is longer
|
||||
# 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
|
||||
|
||||
# bitflags of the services I offer.
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
"""
|
||||
src/pyelliptic/__init__.py
|
||||
=====================================
|
||||
"""
|
||||
# Copyright (C) 2010
|
||||
# Author: Yann GUIBET
|
||||
# Contact: <yannguibet@gmail.com>
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
src/pyelliptic/cipher.py
|
||||
========================
|
||||
"""
|
||||
|
||||
# Copyright (C) 2011 Yann GUIBET <yannguibet@gmail.com>
|
||||
# See LICENSE for details.
|
||||
|
@ -7,7 +11,8 @@
|
|||
from openssl import OpenSSL
|
||||
|
||||
|
||||
class Cipher:
|
||||
# pylint: disable=redefined-builtin
|
||||
class Cipher(object):
|
||||
"""
|
||||
Symmetric encryption
|
||||
|
||||
|
@ -44,30 +49,34 @@ class Cipher:
|
|||
|
||||
@staticmethod
|
||||
def get_blocksize(ciphername):
|
||||
"""This Method returns cipher blocksize"""
|
||||
cipher = OpenSSL.get_cipher(ciphername)
|
||||
return cipher.get_blocksize()
|
||||
|
||||
@staticmethod
|
||||
def gen_IV(ciphername):
|
||||
"""Generate random initialization vector"""
|
||||
cipher = OpenSSL.get_cipher(ciphername)
|
||||
return OpenSSL.rand(cipher.get_blocksize())
|
||||
|
||||
def update(self, input):
|
||||
"""Update result with more data"""
|
||||
i = OpenSSL.c_int(0)
|
||||
buffer = OpenSSL.malloc(b"", len(input) + self.cipher.get_blocksize())
|
||||
inp = OpenSSL.malloc(input, len(input))
|
||||
if OpenSSL.EVP_CipherUpdate(self.ctx, OpenSSL.byref(buffer),
|
||||
OpenSSL.byref(i), inp, len(input)) == 0:
|
||||
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):
|
||||
"""Returning the final value"""
|
||||
i = OpenSSL.c_int(0)
|
||||
buffer = OpenSSL.malloc(b"", self.cipher.get_blocksize())
|
||||
if (OpenSSL.EVP_CipherFinal_ex(self.ctx, OpenSSL.byref(buffer),
|
||||
OpenSSL.byref(i))) == 0:
|
||||
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):
|
||||
"""
|
||||
|
@ -77,6 +86,7 @@ class Cipher:
|
|||
return buff + self.final()
|
||||
|
||||
def __del__(self):
|
||||
# pylint: disable=protected-access
|
||||
if OpenSSL._hexversion > 0x10100000 and not OpenSSL._libreSSL:
|
||||
OpenSSL.EVP_CIPHER_CTX_reset(self.ctx)
|
||||
else:
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
src/pyelliptic/hash.py
|
||||
=====================
|
||||
"""
|
||||
# Copyright (C) 2011 Yann GUIBET <yannguibet@gmail.com>
|
||||
# See LICENSE for details.
|
||||
|
||||
|
@ -27,10 +30,10 @@ def _equals_str(a, b):
|
|||
|
||||
|
||||
def equals(a, b):
|
||||
"""Compare two strings or bytearrays"""
|
||||
if isinstance(a, str):
|
||||
return _equals_str(a, b)
|
||||
else:
|
||||
return _equals_bytes(a, b)
|
||||
return _equals_bytes(a, b)
|
||||
|
||||
|
||||
def hmac_sha256(k, m):
|
||||
|
@ -58,6 +61,7 @@ def hmac_sha512(k, m):
|
|||
|
||||
|
||||
def pbkdf2(password, salt=None, i=10000, keylen=64):
|
||||
"""Key derivation function using SHA256"""
|
||||
if salt is None:
|
||||
salt = OpenSSL.rand(8)
|
||||
p_password = OpenSSL.malloc(password, len(password))
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
src/pyelliptic/openssl.py
|
||||
=====================
|
||||
"""
|
||||
# Copyright (C) 2011 Yann GUIBET <yannguibet@gmail.com>
|
||||
# See LICENSE for details.
|
||||
#
|
||||
# Software slightly changed by Jonathan Warren <bitmessage at-symbol jonwarren.org>
|
||||
# pylint: disable=protected-access
|
||||
|
||||
import sys
|
||||
import ctypes
|
||||
|
@ -13,6 +17,9 @@ OpenSSL = None
|
|||
from kivy.utils import platform
|
||||
|
||||
class CipherName:
|
||||
"""Class returns cipher name, pointer and blocksize"""
|
||||
|
||||
# pylint: disable=old-style-class
|
||||
def __init__(self, name, pointer, blocksize):
|
||||
self._name = name
|
||||
self._pointer = pointer
|
||||
|
@ -24,16 +31,20 @@ class CipherName:
|
|||
" | Function pointer : " + str(self._pointer)
|
||||
|
||||
def get_pointer(self):
|
||||
"""This method returns cipher pointer"""
|
||||
return self._pointer()
|
||||
|
||||
def get_name(self):
|
||||
"""This method returns cipher name"""
|
||||
return self._name
|
||||
|
||||
def get_blocksize(self):
|
||||
"""This method returns cipher blocksize"""
|
||||
return self._blocksize
|
||||
|
||||
|
||||
def get_version(library):
|
||||
"""This function return version, hexversion and cflages"""
|
||||
version = None
|
||||
hexversion = None
|
||||
cflags = None
|
||||
|
@ -68,6 +79,7 @@ class _OpenSSL:
|
|||
"""
|
||||
Wrapper for OpenSSL using ctypes
|
||||
"""
|
||||
# pylint: disable=too-many-statements, too-many-instance-attributes, old-style-class
|
||||
def __init__(self, library):
|
||||
"""
|
||||
Build the wrapper
|
||||
|
@ -594,6 +606,7 @@ class _OpenSSL:
|
|||
"""
|
||||
returns the name of a elliptic curve with his id
|
||||
"""
|
||||
# pylint: disable=redefined-builtin
|
||||
res = None
|
||||
for i in self.curves:
|
||||
if self.curves[i] == id:
|
||||
|
@ -607,6 +620,7 @@ class _OpenSSL:
|
|||
"""
|
||||
OpenSSL random function
|
||||
"""
|
||||
# pylint: disable=redefined-builtin
|
||||
buffer = self.malloc(0, size)
|
||||
# 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
|
||||
|
@ -623,6 +637,7 @@ class _OpenSSL:
|
|||
"""
|
||||
returns a create_string_buffer (ctypes)
|
||||
"""
|
||||
# pylint: disable=redefined-builtin
|
||||
buffer = None
|
||||
if data != 0:
|
||||
if sys.version_info.major == 3 and isinstance(data, type('')):
|
||||
|
@ -634,6 +649,8 @@ class _OpenSSL:
|
|||
|
||||
|
||||
def loadOpenSSL():
|
||||
"""This function finds and load the OpenSSL library"""
|
||||
# pylint: disable=global-statement
|
||||
global OpenSSL
|
||||
from os import path, environ
|
||||
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
|
||||
socksIP = None
|
||||
|
||||
# Network protocols availability, initialised below
|
||||
networkProtocolAvailability = None
|
||||
|
||||
appdata = '' # holds the location of the application data storage directory
|
||||
|
||||
# Set to 1 by the doCleanShutdown function.
|
||||
|
@ -54,14 +51,6 @@ discoveredPeers = {}
|
|||
|
||||
Peer = collections.namedtuple('Peer', ['host', 'port'])
|
||||
|
||||
|
||||
def resetNetworkProtocolAvailability():
|
||||
global networkProtocolAvailability
|
||||
networkProtocolAvailability = {'IPv4': None, 'IPv6': None, 'onion': None}
|
||||
|
||||
|
||||
resetNetworkProtocolAvailability()
|
||||
|
||||
dandelion = 0
|
||||
|
||||
testmode = False
|
||||
|
|
|
@ -1,21 +1,26 @@
|
|||
"""
|
||||
src/storage/filesystem.py
|
||||
=========================
|
||||
"""
|
||||
from binascii import hexlify, unhexlify
|
||||
from os import listdir, makedirs, path, remove, rmdir
|
||||
import string
|
||||
from threading import RLock
|
||||
import time
|
||||
import traceback
|
||||
|
||||
from paths import lookupAppdataFolder
|
||||
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"
|
||||
objectDir = "objects"
|
||||
metadataFilename = "metadata"
|
||||
dataFilename = "data"
|
||||
|
||||
def __init__(self):
|
||||
super(self.__class__, self).__init__()
|
||||
super(FilesystemInventory, self).__init__()
|
||||
self.baseDir = path.join(lookupAppdataFolder(), FilesystemInventory.topDir)
|
||||
for createDir in [self.baseDir, path.join(self.baseDir, "objects")]:
|
||||
if path.exists(createDir):
|
||||
|
@ -23,72 +28,101 @@ class FilesystemInventory(InventoryStorage):
|
|||
raise IOError("%s exists but it's not a directory" % (createDir))
|
||||
else:
|
||||
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._load()
|
||||
|
||||
def __contains__(self, hash):
|
||||
def __contains__(self, hashval):
|
||||
retval = False
|
||||
for streamDict in self._inventory.values():
|
||||
if hash in streamDict:
|
||||
if hashval in streamDict:
|
||||
return True
|
||||
return False
|
||||
|
||||
def __getitem__(self, hash):
|
||||
def __getitem__(self, hashval):
|
||||
for streamDict in self._inventory.values():
|
||||
try:
|
||||
retval = streamDict[hash]
|
||||
retval = streamDict[hashval]
|
||||
except KeyError:
|
||||
continue
|
||||
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
|
||||
raise KeyError(hash)
|
||||
raise KeyError(hashval)
|
||||
|
||||
def __setitem__(self, hash, value):
|
||||
def __setitem__(self, hashval, value):
|
||||
with self.lock:
|
||||
value = InventoryItem(*value)
|
||||
try:
|
||||
makedirs(path.join(self.baseDir, FilesystemInventory.objectDir, hexlify(hash)))
|
||||
makedirs(path.join(self.baseDir, FilesystemInventory.objectDir, hexlify(hashval)))
|
||||
except OSError:
|
||||
pass
|
||||
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)))
|
||||
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)
|
||||
except IOError:
|
||||
raise KeyError
|
||||
try:
|
||||
self._inventory[value.stream][hash] = value
|
||||
self._inventory[value.stream][hashval] = value
|
||||
except KeyError:
|
||||
self._inventory[value.stream] = {}
|
||||
self._inventory[value.stream][hash] = value
|
||||
self._inventory[value.stream][hashval] = value
|
||||
|
||||
def delHashId(self, hash):
|
||||
for stream in self._inventory.keys():
|
||||
def delHashId(self, hashval):
|
||||
"""Remove object from inventory"""
|
||||
for stream in self._inventory:
|
||||
try:
|
||||
del self._inventory[stream][hash]
|
||||
del self._inventory[stream][hashval]
|
||||
except KeyError:
|
||||
pass
|
||||
with self.lock:
|
||||
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:
|
||||
pass
|
||||
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:
|
||||
pass
|
||||
try:
|
||||
rmdir(path.join(self.baseDir, FilesystemInventory.objectDir, hexlify(hash)))
|
||||
rmdir(path.join(self.baseDir, FilesystemInventory.objectDir, hexlify(hashval)))
|
||||
except IOError:
|
||||
pass
|
||||
|
||||
def __iter__(self):
|
||||
elems = []
|
||||
for streamDict in self._inventory.values():
|
||||
elems.extend (streamDict.keys())
|
||||
elems.extend(streamDict.keys())
|
||||
return elems.__iter__()
|
||||
|
||||
def __len__(self):
|
||||
|
@ -103,44 +137,66 @@ class FilesystemInventory(InventoryStorage):
|
|||
try:
|
||||
objectType, streamNumber, expiresTime, tag = self.getMetadata(hashId)
|
||||
try:
|
||||
newInventory[streamNumber][hashId] = InventoryItem(objectType, streamNumber, None, expiresTime, tag)
|
||||
newInventory[streamNumber][hashId] = InventoryItem(
|
||||
objectType, streamNumber, None, expiresTime, tag)
|
||||
except KeyError:
|
||||
newInventory[streamNumber] = {}
|
||||
newInventory[streamNumber][hashId] = InventoryItem(objectType, streamNumber, None, expiresTime, tag)
|
||||
newInventory[streamNumber][hashId] = InventoryItem(
|
||||
objectType, streamNumber, None, expiresTime, tag)
|
||||
except KeyError:
|
||||
print "error loading %s" % (hexlify(hashId))
|
||||
pass
|
||||
self._inventory = newInventory
|
||||
# for i, v in self._inventory.items():
|
||||
# print "loaded stream: %s, %i items" % (i, len(v))
|
||||
|
||||
def stream_list(self):
|
||||
"""Return list of streams"""
|
||||
return self._inventory.keys()
|
||||
|
||||
def object_list(self):
|
||||
"""Return inventory vectors (hashes) from a directory"""
|
||||
return [unhexlify(x) for x in listdir(path.join(self.baseDir, FilesystemInventory.objectDir))]
|
||||
|
||||
def getData(self, hashId):
|
||||
"""Get object data"""
|
||||
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()
|
||||
except IOError:
|
||||
raise AttributeError
|
||||
|
||||
def getMetadata(self, hashId):
|
||||
"""Get object metadata"""
|
||||
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)
|
||||
return [int(objectType), int(streamNumber), int(expiresTime), unhexlify(tag)]
|
||||
except IOError:
|
||||
raise KeyError
|
||||
|
||||
def by_type_and_tag(self, objectType, tag):
|
||||
"""Get a list of objects filtered by object type and tag"""
|
||||
retval = []
|
||||
for stream, streamDict in self._inventory:
|
||||
for hashId, item in streamDict:
|
||||
if item.type == objectType and item.tag == tag:
|
||||
try:
|
||||
try:
|
||||
if item.payload is None:
|
||||
item.payload = self.getData(hashId)
|
||||
except IOError:
|
||||
|
@ -149,12 +205,14 @@ class FilesystemInventory(InventoryStorage):
|
|||
return retval
|
||||
|
||||
def hashes_by_stream(self, stream):
|
||||
"""Return inventory vectors (hashes) for a stream"""
|
||||
try:
|
||||
return self._inventory[stream].keys()
|
||||
except KeyError:
|
||||
return []
|
||||
|
||||
def unexpired_hashes_by_stream(self, stream):
|
||||
"""Return unexpired hashes in the inventory for a particular stream"""
|
||||
t = int(time.time())
|
||||
try:
|
||||
return [x for x, value in self._inventory[stream].items() if value.expires > t]
|
||||
|
@ -162,9 +220,11 @@ class FilesystemInventory(InventoryStorage):
|
|||
return []
|
||||
|
||||
def flush(self):
|
||||
"""Flush the inventory and create a new, empty one"""
|
||||
self._load()
|
||||
|
||||
def clean(self):
|
||||
"""Clean out old items from the inventory"""
|
||||
minTime = int(time.time()) - (60 * 60 * 30)
|
||||
deletes = []
|
||||
for stream, streamDict in self._inventory.items():
|
||||
|
|
|
@ -1,45 +1,59 @@
|
|||
import collections
|
||||
from threading import current_thread, enumerate as threadingEnumerate, RLock
|
||||
import Queue
|
||||
"""
|
||||
src/storage/sqlite.py
|
||||
=========================
|
||||
"""
|
||||
import sqlite3
|
||||
import time
|
||||
from threading import RLock
|
||||
|
||||
from helper_sql import *
|
||||
from helper_sql import sqlQuery, SqlBulkExecute, sqlExecute
|
||||
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:
|
||||
if hash in self._objects:
|
||||
if hash_ in self._objects:
|
||||
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:
|
||||
return False
|
||||
self._objects[hash] = rows[0][0]
|
||||
self._objects[hash_] = rows[0][0]
|
||||
return True
|
||||
|
||||
def __getitem__(self, hash):
|
||||
def __getitem__(self, hash_):
|
||||
with self.lock:
|
||||
if hash in self._inventory:
|
||||
return self._inventory[hash]
|
||||
rows = sqlQuery('SELECT objecttype, streamnumber, payload, expirestime, tag FROM inventory WHERE hash=?', sqlite3.Binary(hash))
|
||||
if hash_ in self._inventory:
|
||||
return self._inventory[hash_]
|
||||
rows = sqlQuery(
|
||||
'SELECT objecttype, streamnumber, payload, expirestime, tag FROM inventory WHERE hash=?',
|
||||
sqlite3.Binary(hash_))
|
||||
if not rows:
|
||||
raise KeyError(hash)
|
||||
raise KeyError(hash_)
|
||||
return InventoryItem(*rows[0])
|
||||
|
||||
def __setitem__(self, hash, value):
|
||||
def __setitem__(self, hash_, value):
|
||||
with self.lock:
|
||||
value = InventoryItem(*value)
|
||||
self._inventory[hash] = value
|
||||
self._objects[hash] = value.stream
|
||||
self._inventory[hash_] = value
|
||||
self._objects[hash_] = value.stream
|
||||
|
||||
def __delitem__(self, hash):
|
||||
def __delitem__(self, hash_):
|
||||
raise NotImplementedError
|
||||
|
||||
def __iter__(self):
|
||||
|
@ -55,18 +69,22 @@ class SqliteInventory(InventoryStorage):
|
|||
def by_type_and_tag(self, objectType, tag):
|
||||
with self.lock:
|
||||
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
|
||||
|
||||
def unexpired_hashes_by_stream(self, stream):
|
||||
with self.lock:
|
||||
t = int(time.time())
|
||||
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
|
||||
|
||||
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:
|
||||
for objectHash, value in self._inventory.items():
|
||||
sql.execute('INSERT INTO inventory VALUES (?, ?, ?, ?, ?, ?)', sqlite3.Binary(objectHash), *value)
|
||||
|
@ -74,8 +92,7 @@ class SqliteInventory(InventoryStorage):
|
|||
|
||||
def clean(self):
|
||||
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()
|
||||
for objectHash, value in self._inventory.items():
|
||||
self._objects[objectHash] = value.stream
|
||||
|
||||
|
|
|
@ -1,27 +1,33 @@
|
|||
"""
|
||||
src/storage/storage.py
|
||||
======================
|
||||
"""
|
||||
import collections
|
||||
|
||||
InventoryItem = collections.namedtuple('InventoryItem', 'type stream payload expires tag')
|
||||
|
||||
|
||||
class Storage(object):
|
||||
"""Base class for storing inventory (extendable for other items to store)"""
|
||||
pass
|
||||
# def __init__(self):
|
||||
# super(self.__class__, self).__init__()
|
||||
|
||||
|
||||
class InventoryStorage(Storage, collections.MutableMapping):
|
||||
"""Module used for inventory storage"""
|
||||
def __init__(self):
|
||||
# super(self.__class__, self).__init__()
|
||||
# pylint: disable=super-init-not-called
|
||||
self.numberOfInventoryLookupsPerformed = 0
|
||||
|
||||
def __contains__(self, hash):
|
||||
def __contains__(self, _):
|
||||
raise NotImplementedError
|
||||
|
||||
def __getitem__(self, hash):
|
||||
def __getitem__(self, _):
|
||||
raise NotImplementedError
|
||||
|
||||
def __setitem__(self, hash, value):
|
||||
def __setitem__(self, _, value):
|
||||
raise NotImplementedError
|
||||
|
||||
def __delitem__(self, hash):
|
||||
def __delitem__(self, _):
|
||||
raise NotImplementedError
|
||||
|
||||
def __iter__(self):
|
||||
|
@ -31,18 +37,24 @@ class InventoryStorage(Storage, collections.MutableMapping):
|
|||
raise NotImplementedError
|
||||
|
||||
def by_type_and_tag(self, objectType, tag):
|
||||
"""Return objects filtered by object type and tag"""
|
||||
raise NotImplementedError
|
||||
|
||||
def unexpired_hashes_by_stream(self, stream):
|
||||
"""Return unexpired inventory vectors filtered by stream"""
|
||||
raise NotImplementedError
|
||||
|
||||
def flush(self):
|
||||
"""Flush cache"""
|
||||
raise NotImplementedError
|
||||
|
||||
def clean(self):
|
||||
"""Free memory / perform garbage collection"""
|
||||
raise NotImplementedError
|
||||
|
||||
class MailboxStorage(Storage, collections.MutableMapping):
|
||||
|
||||
class MailboxStorage(Storage, collections.MutableMapping): # pylint: disable=abstract-method
|
||||
"""Method for storing mails"""
|
||||
def __init__(self):
|
||||
# super(self.__class__, self).__init__()
|
||||
# pylint: disable=super-init-not-called
|
||||
pass
|
||||
|
|
|
@ -13,15 +13,19 @@ import unittest
|
|||
|
||||
import knownnodes
|
||||
import state
|
||||
from bmconfigparser import BMConfigParser
|
||||
from helper_msgcoding import MsgEncode, MsgDecode
|
||||
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
|
||||
|
||||
knownnodes_file = os.path.join(state.appdata, 'knownnodes.dat')
|
||||
program = None
|
||||
|
||||
|
||||
def pickle_knownnodes():
|
||||
"""Generate old style pickled knownnodes.dat"""
|
||||
now = time.time()
|
||||
with open(knownnodes_file, 'wb') as dst:
|
||||
pickle.dump({
|
||||
|
@ -37,6 +41,7 @@ def pickle_knownnodes():
|
|||
|
||||
|
||||
def cleanup():
|
||||
"""Cleanup application files"""
|
||||
os.remove(knownnodes_file)
|
||||
|
||||
|
||||
|
@ -80,8 +85,10 @@ class TestCore(unittest.TestCase):
|
|||
' with no subject!' % e
|
||||
)
|
||||
|
||||
@unittest.skip('Bad environment for asyncore.loop')
|
||||
def test_tcpconnection(self):
|
||||
"""initial fill script from network.tcp"""
|
||||
BMConfigParser().set('bitmessagesettings', 'dontconnect', 'true')
|
||||
try:
|
||||
for peer in (state.Peer("127.0.0.1", 8448),):
|
||||
direct = TCPConnection(peer)
|
||||
|
@ -91,10 +98,18 @@ class TestCore(unittest.TestCase):
|
|||
except:
|
||||
self.fail('Exception in test loop')
|
||||
|
||||
def _wipe_knownnodes(self):
|
||||
@staticmethod
|
||||
def _wipe_knownnodes():
|
||||
with knownnodes.knownNodesLock:
|
||||
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):
|
||||
"""ensure that 3 nodes was imported for each stream"""
|
||||
pickle_knownnodes()
|
||||
|
@ -117,11 +132,10 @@ class TestCore(unittest.TestCase):
|
|||
|
||||
def test_0_cleaner(self):
|
||||
"""test knownnodes starvation leading to IndexError in Asyncore"""
|
||||
for nodes in knownnodes.knownNodes.itervalues():
|
||||
for node in nodes.itervalues():
|
||||
node['lastseen'] -= 2419205 # older than 28 days
|
||||
self._outdate_knownnodes()
|
||||
# time.sleep(303) # singleCleaner wakes up every 5 min
|
||||
knownnodes.cleanupKnownNodes()
|
||||
self.assertTrue(knownnodes.knownNodes[1])
|
||||
while True:
|
||||
try:
|
||||
thread, exc = excQueue.get(block=False)
|
||||
|
@ -130,9 +144,49 @@ class TestCore(unittest.TestCase):
|
|||
if thread == 'Asyncore' and isinstance(exc, IndexError):
|
||||
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"""
|
||||
global program # pylint: disable=global-statement
|
||||
program = prog
|
||||
loader = unittest.TestLoader()
|
||||
loader.sortTestMethodsUsing = None
|
||||
suite = loader.loadTestsFromTestCase(TestCore)
|
||||
|
|
|
@ -31,7 +31,7 @@ class TestAPIShutdown(TestAPIProto, TestProcessShutdown):
|
|||
"""Separate test case for API command 'shutdown'"""
|
||||
def test_shutdown(self):
|
||||
"""Shutdown the pybitmessage"""
|
||||
self.assertEquals(self.api.shutdown(), 'done')
|
||||
self.assertEqual(self.api.shutdown(), 'done')
|
||||
for _ in range(5):
|
||||
if not self.process.is_running():
|
||||
break
|
||||
|
|
|
@ -26,6 +26,7 @@ class TestConfig(unittest.TestCase):
|
|||
False
|
||||
)
|
||||
# no arg for default
|
||||
# pylint: disable=too-many-function-args
|
||||
with self.assertRaises(TypeError):
|
||||
BMConfigParser().safeGetBoolean(
|
||||
'nonexistent', 'nonexistent', True)
|
||||
|
@ -47,9 +48,9 @@ class TestProcessConfig(TestProcessProto):
|
|||
config = BMConfigParser()
|
||||
config.read(os.path.join(self.home, 'keys.dat'))
|
||||
|
||||
self.assertEquals(config.safeGetInt(
|
||||
self.assertEqual(config.safeGetInt(
|
||||
'bitmessagesettings', 'settingsversion'), 10)
|
||||
self.assertEquals(config.safeGetInt(
|
||||
self.assertEqual(config.safeGetInt(
|
||||
'bitmessagesettings', 'port'), 8444)
|
||||
# don't connect
|
||||
self.assertTrue(config.safeGetBoolean(
|
||||
|
@ -59,7 +60,7 @@ class TestProcessConfig(TestProcessProto):
|
|||
'bitmessagesettings', 'apienabled'))
|
||||
|
||||
# extralowdifficulty is false
|
||||
self.assertEquals(config.safeGetInt(
|
||||
self.assertEqual(config.safeGetInt(
|
||||
'bitmessagesettings', 'defaultnoncetrialsperbyte'), 1000)
|
||||
self.assertEquals(config.safeGetInt(
|
||||
self.assertEqual(config.safeGetInt(
|
||||
'bitmessagesettings', 'defaultpayloadlengthextrabytes'), 1000)
|
||||
|
|
Reference in New Issue
Block a user