From 3fd77ce214223afd6d26f50c37592441f5d4a732 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 19 Jan 2015 18:39:02 +0100 Subject: [PATCH 001/210] Merge OpenCL code and make OpenCL auto-detectable --- src/kernel.cl | 278 +++++++++++++++++++++++++++++++++++++++++++++ src/openclpow.py | 66 +++++++++++ src/proofofwork.py | 11 +- 3 files changed, 354 insertions(+), 1 deletion(-) create mode 100644 src/kernel.cl create mode 100644 src/openclpow.py diff --git a/src/kernel.cl b/src/kernel.cl new file mode 100644 index 00000000..d8db974a --- /dev/null +++ b/src/kernel.cl @@ -0,0 +1,278 @@ +/* +* This is based on the John The Ripper SHA512 code, modified for double SHA512 and for use as a miner in Bitmessage. +* This software is originally Copyright (c) 2012 Myrice +* and it is hereby released to the general public under the following terms: +* Redistribution and use in source and binary forms, with or without modification, are permitted. +*/ + +#ifdef cl_khr_byte_addressable_store +#pragma OPENCL EXTENSION cl_khr_byte_addressable_store : disable +#endif + +#define uint8_t unsigned char +#define uint32_t unsigned int +#define uint64_t unsigned long +#define SALT_SIZE 0 + +#define BINARY_SIZE 8 +#define FULL_BINARY_SIZE 64 + + +#define PLAINTEXT_LENGTH 72 + +#define CIPHERTEXT_LENGTH 128 + + +/// Warning: This version of SWAP64(n) is slow and avoid bugs on AMD GPUs(7970) +#define SWAP64(n) as_ulong(as_uchar8(n).s76543210) + +/* +#define SWAP64(n) \ + (((n) << 56) \ + | (((n) & 0xff00) << 40) \ + | (((n) & 0xff0000) << 24) \ + | (((n) & 0xff000000) << 8) \ + | (((n) >> 8) & 0xff000000) \ + | (((n) >> 24) & 0xff0000) \ + | (((n) >> 40) & 0xff00) \ + | ((n) >> 56)) +*/ + + + +#define rol(x,n) ((x << n) | (x >> (64-n))) +#define ror(x,n) ((x >> n) | (x << (64-n))) +#define Ch(x,y,z) ((x & y) ^ ( (~x) & z)) +#define Maj(x,y,z) ((x & y) ^ (x & z) ^ (y & z)) +#define Sigma0(x) ((ror(x,28)) ^ (ror(x,34)) ^ (ror(x,39))) +#define Sigma1(x) ((ror(x,14)) ^ (ror(x,18)) ^ (ror(x,41))) +#define sigma0(x) ((ror(x,1)) ^ (ror(x,8)) ^(x>>7)) +#define sigma1(x) ((ror(x,19)) ^ (ror(x,61)) ^(x>>6)) + + + +typedef struct { // notice memory align problem + uint64_t H[8]; + uint32_t buffer[32]; //1024 bits + uint32_t buflen; +} sha512_ctx; + +typedef struct { + uint64_t target; + char v[PLAINTEXT_LENGTH+1]; +} sha512_key; + + +/* Macros for reading/writing chars from int32's */ +#define PUTCHAR(buf, index, val) (buf)[(index)>>2] = ((buf)[(index)>>2] & ~(0xffU << (((index) & 3) << 3))) + ((val) << (((index) & 3) << 3)) + + +__constant uint64_t k[] = { + 0x428a2f98d728ae22UL, 0x7137449123ef65cdUL, 0xb5c0fbcfec4d3b2fUL, + 0xe9b5dba58189dbbcUL, + 0x3956c25bf348b538UL, 0x59f111f1b605d019UL, 0x923f82a4af194f9bUL, + 0xab1c5ed5da6d8118UL, + 0xd807aa98a3030242UL, 0x12835b0145706fbeUL, 0x243185be4ee4b28cUL, + 0x550c7dc3d5ffb4e2UL, + 0x72be5d74f27b896fUL, 0x80deb1fe3b1696b1UL, 0x9bdc06a725c71235UL, + 0xc19bf174cf692694UL, + 0xe49b69c19ef14ad2UL, 0xefbe4786384f25e3UL, 0x0fc19dc68b8cd5b5UL, + 0x240ca1cc77ac9c65UL, + 0x2de92c6f592b0275UL, 0x4a7484aa6ea6e483UL, 0x5cb0a9dcbd41fbd4UL, + 0x76f988da831153b5UL, + 0x983e5152ee66dfabUL, 0xa831c66d2db43210UL, 0xb00327c898fb213fUL, + 0xbf597fc7beef0ee4UL, + 0xc6e00bf33da88fc2UL, 0xd5a79147930aa725UL, 0x06ca6351e003826fUL, + 0x142929670a0e6e70UL, + 0x27b70a8546d22ffcUL, 0x2e1b21385c26c926UL, 0x4d2c6dfc5ac42aedUL, + 0x53380d139d95b3dfUL, + 0x650a73548baf63deUL, 0x766a0abb3c77b2a8UL, 0x81c2c92e47edaee6UL, + 0x92722c851482353bUL, + 0xa2bfe8a14cf10364UL, 0xa81a664bbc423001UL, 0xc24b8b70d0f89791UL, + 0xc76c51a30654be30UL, + 0xd192e819d6ef5218UL, 0xd69906245565a910UL, 0xf40e35855771202aUL, + 0x106aa07032bbd1b8UL, + 0x19a4c116b8d2d0c8UL, 0x1e376c085141ab53UL, 0x2748774cdf8eeb99UL, + 0x34b0bcb5e19b48a8UL, + 0x391c0cb3c5c95a63UL, 0x4ed8aa4ae3418acbUL, 0x5b9cca4f7763e373UL, + 0x682e6ff3d6b2b8a3UL, + 0x748f82ee5defb2fcUL, 0x78a5636f43172f60UL, 0x84c87814a1f0ab72UL, + 0x8cc702081a6439ecUL, + 0x90befffa23631e28UL, 0xa4506cebde82bde9UL, 0xbef9a3f7b2c67915UL, + 0xc67178f2e372532bUL, + 0xca273eceea26619cUL, 0xd186b8c721c0c207UL, 0xeada7dd6cde0eb1eUL, + 0xf57d4f7fee6ed178UL, + 0x06f067aa72176fbaUL, 0x0a637dc5a2c898a6UL, 0x113f9804bef90daeUL, + 0x1b710b35131c471bUL, + 0x28db77f523047d84UL, 0x32caab7b40c72493UL, 0x3c9ebe0a15c9bebcUL, + 0x431d67c49c100d4cUL, + 0x4cc5d4becb3e42b6UL, 0x597f299cfc657e2aUL, 0x5fcb6fab3ad6faecUL, + 0x6c44198c4a475817UL, +}; + + + +void setup_ctx(sha512_ctx* ctx, const char * password, uint8_t pass_len) +{ + uint32_t* b32 = ctx->buffer; + + //set password to buffer + for (uint32_t i = 0; i < pass_len; i++) { + PUTCHAR(b32,i,password[i]); + } + ctx->buflen = pass_len; + + //append 1 to ctx buffer + uint32_t length = ctx->buflen; + PUTCHAR(b32, length, 0x80); + while((++length & 3) != 0) { + PUTCHAR(b32, length, 0); + } + + uint32_t* buffer32 = b32+(length>>2); + for(uint32_t i = length; i < 128; i+=4) {// append 0 to 128 + *buffer32++=0; + } + + //append length to buffer + uint64_t *buffer64 = (uint64_t *)ctx->buffer; + buffer64[15] = SWAP64(((uint64_t) ctx->buflen) * 8); +} + +inline uint64_t sha512(char* password) +{ + __private sha512_ctx ctx; + setup_ctx(&ctx, password, 72); + // sha512 main` + int i; + + uint64_t a = 0x6a09e667f3bcc908UL; + uint64_t b = 0xbb67ae8584caa73bUL; + uint64_t c = 0x3c6ef372fe94f82bUL; + uint64_t d = 0xa54ff53a5f1d36f1UL; + uint64_t e = 0x510e527fade682d1UL; + uint64_t f = 0x9b05688c2b3e6c1fUL; + uint64_t g = 0x1f83d9abfb41bd6bUL; + uint64_t h = 0x5be0cd19137e2179UL; + + __private uint64_t w[16]; + + uint64_t *data = (uint64_t *) ctx.buffer; + + for (i = 0; i < 16; i++) + w[i] = SWAP64(data[i]); + + uint64_t t1, t2; + for (i = 0; i < 16; i++) { + t1 = k[i] + w[i] + h + Sigma1(e) + Ch(e, f, g); + t2 = Maj(a, b, c) + Sigma0(a); + + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + } + + for (i = 16; i < 80; i++) { + + w[i & 15] =sigma1(w[(i - 2) & 15]) + sigma0(w[(i - 15) & 15]) + w[(i -16) & 15] + w[(i - 7) & 15]; + t1 = k[i] + w[i & 15] + h + Sigma1(e) + Ch(e, f, g); + t2 = Maj(a, b, c) + Sigma0(a); + + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + } + + uint64_t finalhash[8]; + + finalhash[0] = SWAP64(a + 0x6a09e667f3bcc908UL); + finalhash[1] = SWAP64(b + 0xbb67ae8584caa73bUL); + finalhash[2] = SWAP64(c + 0x3c6ef372fe94f82bUL); + finalhash[3] = SWAP64(d + 0xa54ff53a5f1d36f1UL); + finalhash[4] = SWAP64(e + 0x510e527fade682d1UL); + finalhash[5] = SWAP64(f + 0x9b05688c2b3e6c1fUL); + finalhash[6] = SWAP64(g + 0x1f83d9abfb41bd6bUL); + finalhash[7] = SWAP64(h + 0x5be0cd19137e2179UL); + + setup_ctx(&ctx, (char*) finalhash, 64); + + a = 0x6a09e667f3bcc908UL; + b = 0xbb67ae8584caa73bUL; + c = 0x3c6ef372fe94f82bUL; + d = 0xa54ff53a5f1d36f1UL; + e = 0x510e527fade682d1UL; + f = 0x9b05688c2b3e6c1fUL; + g = 0x1f83d9abfb41bd6bUL; + h = 0x5be0cd19137e2179UL; + + data = (uint64_t *) ctx.buffer; + //((uint64_t*)ctx.buffer)[8] = SWAP64((uint64_t)0x80); + + for (i = 0; i < 16; i++) + w[i] = SWAP64(data[i]); + + for (i = 0; i < 16; i++) { + t1 = k[i] + w[i] + h + Sigma1(e) + Ch(e, f, g); + t2 = Maj(a, b, c) + Sigma0(a); + + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + } + + for (i = 16; i < 80; i++) { + + w[i & 15] =sigma1(w[(i - 2) & 15]) + sigma0(w[(i - 15) & 15]) + w[(i -16) & 15] + w[(i - 7) & 15]; + t1 = k[i] + w[i & 15] + h + Sigma1(e) + Ch(e, f, g); + t2 = Maj(a, b, c) + Sigma0(a); + + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + } + return SWAP64(a + 0x6a09e667f3bcc908UL); +} + +__kernel void kernel_sha512(__global const sha512_key *password,__global uint64_t *hash, uint64_t start) +{ + uint64_t idx = get_global_id(0); + if (idx == 0 && start == 0) { + *hash = 0; + } + uint64_t winval; + + uint64_t junk[9]; + + __global uint64_t * source = (__global uint64_t*) password->v; + for (int i = 1; i < 9; i++) { + junk[i] = source[i]; + } + + junk[0] = SWAP64(idx + (start)); + + winval = sha512((char*)junk); + if (SWAP64(winval) < password->target) { + *hash = SWAP64(junk[0]); + } +} + diff --git a/src/openclpow.py b/src/openclpow.py new file mode 100644 index 00000000..0876aa79 --- /dev/null +++ b/src/openclpow.py @@ -0,0 +1,66 @@ +import numpy +from struct import pack, unpack +import time +import hashlib +import pyopencl as cl + +hash_dt = numpy.dtype([('target', numpy.uint64), ('v', numpy.str_, 73)]) +ctx = False +queue = False +program = False + +try: + if (len(cl.get_platforms()) > 0): + ctx = cl.create_some_context() + queue = cl.CommandQueue(ctx) + + f = open('kernel.cl', 'r') + fstr = ''.join(f.readlines()) + program = cl.Program(ctx, fstr).build() +except: + ctx = False + +def has_opencl(): + return (ctx != False) + +def do_opencl_pow(hash, target): + output = numpy.zeros(1, dtype=[('v', numpy.uint64, 1)]) + if (ctx == False): + return output[0][0] + + data = numpy.zeros(1, dtype=hash_dt, order='C') + data[0]['v'] = ("0000000000000000" + hash).decode("hex") + data[0]['target'] = target + + hash_buf = cl.Buffer(ctx, cl.mem_flags.READ_ONLY | cl.mem_flags.COPY_HOST_PTR, hostbuf=data) + dest_buf = cl.Buffer(ctx, cl.mem_flags.WRITE_ONLY, output.nbytes) + + kernel = program.kernel_sha512 + worksize = kernel.get_work_group_info(cl.kernel_work_group_info.WORK_GROUP_SIZE, cl.get_platforms()[0].get_devices()[0]) + + kernel.set_arg(0, hash_buf) + kernel.set_arg(1, dest_buf) + + start = time.time() + startpos = 0 + globamt = worksize*2000 + + while output[0][0] == 0: + kernel.set_arg(2, pack("Q',hashlib.sha512(hashlib.sha512(pack('>Q',nonce) + initialHash).digest()).digest()[0:8]) + print "{} - value {} < {}".format(nonce, trialValue, target) + diff --git a/src/proofofwork.py b/src/proofofwork.py index 8857ccb3..f05e7eea 100644 --- a/src/proofofwork.py +++ b/src/proofofwork.py @@ -6,6 +6,7 @@ from struct import unpack, pack import sys from shared import config, frozen import shared +from openclpow import do_opencl_pow #import os def _set_idle(): @@ -70,8 +71,16 @@ def _doFastPoW(target, initialHash): return result[0], result[1] time.sleep(0.2) +def _doGPUPow(target, initialHash): + nonce = do_opencl_pow(initialHash.encode("hex"), target) + trialValue, = unpack('>Q',hashlib.sha512(hashlib.sha512(pack('>Q',nonce) + initialHash).digest()).digest()[0:8]) + #print "{} - value {} < {}".format(nonce, trialValue, target) + return [trialValue, nonce] + def run(target, initialHash): - if frozen == "macosx_app" or not frozen: + if has_opencl: + return _doGPUPow(target, initialHash) + elif frozen == "macosx_app" or not frozen: return _doFastPoW(target, initialHash) else: return _doSafePoW(target, initialHash) From 6a86ecd239ba92b747743e71edcc99e3958bd8f7 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 19 Jan 2015 18:53:07 +0100 Subject: [PATCH 002/210] Added a CLI interface to the daemon from https://github.com/Dokument/PyBitmessage-Daemon/raw/master/daemon.py --- src/bitmessagecli.py | 1710 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1710 insertions(+) create mode 100644 src/bitmessagecli.py diff --git a/src/bitmessagecli.py b/src/bitmessagecli.py new file mode 100644 index 00000000..cfe892b6 --- /dev/null +++ b/src/bitmessagecli.py @@ -0,0 +1,1710 @@ +#!/usr/bin/env python2.7.x +# Created by Adam Melton (.dok) referenceing https://bitmessage.org/wiki/API_Reference for API documentation +# Distributed under the MIT/X11 software license. See http://www.opensource.org/licenses/mit-license.php. + +# This is an example of a daemon client for PyBitmessage 0.4.2, by .dok (Version 0.3.0) + + +import ConfigParser +import xmlrpclib +import datetime +import hashlib +import getopt +import imghdr +import ntpath +import json +import time +import sys +import os + +api = '' +keysName = 'keys.dat' +keysPath = 'keys.dat' +usrPrompt = 0 #0 = First Start, 1 = prompt, 2 = no prompt if the program is starting up +knownAddresses = dict() + +def userInput(message): #Checks input for exit or quit. Also formats for input, etc + global usrPrompt + print '\n' + message + uInput = raw_input('> ') + + if (uInput.lower() == 'exit'): #Returns the user to the main menu + usrPrompt = 1 + main() + + elif (uInput.lower() == 'quit'): #Quits the program + print '\n Bye\n' + sys.exit() + os.exit() + else: + return uInput + +def restartBmNotify(): #Prompts the user to restart Bitmessage. + print '\n *******************************************************************' + print ' WARNING: If Bitmessage is running locally, you must restart it now.' + print ' *******************************************************************\n' + +def safeConfigGetBoolean(section,field): + global keysPath + config = ConfigParser.SafeConfigParser() + config.read(keysPath) + + try: + return config.getboolean(section,field) + except: + return False + +#Begin keys.dat interactions +def lookupAppdataFolder(): #gets the appropriate folders for the .dat files depending on the OS. Taken from bitmessagemain.py + APPNAME = "PyBitmessage" + from os import path, environ + if sys.platform == 'darwin': + if "HOME" in environ: + dataFolder = path.join(os.environ["HOME"], "Library/Application support/", APPNAME) + '/' + else: + print ' Could not find home folder, please report this message and your OS X version to the Daemon Github.' + os.exit() + + elif 'win32' in sys.platform or 'win64' in sys.platform: + dataFolder = path.join(environ['APPDATA'], APPNAME) + '\\' + else: + dataFolder = path.expanduser(path.join("~", ".config/" + APPNAME + "/")) + return dataFolder + +def configInit(): + global keysName + config = ConfigParser.SafeConfigParser() + + config.add_section('bitmessagesettings') + config.set('bitmessagesettings', 'port', '8444') #Sets the bitmessage port to stop the warning about the api not properly being setup. This is in the event that the keys.dat is in a different directory or is created locally to connect to a machine remotely. + config.set('bitmessagesettings','apienabled','true') #Sets apienabled to true in keys.dat + + with open(keysName, 'wb') as configfile: + config.write(configfile) + + print '\n ' + str(keysName) + ' Initalized in the same directory as daemon.py' + print ' You will now need to configure the ' + str(keysName) + ' file.\n' + +def apiInit(apiEnabled): + global keysPath + global usrPrompt + config = ConfigParser.SafeConfigParser() + config.read(keysPath) + + + + if (apiEnabled == False): #API information there but the api is disabled. + uInput = userInput("The API is not enabled. Would you like to do that now, (Y)es or (N)o?").lower() + + if uInput == "y": # + config.set('bitmessagesettings','apienabled','true') #Sets apienabled to true in keys.dat + with open(keysPath, 'wb') as configfile: + config.write(configfile) + + print 'Done' + restartBmNotify() + return True + + elif uInput == "n": + print ' \n************************************************************' + print ' Daemon will not work when the API is disabled. ' + print ' Please refer to the Bitmessage Wiki on how to setup the API.' + print ' ************************************************************\n' + usrPrompt = 1 + main() + + else: + print '\n Invalid Entry\n' + usrPrompt = 1 + main() + elif (apiEnabled == True): #API correctly setup + #Everything is as it should be + return True + + else: #API information was not present. + print '\n ' + str(keysPath) + ' not properly configured!\n' + uInput = userInput("Would you like to do this now, (Y)es or (N)o?").lower() + + if uInput == "y": #User said yes, initalize the api by writing these values to the keys.dat file + print ' ' + + apiUsr = userInput("API Username") + apiPwd = userInput("API Password") + apiInterface = userInput("API Interface. (127.0.0.1)") + apiPort = userInput("API Port") + apiEnabled = userInput("API Enabled? (True) or (False)").lower() + daemon = userInput("Daemon mode Enabled? (True) or (False)").lower() + + if (daemon != 'true' and daemon != 'false'): + print '\n Invalid Entry for Daemon.\n' + uInput = 1 + main() + + print ' -----------------------------------\n' + + config.set('bitmessagesettings', 'port', '8444') #sets the bitmessage port to stop the warning about the api not properly being setup. This is in the event that the keys.dat is in a different directory or is created locally to connect to a machine remotely. + config.set('bitmessagesettings','apienabled','true') + config.set('bitmessagesettings', 'apiport', apiPort) + config.set('bitmessagesettings', 'apiinterface', '127.0.0.1') + config.set('bitmessagesettings', 'apiusername', apiUsr) + config.set('bitmessagesettings', 'apipassword', apiPwd) + config.set('bitmessagesettings', 'daemon', daemon) + with open(keysPath, 'wb') as configfile: + config.write(configfile) + + print '\n Finished configuring the keys.dat file with API information.\n' + restartBmNotify() + return True + + elif uInput == "n": + print '\n ***********************************************************' + print ' Please refer to the Bitmessage Wiki on how to setup the API.' + print ' ***********************************************************\n' + usrPrompt = 1 + main() + else: + print ' \nInvalid entry\n' + usrPrompt = 1 + main() + + +def apiData(): + global keysName + global keysPath + global usrPrompt + + config = ConfigParser.SafeConfigParser() + config.read(keysPath) #First try to load the config file (the keys.dat file) from the program directory + + try: + config.get('bitmessagesettings','port') + appDataFolder = '' + except: + #Could not load the keys.dat file in the program directory. Perhaps it is in the appdata directory. + appDataFolder = lookupAppdataFolder() + keysPath = appDataFolder + keysPath + config = ConfigParser.SafeConfigParser() + config.read(keysPath) + + try: + config.get('bitmessagesettings','port') + except: + #keys.dat was not there either, something is wrong. + print '\n ******************************************************************' + print ' There was a problem trying to access the Bitmessage keys.dat file' + print ' or keys.dat is not set up correctly' + print ' Make sure that daemon is in the same directory as Bitmessage. ' + print ' ******************************************************************\n' + + uInput = userInput("Would you like to create a keys.dat in the local directory, (Y)es or (N)o?").lower() + + if (uInput == "y" or uInput == "yes"): + configInit() + keysPath = keysName + usrPrompt = 0 + main() + elif (uInput == "n" or uInput == "no"): + print '\n Trying Again.\n' + usrPrompt = 0 + main() + else: + print '\n Invalid Input.\n' + + usrPrompt = 1 + main() + + try: #checks to make sure that everyting is configured correctly. Excluding apiEnabled, it is checked after + config.get('bitmessagesettings', 'apiport') + config.get('bitmessagesettings', 'apiinterface') + config.get('bitmessagesettings', 'apiusername') + config.get('bitmessagesettings', 'apipassword') + except: + apiInit("") #Initalize the keys.dat file with API information + + #keys.dat file was found or appropriately configured, allow information retrieval + apiEnabled = apiInit(safeConfigGetBoolean('bitmessagesettings','apienabled')) #if false it will prompt the user, if true it will return true + + config.read(keysPath)#read again since changes have been made + apiPort = int(config.get('bitmessagesettings', 'apiport')) + apiInterface = config.get('bitmessagesettings', 'apiinterface') + apiUsername = config.get('bitmessagesettings', 'apiusername') + apiPassword = config.get('bitmessagesettings', 'apipassword') + + print '\n API data successfully imported.\n' + + return "http://" + apiUsername + ":" + apiPassword + "@" + apiInterface+ ":" + str(apiPort) + "/" #Build the api credentials + +#End keys.dat interactions + + +def apiTest(): #Tests the API connection to bitmessage. Returns true if it is connected. + + try: + result = api.add(2,3) + except: + return False + + if (result == 5): + return True + else: + return False + +def bmSettings(): #Allows the viewing and modification of keys.dat settings. + global keysPath + global usrPrompt + config = ConfigParser.SafeConfigParser() + keysPath = 'keys.dat' + + config.read(keysPath)#Read the keys.dat + try: + port = config.get('bitmessagesettings', 'port') + except: + print '\n File not found.\n' + usrPrompt = 0 + main() + + startonlogon = safeConfigGetBoolean('bitmessagesettings', 'startonlogon') + minimizetotray = safeConfigGetBoolean('bitmessagesettings', 'minimizetotray') + showtraynotifications = safeConfigGetBoolean('bitmessagesettings', 'showtraynotifications') + startintray = safeConfigGetBoolean('bitmessagesettings', 'startintray') + defaultnoncetrialsperbyte = config.get('bitmessagesettings', 'defaultnoncetrialsperbyte') + defaultpayloadlengthextrabytes = config.get('bitmessagesettings', 'defaultpayloadlengthextrabytes') + daemon = safeConfigGetBoolean('bitmessagesettings', 'daemon') + + socksproxytype = config.get('bitmessagesettings', 'socksproxytype') + sockshostname = config.get('bitmessagesettings', 'sockshostname') + socksport = config.get('bitmessagesettings', 'socksport') + socksauthentication = safeConfigGetBoolean('bitmessagesettings', 'socksauthentication') + socksusername = config.get('bitmessagesettings', 'socksusername') + sockspassword = config.get('bitmessagesettings', 'sockspassword') + + + print '\n -----------------------------------' + print ' | Current Bitmessage Settings |' + print ' -----------------------------------' + print ' port = ' + port + print ' startonlogon = ' + str(startonlogon) + print ' minimizetotray = ' + str(minimizetotray) + print ' showtraynotifications = ' + str(showtraynotifications) + print ' startintray = ' + str(startintray) + print ' defaultnoncetrialsperbyte = ' + defaultnoncetrialsperbyte + print ' defaultpayloadlengthextrabytes = ' + defaultpayloadlengthextrabytes + print ' daemon = ' + str(daemon) + print '\n ------------------------------------' + print ' | Current Connection Settings |' + print ' -----------------------------------' + print ' socksproxytype = ' + socksproxytype + print ' sockshostname = ' + sockshostname + print ' socksport = ' + socksport + print ' socksauthentication = ' + str(socksauthentication) + print ' socksusername = ' + socksusername + print ' sockspassword = ' + sockspassword + print ' ' + + uInput = userInput("Would you like to modify any of these settings, (Y)es or (N)o?").lower() + + if uInput == "y": + while True: #loops if they mistype the setting name, they can exit the loop with 'exit' + invalidInput = False + uInput = userInput("What setting would you like to modify?").lower() + print ' ' + + if uInput == "port": + print ' Current port number: ' + port + uInput = userInput("Enter the new port number.") + config.set('bitmessagesettings', 'port', str(uInput)) + elif uInput == "startonlogon": + print ' Current status: ' + str(startonlogon) + uInput = userInput("Enter the new status.") + config.set('bitmessagesettings', 'startonlogon', str(uInput)) + elif uInput == "minimizetotray": + print ' Current status: ' + str(minimizetotray) + uInput = userInput("Enter the new status.") + config.set('bitmessagesettings', 'minimizetotray', str(uInput)) + elif uInput == "showtraynotifications": + print ' Current status: ' + str(showtraynotifications) + uInput = userInput("Enter the new status.") + config.set('bitmessagesettings', 'showtraynotifications', str(uInput)) + elif uInput == "startintray": + print ' Current status: ' + str(startintray) + uInput = userInput("Enter the new status.") + config.set('bitmessagesettings', 'startintray', str(uInput)) + elif uInput == "defaultnoncetrialsperbyte": + print ' Current default nonce trials per byte: ' + defaultnoncetrialsperbyte + uInput = userInput("Enter the new defaultnoncetrialsperbyte.") + config.set('bitmessagesettings', 'defaultnoncetrialsperbyte', str(uInput)) + elif uInput == "defaultpayloadlengthextrabytes": + print ' Current default payload length extra bytes: ' + defaultpayloadlengthextrabytes + uInput = userInput("Enter the new defaultpayloadlengthextrabytes.") + config.set('bitmessagesettings', 'defaultpayloadlengthextrabytes', str(uInput)) + elif uInput == "daemon": + print ' Current status: ' + str(daemon) + uInput = userInput("Enter the new status.").lower() + config.set('bitmessagesettings', 'daemon', str(uInput)) + elif uInput == "socksproxytype": + print ' Current socks proxy type: ' + socksproxytype + print "Possibilities: 'none', 'SOCKS4a', 'SOCKS5'." + uInput = userInput("Enter the new socksproxytype.") + config.set('bitmessagesettings', 'socksproxytype', str(uInput)) + elif uInput == "sockshostname": + print ' Current socks host name: ' + sockshostname + uInput = userInput("Enter the new sockshostname.") + config.set('bitmessagesettings', 'sockshostname', str(uInput)) + elif uInput == "socksport": + print ' Current socks port number: ' + socksport + uInput = userInput("Enter the new socksport.") + config.set('bitmessagesettings', 'socksport', str(uInput)) + elif uInput == "socksauthentication": + print ' Current status: ' + str(socksauthentication) + uInput = userInput("Enter the new status.") + config.set('bitmessagesettings', 'socksauthentication', str(uInput)) + elif uInput == "socksusername": + print ' Current socks username: ' + socksusername + uInput = userInput("Enter the new socksusername.") + config.set('bitmessagesettings', 'socksusername', str(uInput)) + elif uInput == "sockspassword": + print ' Current socks password: ' + sockspassword + uInput = userInput("Enter the new password.") + config.set('bitmessagesettings', 'sockspassword', str(uInput)) + else: + print "\n Invalid input. Please try again.\n" + invalidInput = True + + if invalidInput != True: #don't prompt if they made a mistake. + uInput = userInput("Would you like to change another setting, (Y)es or (N)o?").lower() + + if uInput != "y": + print '\n Changes Made.\n' + with open(keysPath, 'wb') as configfile: + config.write(configfile) + restartBmNotify() + break + + + elif uInput == "n": + usrPrompt = 1 + main() + else: + print "Invalid input." + usrPrompt = 1 + main() + +def validAddress(address): + address_information = api.decodeAddress(address) + address_information = eval(address_information) + + if 'success' in str(address_information.get('status')).lower(): + return True + else: + return False + +def getAddress(passphrase,vNumber,sNumber): + passphrase = passphrase.encode('base64')#passphrase must be encoded + + return api.getDeterministicAddress(passphrase,vNumber,sNumber) + +def subscribe(): + global usrPrompt + + while True: + address = userInput("What address would you like to subscribe to?") + + if (address == "c"): + usrPrompt = 1 + print ' ' + main() + elif (validAddress(address)== False): + print '\n Invalid. "c" to cancel. Please try again.\n' + else: + break + + label = userInput("Enter a label for this address.") + label = label.encode('base64') + + api.addSubscription(address,label) + print ('\n You are now subscribed to: ' + address + '\n') + +def unsubscribe(): + global usrPrompt + + while True: + address = userInput("What address would you like to unsubscribe from?") + + if (address == "c"): + usrPrompt = 1 + print ' ' + main() + elif (validAddress(address)== False): + print '\n Invalid. "c" to cancel. Please try again.\n' + else: + break + + + uInput = userInput("Are you sure, (Y)es or (N)o?").lower() + + api.deleteSubscription(address) + print ('\n You are now unsubscribed from: ' + address + '\n') + +def listSubscriptions(): + global usrPrompt + #jsonAddresses = json.loads(api.listSubscriptions()) + #numAddresses = len(jsonAddresses['addresses']) #Number of addresses + print '\nLabel, Address, Enabled\n' + try: + print api.listSubscriptions() + except: + print '\n Connection Error\n' + usrPrompt = 0 + main() + + '''for addNum in range (0, numAddresses): #processes all of the addresses and lists them out + label = jsonAddresses['addresses'][addNum]['label'] + address = jsonAddresses['addresses'][addNum]['address'] + enabled = jsonAddresses['addresses'][addNum]['enabled'] + + print label, address, enabled + ''' + print ' ' + +def createChan(): + global usrPrompt + password = userInput("Enter channel name") + password = password.encode('base64') + try: + print api.createChan(password) + except: + print '\n Connection Error\n' + usrPrompt = 0 + main() + + +def joinChan(): + global usrPrompt + while True: + address = userInput("Enter channel address") + + if (address == "c"): + usrPrompt = 1 + print ' ' + main() + elif (validAddress(address)== False): + print '\n Invalid. "c" to cancel. Please try again.\n' + else: + break + + password = userInput("Enter channel name") + password = password.encode('base64') + try: + print api.joinChan(password,address) + except: + print '\n Connection Error\n' + usrPrompt = 0 + main() + +def leaveChan(): + global usrPrompt + while True: + address = userInput("Enter channel address") + + if (address == "c"): + usrPrompt = 1 + print ' ' + main() + elif (validAddress(address)== False): + print '\n Invalid. "c" to cancel. Please try again.\n' + else: + break + + try: + print api.leaveChan(address) + except: + print '\n Connection Error\n' + usrPrompt = 0 + main() + + +def listAdd(): #Lists all of the addresses and their info + global usrPrompt + try: + jsonAddresses = json.loads(api.listAddresses()) + numAddresses = len(jsonAddresses['addresses']) #Number of addresses + except: + print '\n Connection Error\n' + usrPrompt = 0 + main() + + #print '\nAddress Number,Label,Address,Stream,Enabled\n' + print '\n --------------------------------------------------------------------------' + print ' | # | Label | Address |S#|Enabled|' + print ' |---|-------------------|-------------------------------------|--|-------|' + for addNum in range (0, numAddresses): #processes all of the addresses and lists them out + label = str(jsonAddresses['addresses'][addNum]['label']) + address = str(jsonAddresses['addresses'][addNum]['address']) + stream = str(jsonAddresses['addresses'][addNum]['stream']) + enabled = str(jsonAddresses['addresses'][addNum]['enabled']) + + if (len(label) > 19): + label = label[:16] + '...' + + print ' |' + str(addNum).ljust(3) + '|' + label.ljust(19) + '|' + address.ljust(37) + '|' + stream.ljust(1), '|' + enabled.ljust(7) + '|' + + print ' --------------------------------------------------------------------------\n' + +def genAdd(lbl,deterministic, passphrase, numOfAdd, addVNum, streamNum, ripe): #Generate address + global usrPrompt + if deterministic == False: #Generates a new address with the user defined label. non-deterministic + addressLabel = lbl.encode('base64') + try: + generatedAddress = api.createRandomAddress(addressLabel) + except: + print '\n Connection Error\n' + usrPrompt = 0 + main() + + return generatedAddress + + elif deterministic == True: #Generates a new deterministic address with the user inputs. + passphrase = passphrase.encode('base64') + try: + generatedAddress = api.createDeterministicAddresses(passphrase, numOfAdd, addVNum, streamNum, ripe) + except: + print '\n Connection Error\n' + usrPrompt = 0 + main() + return generatedAddress + else: + return 'Entry Error' + +def saveFile(fileName, fileData): #Allows attachments and messages/broadcats to be saved + + #This section finds all invalid characters and replaces them with ~ + fileName = fileName.replace(" ", "") + fileName = fileName.replace("/", "~") + #fileName = fileName.replace("\\", "~") How do I get this to work...? + fileName = fileName.replace(":", "~") + fileName = fileName.replace("*", "~") + fileName = fileName.replace("?", "~") + fileName = fileName.replace('"', "~") + fileName = fileName.replace("<", "~") + fileName = fileName.replace(">", "~") + fileName = fileName.replace("|", "~") + + directory = 'attachments' + + if not os.path.exists(directory): + os.makedirs(directory) + + filePath = directory +'/'+ fileName + + '''try: #Checks if file already exists + with open(filePath): + print 'File Already Exists' + return + except IOError: pass''' + + + f = open(filePath, 'wb+') #Begin saving to file + f.write(fileData.decode("base64")) + f.close + + print '\n Successfully saved '+ filePath + '\n' + +def attachment(): #Allows users to attach a file to their message or broadcast + theAttachmentS = '' + + while True: + + isImage = False + theAttachment = '' + + while True:#loops until valid path is entered + filePath = userInput('\nPlease enter the path to the attachment or just the attachment name if in this folder.') + + try: + with open(filePath): break + except IOError: + print '\n %s was not found on your filesystem or can not be opened.\n' % filePath + pass + + #print filesize, and encoding estimate with confirmation if file is over X size (1mb?) + invSize = os.path.getsize(filePath) + invSize = (invSize / 1024) #Converts to kilobytes + round(invSize,2) #Rounds to two decimal places + + if (invSize > 500.0):#If over 500KB + print '\n WARNING:The file that you are trying to attach is ', invSize, 'KB and will take considerable time to send.\n' + uInput = userInput('Are you sure you still want to attach it, (Y)es or (N)o?').lower() + + if uInput != "y": + print '\n Attachment discarded.\n' + return '' + elif (invSize > 184320.0): #If larger than 180MB, discard. + print '\n Attachment too big, maximum allowed size:180MB\n' + main() + + pathLen = len(str(ntpath.basename(filePath))) #Gets the length of the filepath excluding the filename + fileName = filePath[(len(str(filePath)) - pathLen):] #reads the filename + + filetype = imghdr.what(filePath) #Tests if it is an image file + if filetype is not None: + print '\n ---------------------------------------------------' + print ' Attachment detected as an Image.' + print ' tags will automatically be included,' + print ' allowing the recipient to view the image' + print ' using the "View HTML code..." option in Bitmessage.' + print ' ---------------------------------------------------\n' + isImage = True + time.sleep(2) + + print '\n Encoding Attachment, Please Wait ...\n' #Alert the user that the encoding process may take some time. + + with open(filePath, 'rb') as f: #Begin the actual encoding + data = f.read(188743680) #Reads files up to 180MB, the maximum size for Bitmessage. + data = data.encode("base64") + + if (isImage == True): #If it is an image, include image tags in the message + theAttachment = """ + + + +Filename:%s +Filesize:%sKB +Encoding:base64 + +
+
+ %s +
+
""" % (fileName,invSize,fileName,filetype,data) + else: #Else it is not an image so do not include the embedded image code. + theAttachment = """ + + + +Filename:%s +Filesize:%sKB +Encoding:base64 + +""" % (fileName,invSize,fileName,fileName,data) + + uInput = userInput('Would you like to add another attachment, (Y)es or (N)o?').lower() + + if (uInput == 'y' or uInput == 'yes'):#Allows multiple attachments to be added to one message + theAttachmentS = str(theAttachmentS) + str(theAttachment)+ '\n\n' + elif (uInput == 'n' or uInput == 'no'): + break + + theAttachmentS = theAttachmentS + theAttachment + return theAttachmentS + +def sendMsg(toAddress, fromAddress, subject, message): #With no arguments sent, sendMsg fills in the blanks. subject and message must be encoded before they are passed. + global usrPrompt + if (validAddress(toAddress)== False): + while True: + toAddress = userInput("What is the To Address?") + + if (toAddress == "c"): + usrPrompt = 1 + print ' ' + main() + elif (validAddress(toAddress)== False): + print '\n Invalid Address. "c" to cancel. Please try again.\n' + else: + break + + + if (validAddress(fromAddress)== False): + try: + jsonAddresses = json.loads(api.listAddresses()) + numAddresses = len(jsonAddresses['addresses']) #Number of addresses + except: + print '\n Connection Error\n' + usrPrompt = 0 + main() + + if (numAddresses > 1): #Ask what address to send from if multiple addresses + found = False + while True: + print ' ' + fromAddress = userInput("Enter an Address or Address Label to send from.") + + if fromAddress == "exit": + usrPrompt = 1 + main() + + for addNum in range (0, numAddresses): #processes all of the addresses + label = jsonAddresses['addresses'][addNum]['label'] + address = jsonAddresses['addresses'][addNum]['address'] + #stream = jsonAddresses['addresses'][addNum]['stream'] + #enabled = jsonAddresses['addresses'][addNum]['enabled'] + if (fromAddress == label): #address entered was a label and is found + fromAddress = address + found = True + break + + if (found == False): + if(validAddress(fromAddress)== False): + print '\n Invalid Address. Please try again.\n' + + else: + for addNum in range (0, numAddresses): #processes all of the addresses + #label = jsonAddresses['addresses'][addNum]['label'] + address = jsonAddresses['addresses'][addNum]['address'] + #stream = jsonAddresses['addresses'][addNum]['stream'] + #enabled = jsonAddresses['addresses'][addNum]['enabled'] + if (fromAddress == address): #address entered was a found in our addressbook. + found = True + break + + if (found == False): + print '\n The address entered is not one of yours. Please try again.\n' + + if (found == True): + break #Address was found + + else: #Only one address in address book + print '\n Using the only address in the addressbook to send from.\n' + fromAddress = jsonAddresses['addresses'][0]['address'] + + if (subject == ''): + subject = userInput("Enter your Subject.") + subject = subject.encode('base64') + if (message == ''): + message = userInput("Enter your Message.") + + uInput = userInput('Would you like to add an attachment, (Y)es or (N)o?').lower() + if uInput == "y": + message = message + '\n\n' + attachment() + + message = message.encode('base64') + + try: + ackData = api.sendMessage(toAddress, fromAddress, subject, message) + print '\n Message Status:', api.getStatus(ackData), '\n' + except: + print '\n Connection Error\n' + usrPrompt = 0 + main() + + +def sendBrd(fromAddress, subject, message): #sends a broadcast + global usrPrompt + if (fromAddress == ''): + + try: + jsonAddresses = json.loads(api.listAddresses()) + numAddresses = len(jsonAddresses['addresses']) #Number of addresses + except: + print '\n Connection Error\n' + usrPrompt = 0 + main() + + if (numAddresses > 1): #Ask what address to send from if multiple addresses + found = False + while True: + fromAddress = userInput("\nEnter an Address or Address Label to send from.") + + if fromAddress == "exit": + usrPrompt = 1 + main() + + for addNum in range (0, numAddresses): #processes all of the addresses + label = jsonAddresses['addresses'][addNum]['label'] + address = jsonAddresses['addresses'][addNum]['address'] + #stream = jsonAddresses['addresses'][addNum]['stream'] + #enabled = jsonAddresses['addresses'][addNum]['enabled'] + if (fromAddress == label): #address entered was a label and is found + fromAddress = address + found = True + break + + if (found == False): + if(validAddress(fromAddress)== False): + print '\n Invalid Address. Please try again.\n' + + else: + for addNum in range (0, numAddresses): #processes all of the addresses + #label = jsonAddresses['addresses'][addNum]['label'] + address = jsonAddresses['addresses'][addNum]['address'] + #stream = jsonAddresses['addresses'][addNum]['stream'] + #enabled = jsonAddresses['addresses'][addNum]['enabled'] + if (fromAddress == address): #address entered was a found in our addressbook. + found = True + break + + if (found == False): + print '\n The address entered is not one of yours. Please try again.\n' + + if (found == True): + break #Address was found + + else: #Only one address in address book + print '\n Using the only address in the addressbook to send from.\n' + fromAddress = jsonAddresses['addresses'][0]['address'] + + if (subject == ''): + subject = userInput("Enter your Subject.") + subject = subject.encode('base64') + if (message == ''): + message = userInput("Enter your Message.") + + uInput = userInput('Would you like to add an attachment, (Y)es or (N)o?').lower() + if uInput == "y": + message = message + '\n\n' + attachment() + + message = message.encode('base64') + + try: + ackData = api.sendBroadcast(fromAddress, subject, message) + print '\n Message Status:', api.getStatus(ackData), '\n' + except: + print '\n Connection Error\n' + usrPrompt = 0 + main() + +def inbox(unreadOnly = False): #Lists the messages by: Message Number, To Address Label, From Address Label, Subject, Received Time) + global usrPrompt + try: + inboxMessages = json.loads(api.getAllInboxMessages()) + numMessages = len(inboxMessages['inboxMessages']) + except: + print '\n Connection Error\n' + usrPrompt = 0 + main() + + messagesPrinted = 0 + messagesUnread = 0 + for msgNum in range (0, numMessages): #processes all of the messages in the inbox + message = inboxMessages['inboxMessages'][msgNum] + # if we are displaying all messages or if this message is unread then display it + if not unreadOnly or not message['read']: + print ' -----------------------------------\n' + print ' Message Number:',msgNum #Message Number + print ' To:', getLabelForAddress(message['toAddress']) #Get the to address + print ' From:', getLabelForAddress(message['fromAddress']) #Get the from address + print ' Subject:', message['subject'].decode('base64') #Get the subject + print ' Received:', datetime.datetime.fromtimestamp(float(message['receivedTime'])).strftime('%Y-%m-%d %H:%M:%S') + messagesPrinted += 1 + if not message['read']: messagesUnread += 1 + + if (messagesPrinted%20 == 0 and messagesPrinted != 0): + uInput = userInput('(Press Enter to continue or type (Exit) to return to the main menu.)').lower() + + print '\n -----------------------------------' + print ' There are %d unread messages of %d messages in the inbox.' % (messagesUnread, numMessages) + print ' -----------------------------------\n' + +def outbox(): + global usrPrompt + try: + outboxMessages = json.loads(api.getAllSentMessages()) + numMessages = len(outboxMessages['sentMessages']) + except: + print '\n Connection Error\n' + usrPrompt = 0 + main() + + for msgNum in range (0, numMessages): #processes all of the messages in the outbox + print '\n -----------------------------------\n' + print ' Message Number:',msgNum #Message Number + #print ' Message ID:', outboxMessages['sentMessages'][msgNum]['msgid'] + print ' To:', getLabelForAddress(outboxMessages['sentMessages'][msgNum]['toAddress']) #Get the to address + print ' From:', getLabelForAddress(outboxMessages['sentMessages'][msgNum]['fromAddress']) #Get the from address + print ' Subject:', outboxMessages['sentMessages'][msgNum]['subject'].decode('base64') #Get the subject + print ' Status:', outboxMessages['sentMessages'][msgNum]['status'] #Get the subject + + print ' Last Action Time:', datetime.datetime.fromtimestamp(float(outboxMessages['sentMessages'][msgNum]['lastActionTime'])).strftime('%Y-%m-%d %H:%M:%S') + + if (msgNum%20 == 0 and msgNum != 0): + uInput = userInput('(Press Enter to continue or type (Exit) to return to the main menu.)').lower() + + print '\n -----------------------------------' + print ' There are ',numMessages,' messages in the outbox.' + print ' -----------------------------------\n' + +def readSentMsg(msgNum): #Opens a sent message for reading + global usrPrompt + try: + outboxMessages = json.loads(api.getAllSentMessages()) + numMessages = len(outboxMessages['sentMessages']) + except: + print '\n Connection Error\n' + usrPrompt = 0 + main() + + print ' ' + + if (msgNum >= numMessages): + print '\n Invalid Message Number.\n' + main() + + #Begin attachment detection + message = outboxMessages['sentMessages'][msgNum]['message'].decode('base64') + + while True: #Allows multiple messages to be downloaded/saved + if (';base64,' in message): #Found this text in the message, there is probably an attachment. + attPos= message.index(";base64,") #Finds the attachment position + attEndPos = message.index("' />") #Finds the end of the attachment + #attLen = attEndPos - attPos #Finds the length of the message + + + if ('alt = "' in message): #We can get the filename too + fnPos = message.index('alt = "') #Finds position of the filename + fnEndPos = message.index('" src=') #Finds the end position + #fnLen = fnEndPos - fnPos #Finds the length of the filename + + fileName = message[fnPos+7:fnEndPos] + else: + fnPos = attPos + fileName = 'Attachment' + + uInput = userInput('\n Attachment Detected. Would you like to save the attachment, (Y)es or (N)o?').lower() + if (uInput == "y" or uInput == 'yes'): + + attachment = message[attPos+9:attEndPos] + saveFile(fileName,attachment) + + message = message[:fnPos] + '~~' + message[(attEndPos+4):] + + else: + break + + #End attachment Detection + + print '\n To:', getLabelForAddress(outboxMessages['sentMessages'][msgNum]['toAddress']) #Get the to address + print ' From:', getLabelForAddress(outboxMessages['sentMessages'][msgNum]['fromAddress']) #Get the from address + print ' Subject:', outboxMessages['sentMessages'][msgNum]['subject'].decode('base64') #Get the subject + print ' Status:', outboxMessages['sentMessages'][msgNum]['status'] #Get the subject + print ' Last Action Time:', datetime.datetime.fromtimestamp(float(outboxMessages['sentMessages'][msgNum]['lastActionTime'])).strftime('%Y-%m-%d %H:%M:%S') + print ' Message:\n' + print message #inboxMessages['inboxMessages'][msgNum]['message'].decode('base64') + print ' ' + +def readMsg(msgNum): #Opens a message for reading + global usrPrompt + try: + inboxMessages = json.loads(api.getAllInboxMessages()) + numMessages = len(inboxMessages['inboxMessages']) + except: + print '\n Connection Error\n' + usrPrompt = 0 + main() + + if (msgNum >= numMessages): + print '\n Invalid Message Number.\n' + main() + + #Begin attachment detection + message = inboxMessages['inboxMessages'][msgNum]['message'].decode('base64') + + while True: #Allows multiple messages to be downloaded/saved + if (';base64,' in message): #Found this text in the message, there is probably an attachment. + attPos= message.index(";base64,") #Finds the attachment position + attEndPos = message.index("' />") #Finds the end of the attachment + #attLen = attEndPos - attPos #Finds the length of the message + + + if ('alt = "' in message): #We can get the filename too + fnPos = message.index('alt = "') #Finds position of the filename + fnEndPos = message.index('" src=') #Finds the end position + #fnLen = fnEndPos - fnPos #Finds the length of the filename + + fileName = message[fnPos+7:fnEndPos] + else: + fnPos = attPos + fileName = 'Attachment' + + uInput = userInput('\n Attachment Detected. Would you like to save the attachment, (Y)es or (N)o?').lower() + if (uInput == "y" or uInput == 'yes'): + + attachment = message[attPos+9:attEndPos] + saveFile(fileName,attachment) + + message = message[:fnPos] + '~~' + message[(attEndPos+4):] + + else: + break + + #End attachment Detection + print '\n To:', getLabelForAddress(inboxMessages['inboxMessages'][msgNum]['toAddress']) #Get the to address + print ' From:', getLabelForAddress(inboxMessages['inboxMessages'][msgNum]['fromAddress']) #Get the from address + print ' Subject:', inboxMessages['inboxMessages'][msgNum]['subject'].decode('base64') #Get the subject + print ' Received:',datetime.datetime.fromtimestamp(float(inboxMessages['inboxMessages'][msgNum]['receivedTime'])).strftime('%Y-%m-%d %H:%M:%S') + print ' Message:\n' + print message #inboxMessages['inboxMessages'][msgNum]['message'].decode('base64') + print ' ' + return inboxMessages['inboxMessages'][msgNum]['msgid'] + +def replyMsg(msgNum,forwardORreply): #Allows you to reply to the message you are currently on. Saves typing in the addresses and subject. + global usrPrompt + forwardORreply = forwardORreply.lower() #makes it lowercase + try: + inboxMessages = json.loads(api.getAllInboxMessages()) + except: + print '\n Connection Error\n' + usrPrompt = 0 + main() + + fromAdd = inboxMessages['inboxMessages'][msgNum]['toAddress']#Address it was sent To, now the From address + message = inboxMessages['inboxMessages'][msgNum]['message'].decode('base64') #Message that you are replying too. + + subject = inboxMessages['inboxMessages'][msgNum]['subject'] + subject = subject.decode('base64') + + if (forwardORreply == 'reply'): + toAdd = inboxMessages['inboxMessages'][msgNum]['fromAddress'] #Address it was From, now the To address + subject = "Re: " + subject + + elif (forwardORreply == 'forward'): + subject = "Fwd: " + subject + + while True: + toAdd = userInput("What is the To Address?") + + if (toAdd == "c"): + usrPrompt = 1 + print ' ' + main() + elif (validAddress(toAdd)== False): + print '\n Invalid Address. "c" to cancel. Please try again.\n' + else: + break + else: + print '\n Invalid Selection. Reply or Forward only' + usrPrompt = 0 + main() + + subject = subject.encode('base64') + + newMessage = userInput("Enter your Message.") + + uInput = userInput('Would you like to add an attachment, (Y)es or (N)o?').lower() + if uInput == "y": + newMessage = newMessage + '\n\n' + attachment() + + newMessage = newMessage + '\n\n------------------------------------------------------\n' + newMessage = newMessage + message + newMessage = newMessage.encode('base64') + + sendMsg(toAdd, fromAdd, subject, newMessage) + + main() + +def delMsg(msgNum): #Deletes a specified message from the inbox + global usrPrompt + try: + inboxMessages = json.loads(api.getAllInboxMessages()) + msgId = inboxMessages['inboxMessages'][int(msgNum)]['msgid'] #gets the message ID via the message index number + + msgAck = api.trashMessage(msgId) + except: + print '\n Connection Error\n' + usrPrompt = 0 + main() + + return msgAck + +def delSentMsg(msgNum): #Deletes a specified message from the outbox + global usrPrompt + try: + outboxMessages = json.loads(api.getAllSentMessages()) + msgId = outboxMessages['sentMessages'][int(msgNum)]['msgid'] #gets the message ID via the message index number + msgAck = api.trashSentMessage(msgId) + except: + print '\n Connection Error\n' + usrPrompt = 0 + main() + + return msgAck + +def getLabelForAddress(address): + global usrPrompt + + if address in knownAddresses: + return knownAddresses[address] + else: + buildKnownAddresses() + if address in knownAddresses: + return knownAddresses[address] + + return address + +def buildKnownAddresses(): + # add from address book + try: + response = api.listAddressBookEntries() + # if api is too old then fail + if "API Error 0020" in response: return + addressBook = json.loads(response) + for entry in addressBook['addresses']: + if entry['address'] not in knownAddresses: + knownAddresses[entry['address']] = "%s (%s)" % (entry['label'].decode('base64'), entry['address']) + except: + print '\n Connection Error\n' + usrPrompt = 0 + main() + + # add from my addresses + try: + response = api.listAddresses2() + # if api is too old just return then fail + if "API Error 0020" in response: return + addresses = json.loads(response) + for entry in addresses['addresses']: + if entry['address'] not in knownAddresses: + knownAddresses[entry['address']] = "%s (%s)" % (entry['label'].decode('base64'), entry['address']) + except: + print '\n Connection Error\n' + usrPrompt = 0 + main() + +def listAddressBookEntries(): + try: + response = api.listAddressBookEntries() + if "API Error" in response: + return getAPIErrorCode(response) + addressBook = json.loads(response) + print + print ' --------------------------------------------------------------' + print ' | Label | Address |' + print ' |--------------------|---------------------------------------|' + for entry in addressBook['addresses']: + label = entry['label'].decode('base64') + address = entry['address'] + if (len(label) > 19): label = label[:16] + '...' + print ' | ' + label.ljust(19) + '| ' + address.ljust(37) + ' |' + print ' --------------------------------------------------------------' + print + + except: + print '\n Connection Error\n' + usrPrompt = 0 + main() + +def addAddressToAddressBook(address, label): + try: + response = api.addAddressBookEntry(address, label.encode('base64')) + if "API Error" in response: + return getAPIErrorCode(response) + except: + print '\n Connection Error\n' + usrPrompt = 0 + main() + +def deleteAddressFromAddressBook(address): + try: + response = api.deleteAddressBookEntry(address) + if "API Error" in response: + return getAPIErrorCode(response) + except: + print '\n Connection Error\n' + usrPrompt = 0 + main() + +def getAPIErrorCode(response): + if "API Error" in response: + # if we got an API error return the number by getting the number + # after the second space and removing the trailing colon + return int(response.split()[2][:-1]) + +def markMessageRead(messageID): + try: + response = api.getInboxMessageByID(messageID, True) + if "API Error" in response: + return getAPIErrorCode(response) + except: + print '\n Connection Error\n' + usrPrompt = 0 + main() + +def markMessageUnread(messageID): + try: + response = api.getInboxMessageByID(messageID, False) + if "API Error" in response: + return getAPIErrorCode(response) + except: + print '\n Connection Error\n' + usrPrompt = 0 + main() + +def markAllMessagesRead(): + try: + inboxMessages = json.loads(api.getAllInboxMessages())['inboxMessages'] + except: + print '\n Connection Error\n' + usrPrompt = 0 + main() + for message in inboxMessages: + if not message['read']: + markMessageRead(message['msgid']) + +def markAllMessagesUnread(): + try: + inboxMessages = json.loads(api.getAllInboxMessages())['inboxMessages'] + except: + print '\n Connection Error\n' + usrPrompt = 0 + main() + for message in inboxMessages: + if message['read']: + markMessageUnread(message['msgid']) + + +def UI(usrInput): #Main user menu + global usrPrompt + + if usrInput == "help" or usrInput == "h" or usrInput == "?": + print ' ' + print ' -------------------------------------------------------------------------' + print ' | https://github.com/Dokument/PyBitmessage-Daemon |' + print ' |-----------------------------------------------------------------------|' + print ' | Command | Description |' + print ' |------------------------|----------------------------------------------|' + print ' | help | This help file. |' + print ' | apiTest | Tests the API |' + print ' | addInfo | Returns address information (If valid) |' + print ' | bmSettings | BitMessage settings |' + print ' | exit | Use anytime to return to main menu |' + print ' | quit | Quits the program |' + print ' |------------------------|----------------------------------------------|' + print ' | listAddresses | Lists all of the users addresses |' + print ' | generateAddress | Generates a new address |' + print ' | getAddress | Get determinist address from passphrase |' + print ' |------------------------|----------------------------------------------|' + print ' | listAddressBookEntries | Lists entries from the Address Book |' + print ' | addAddressBookEntry | Add address to the Address Book |' + print ' | deleteAddressBookEntry | Deletes address from the Address Book |' + print ' |------------------------|----------------------------------------------|' + print ' | subscribe | Subscribes to an address |' + print ' | unsubscribe | Unsubscribes from an address |' + #print ' | listSubscriptions | Lists all of the subscriptions. |' + print ' |------------------------|----------------------------------------------|' + print ' | create | Creates a channel |' + print ' | join | Joins a channel |' + print ' | leave | Leaves a channel |' + print ' |------------------------|----------------------------------------------|' + print ' | inbox | Lists the message information for the inbox |' + print ' | outbox | Lists the message information for the outbox |' + print ' | send | Send a new message or broadcast |' + print ' | unread | Lists all unread inbox messages |' + print ' | read | Reads a message from the inbox or outbox |' + print ' | save | Saves message to text file |' + print ' | delete | Deletes a message or all messages |' + print ' -------------------------------------------------------------------------' + print ' ' + main() + + elif usrInput == "apitest": #tests the API Connection. + if (apiTest() == True): + print '\n API connection test has: PASSED\n' + else: + print '\n API connection test has: FAILED\n' + main() + + elif usrInput == "addinfo": + tmp_address = userInput('\nEnter the Bitmessage Address.') + address_information = api.decodeAddress(tmp_address) + address_information = eval(address_information) + + print '\n------------------------------' + + if 'success' in str(address_information.get('status')).lower(): + print ' Valid Address' + print ' Address Version: %s' % str(address_information.get('addressVersion')) + print ' Stream Number: %s' % str(address_information.get('streamNumber')) + else: + print ' Invalid Address !' + + print '------------------------------\n' + main() + + elif usrInput == "bmsettings": #tests the API Connection. + bmSettings() + print ' ' + main() + + elif usrInput == "quit": #Quits the application + print '\n Bye\n' + sys.exit() + os.exit() + + elif usrInput == "listaddresses": #Lists all of the identities in the addressbook + listAdd() + main() + + elif usrInput == "generateaddress": #Generates a new address + uInput = userInput('\nWould you like to create a (D)eterministic or (R)andom address?').lower() + + if uInput == "d" or uInput == "determinstic": #Creates a deterministic address + deterministic = True + + #lbl = raw_input('Label the new address:') #currently not possible via the api + lbl = '' + passphrase = userInput('Enter the Passphrase.')#.encode('base64') + numOfAdd = int(userInput('How many addresses would you like to generate?')) + #addVNum = int(raw_input('Address version number (default "0"):')) + #streamNum = int(raw_input('Stream number (default "0"):')) + addVNum = 3 + streamNum = 1 + isRipe = userInput('Shorten the address, (Y)es or (N)o?').lower() + + if isRipe == "y": + ripe = True + print genAdd(lbl,deterministic, passphrase, numOfAdd, addVNum, streamNum, ripe) + main() + elif isRipe == "n": + ripe = False + print genAdd(lbl, deterministic, passphrase, numOfAdd, addVNum, streamNum, ripe) + main() + elif isRipe == "exit": + usrPrompt = 1 + main() + else: + print '\n Invalid input\n' + main() + + + elif uInput == "r" or uInput == "random": #Creates a random address with user-defined label + deterministic = False + null = '' + lbl = userInput('Enter the label for the new address.') + + print genAdd(lbl,deterministic, null,null, null, null, null) + main() + + else: + print '\n Invalid input\n' + main() + + elif usrInput == "getaddress": #Gets the address for/from a passphrase + phrase = userInput("Enter the address passphrase.") + print '\n Working...\n' + #vNumber = int(raw_input("Enter the address version number:")) + #sNumber = int(raw_input("Enter the address stream number:")) + + address = getAddress(phrase,4,1)#,vNumber,sNumber) + print ('\n Address: ' + address + '\n') + + usrPrompt = 1 + main() + + elif usrInput == "subscribe": #Subsribe to an address + subscribe() + usrPrompt = 1 + main() + elif usrInput == "unsubscribe": #Unsubscribe from an address + unsubscribe() + usrPrompt = 1 + main() + elif usrInput == "listsubscriptions": #Unsubscribe from an address + listSubscriptions() + usrPrompt = 1 + main() + + elif usrInput == "create": + createChan() + userPrompt = 1 + main() + + elif usrInput == "join": + joinChan() + userPrompt = 1 + main() + + elif usrInput == "leave": + leaveChan() + userPrompt = 1 + main() + + elif usrInput == "inbox": + print '\n Loading...\n' + inbox() + main() + + elif usrInput == "unread": + print '\n Loading...\n' + inbox(True) + main() + + elif usrInput == "outbox": + print '\n Loading...\n' + outbox() + main() + + elif usrInput == 'send': #Sends a message or broadcast + uInput = userInput('Would you like to send a (M)essage or (B)roadcast?').lower() + + if (uInput == 'm' or uInput == 'message'): + null = '' + sendMsg(null,null,null,null) + main() + elif (uInput =='b' or uInput == 'broadcast'): + null = '' + sendBrd(null,null,null) + main() + + + elif usrInput == "read": #Opens a message from the inbox for viewing. + + uInput = userInput("Would you like to read a message from the (I)nbox or (O)utbox?").lower() + + if (uInput != 'i' and uInput != 'inbox' and uInput != 'o' and uInput != 'outbox'): + print '\n Invalid Input.\n' + usrPrompt = 1 + main() + + msgNum = int(userInput("What is the number of the message you wish to open?")) + + if (uInput == 'i' or uInput == 'inbox'): + print '\n Loading...\n' + messageID = readMsg(msgNum) + + uInput = userInput("\nWould you like to keep this message unread, (Y)es or (N)o?").lower() + + if not (uInput == 'y' or uInput == 'yes'): + markMessageRead(messageID) + usrPrompt = 1 + + uInput = userInput("\nWould you like to (D)elete, (F)orward, (R)eply to, or (Exit) this message?").lower() + + if (uInput == 'r' or uInput == 'reply'): + print '\n Loading...\n' + print ' ' + replyMsg(msgNum,'reply') + usrPrompt = 1 + + elif (uInput == 'f' or uInput == 'forward'): + print '\n Loading...\n' + print ' ' + replyMsg(msgNum,'forward') + usrPrompt = 1 + + elif (uInput == "d" or uInput == 'delete'): + uInput = userInput("Are you sure, (Y)es or (N)o?").lower()#Prevent accidental deletion + + if uInput == "y": + delMsg(msgNum) + print '\n Message Deleted.\n' + usrPrompt = 1 + else: + usrPrompt = 1 + else: + print '\n Invalid entry\n' + usrPrompt = 1 + + elif (uInput == 'o' or uInput == 'outbox'): + readSentMsg(msgNum) + + uInput = userInput("Would you like to (D)elete, or (Exit) this message?").lower() #Gives the user the option to delete the message + + if (uInput == "d" or uInput == 'delete'): + uInput = userInput('Are you sure, (Y)es or (N)o?').lower() #Prevent accidental deletion + + if uInput == "y": + delSentMsg(msgNum) + print '\n Message Deleted.\n' + usrPrompt = 1 + else: + usrPrompt = 1 + else: + print '\n Invalid Entry\n' + usrPrompt = 1 + + main() + + elif usrInput == "save": + + uInput = userInput("Would you like to save a message from the (I)nbox or (O)utbox?").lower() + + if (uInput != 'i' and uInput == 'inbox' and uInput != 'o' and uInput == 'outbox'): + print '\n Invalid Input.\n' + usrPrompt = 1 + main() + + if (uInput == 'i' or uInput == 'inbox'): + inboxMessages = json.loads(api.getAllInboxMessages()) + numMessages = len(inboxMessages['inboxMessages']) + + while True: + msgNum = int(userInput("What is the number of the message you wish to save?")) + + if (msgNum >= numMessages): + print '\n Invalid Message Number.\n' + else: + break + + subject = inboxMessages['inboxMessages'][msgNum]['subject'].decode('base64') + message = inboxMessages['inboxMessages'][msgNum]['message']#Don't decode since it is done in the saveFile function + + elif (uInput == 'o' or uInput == 'outbox'): + outboxMessages = json.loads(api.getAllSentMessages()) + numMessages = len(outboxMessages['sentMessages']) + + while True: + msgNum = int(userInput("What is the number of the message you wish to save?")) + + if (msgNum >= numMessages): + print '\n Invalid Message Number.\n' + else: + break + + subject = outboxMessages['sentMessages'][msgNum]['subject'].decode('base64') + message = outboxMessages['sentMessages'][msgNum]['message']#Don't decode since it is done in the saveFile function + + subject = subject +'.txt' + saveFile(subject,message) + + usrPrompt = 1 + main() + + elif usrInput == "delete": #will delete a message from the system, not reflected on the UI. + + uInput = userInput("Would you like to delete a message from the (I)nbox or (O)utbox?").lower() + + if (uInput == 'i' or uInput == 'inbox'): + inboxMessages = json.loads(api.getAllInboxMessages()) + numMessages = len(inboxMessages['inboxMessages']) + + while True: + msgNum = userInput('Enter the number of the message you wish to delete or (A)ll to empty the inbox.').lower() + + if (msgNum == 'a' or msgNum == 'all'): + break + elif (int(msgNum) >= numMessages): + print '\n Invalid Message Number.\n' + else: + break + + uInput = userInput("Are you sure, (Y)es or (N)o?").lower()#Prevent accidental deletion + + if uInput == "y": + if (msgNum == 'a' or msgNum == 'all'): + print ' ' + for msgNum in range (0, numMessages): #processes all of the messages in the inbox + print ' Deleting message ', msgNum+1, ' of ', numMessages + delMsg(0) + + print '\n Inbox is empty.' + usrPrompt = 1 + else: + delMsg(int(msgNum)) + + print '\n Notice: Message numbers may have changed.\n' + main() + else: + usrPrompt = 1 + elif (uInput == 'o' or uInput == 'outbox'): + outboxMessages = json.loads(api.getAllSentMessages()) + numMessages = len(outboxMessages['sentMessages']) + + while True: + msgNum = userInput('Enter the number of the message you wish to delete or (A)ll to empty the inbox.').lower() + + if (msgNum == 'a' or msgNum == 'all'): + break + elif (int(msgNum) >= numMessages): + print '\n Invalid Message Number.\n' + else: + break + + uInput = userInput("Are you sure, (Y)es or (N)o?").lower()#Prevent accidental deletion + + if uInput == "y": + if (msgNum == 'a' or msgNum == 'all'): + print ' ' + for msgNum in range (0, numMessages): #processes all of the messages in the outbox + print ' Deleting message ', msgNum+1, ' of ', numMessages + delSentMsg(0) + + print '\n Outbox is empty.' + usrPrompt = 1 + else: + delSentMsg(int(msgNum)) + print '\n Notice: Message numbers may have changed.\n' + main() + else: + usrPrompt = 1 + else: + print '\n Invalid Entry.\n' + userPrompt = 1 + main() + + elif usrInput == "exit": + print '\n You are already at the main menu. Use "quit" to quit.\n' + usrPrompt = 1 + main() + + elif usrInput == "listaddressbookentries": + res = listAddressBookEntries() + if res == 20: print '\n Error: API function not supported.\n' + usrPrompt = 1 + main() + + elif usrInput == "addaddressbookentry": + address = userInput('Enter address') + label = userInput('Enter label') + res = addAddressToAddressBook(address, label) + if res == 16: print '\n Error: Address already exists in Address Book.\n' + if res == 20: print '\n Error: API function not supported.\n' + usrPrompt = 1 + main() + + elif usrInput == "deleteaddressbookentry": + address = userInput('Enter address') + res = deleteAddressFromAddressBook(address) + if res == 20: print '\n Error: API function not supported.\n' + usrPrompt = 1 + main() + + elif usrInput == "markallmessagesread": + markAllMessagesRead() + usrPrompt = 1 + main() + + elif usrInput == "markallmessagesunread": + markAllMessagesUnread() + usrPrompt = 1 + main() + + else: + print '\n "',usrInput,'" is not a command.\n' + usrPrompt = 1 + main() + +def main(): + global api + global usrPrompt + + if (usrPrompt == 0): + print '\n ------------------------------' + print ' | Bitmessage Daemon by .dok |' + print ' | Version 0.2.6 for BM 0.3.5 |' + print ' ------------------------------' + api = xmlrpclib.ServerProxy(apiData()) #Connect to BitMessage using these api credentials + + if (apiTest() == False): + print '\n ****************************************************************' + print ' WARNING: You are not connected to the Bitmessage client.' + print ' Either Bitmessage is not running or your settings are incorrect.' + print ' Use the command "apiTest" or "bmSettings" to resolve this issue.' + print ' ****************************************************************\n' + + print 'Type (H)elp for a list of commands.' #Startup message + usrPrompt = 2 + + #if (apiTest() == False):#Preform a connection test #taken out until I get the error handler working + # print '*************************************' + # print 'WARNING: No connection to Bitmessage.' + # print '*************************************' + # print ' ' + elif (usrPrompt == 1): + print '\nType (H)elp for a list of commands.' #Startup message + usrPrompt = 2 + + try: + UI((raw_input('>').lower()).replace(" ", "")) + except EOFError: + UI("quit") + +if __name__ == "__main__": + main() From 505c5689c2488c36296b3c37e2625daffca15a7f Mon Sep 17 00:00:00 2001 From: sbkaf Date: Tue, 3 Mar 2015 18:17:56 +0100 Subject: [PATCH 003/210] make interface look more like an e-mail programm, not yet functional --- src/bitmessageqt/__init__.py | 220 +++-- src/bitmessageqt/bitmessageui.py | 640 +++++++------- src/bitmessageqt/bitmessageui.ui | 1419 +++++++++++++++--------------- 3 files changed, 1152 insertions(+), 1127 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 3b424c8e..4596525e 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -199,13 +199,11 @@ class MyForm(QtGui.QMainWindow): QtCore.SIGNAL( "triggered()"), self.click_actionRegenerateDeterministicAddresses) - QtCore.QObject.connect(self.ui.actionJoinChan, QtCore.SIGNAL( - "triggered()"), + QtCore.QObject.connect(self.ui.pushButtonAddChanel, QtCore.SIGNAL( + "clicked()"), self.click_actionJoinChan) # also used for creating chans. QtCore.QObject.connect(self.ui.pushButtonNewAddress, QtCore.SIGNAL( "clicked()"), self.click_NewAddressDialog) - QtCore.QObject.connect(self.ui.comboBoxSendFrom, QtCore.SIGNAL( - "activated(int)"), self.redrawLabelFrom) QtCore.QObject.connect(self.ui.pushButtonAddAddressBook, QtCore.SIGNAL( "clicked()"), self.click_pushButtonAddAddressBook) QtCore.QObject.connect(self.ui.pushButtonAddSubscription, QtCore.SIGNAL( @@ -214,12 +212,8 @@ class MyForm(QtGui.QMainWindow): "clicked()"), self.click_pushButtonAddBlacklist) QtCore.QObject.connect(self.ui.pushButtonSend, QtCore.SIGNAL( "clicked()"), self.click_pushButtonSend) - QtCore.QObject.connect(self.ui.pushButtonLoadFromAddressBook, - QtCore.SIGNAL( - "clicked()"), - self.click_pushButtonLoadFromAddressBook) - QtCore.QObject.connect(self.ui.pushButtonFetchNamecoinID, QtCore.SIGNAL( - "clicked()"), self.click_pushButtonFetchNamecoinID) + #QtCore.QObject.connect(self.ui.pushButtonFetchNamecoinID, QtCore.SIGNAL( + #"clicked()"), self.click_pushButtonFetchNamecoinID) QtCore.QObject.connect(self.ui.radioButtonBlacklist, QtCore.SIGNAL( "clicked()"), self.click_radioButtonBlacklist) QtCore.QObject.connect(self.ui.radioButtonWhitelist, QtCore.SIGNAL( @@ -297,12 +291,14 @@ class MyForm(QtGui.QMainWindow): _translate( "MainWindow", "Special address behavior..."), self.on_action_SpecialAddressBehaviorDialog) + self.ui.tableWidgetYourIdentities.setContextMenuPolicy( QtCore.Qt.CustomContextMenu) if connectSignal: self.connect(self.ui.tableWidgetYourIdentities, QtCore.SIGNAL( 'customContextMenuRequested(const QPoint&)'), self.on_context_menuYourIdentities) + self.popMenu = QtGui.QMenu(self) self.popMenu.addAction(self.actionNew) self.popMenu.addSeparator() @@ -405,12 +401,6 @@ class MyForm(QtGui.QMainWindow): self.actionForceSend = self.ui.sentContextMenuToolbar.addAction( _translate( "MainWindow", "Force send"), self.on_action_ForceSend) - self.ui.tableWidgetSent.setContextMenuPolicy( - QtCore.Qt.CustomContextMenu) - if connectSignal: - self.connect(self.ui.tableWidgetSent, QtCore.SIGNAL( - 'customContextMenuRequested(const QPoint&)'), - self.on_context_menuSent) # self.popMenuSent = QtGui.QMenu( self ) # self.popMenuSent.addAction( self.actionSentClipboard ) # self.popMenuSent.addAction( self.actionTrashSentMessage ) @@ -498,8 +488,6 @@ class MyForm(QtGui.QMainWindow): self.totalNumberOfBytesReceived = 0 self.totalNumberOfBytesSent = 0 - self.ui.labelSendBroadcastWarning.setVisible(False) - self.timer = QtCore.QTimer() self.timer.start(2000) # milliseconds QtCore.QObject.connect(self.timer, QtCore.SIGNAL("timeout()"), self.runEveryTwoSeconds) @@ -512,7 +500,7 @@ class MyForm(QtGui.QMainWindow): self.init_sent_popup_menu() self.init_blacklist_popup_menu() - # Initialize the user's list of addresses on the 'Your Identities' tab. + # Initialize the user's list of addresses on the 'Your Identities' table. configSections = shared.config.sections() for addressInKeysFile in configSections: if addressInKeysFile != 'bitmessagesettings': @@ -546,6 +534,26 @@ class MyForm(QtGui.QMainWindow): status, addressVersionNumber, streamNumber, hash = decodeAddress( addressInKeysFile) + # Initialize the user's list of addresses on the 'Received' tab. + configSections = shared.config.sections() + for addressInKeysFile in configSections: + if addressInKeysFile != 'bitmessagesettings': + isEnabled = shared.config.getboolean( + addressInKeysFile, 'enabled') + + if isEnabled and not shared.safeConfigGetBoolean(addressInKeysFile, 'chan') and not shared.safeConfigGetBoolean(addressInKeysFile, 'mailinglist'): + newItem = QtGui.QTreeWidgetItem(self.ui.treeWidgetYourIdentities) + newItem.setIcon(0, avatarize(addressInKeysFile)) + newItem.setText(0, unicode( + shared.config.get(addressInKeysFile, 'label'), 'utf-8)') + + ' (' + addressInKeysFile + ')') + newSubItem = QtGui.QTreeWidgetItem(newItem) + newSubItem.setText(0, _translate("MainWindow", "inbox")) + newSubItem = QtGui.QTreeWidgetItem(newItem) + newSubItem.setText(0, _translate("MainWindow", "sent")) + newSubItem = QtGui.QTreeWidgetItem(newItem) + newSubItem.setText(0, _translate("MainWindow", "trash")) + # Load inbox from messages database file self.loadInbox() @@ -562,10 +570,6 @@ class MyForm(QtGui.QMainWindow): QtCore.QObject.connect(self.ui.inboxSearchLineEdit, QtCore.SIGNAL( "returnPressed()"), self.inboxSearchLineEditPressed) - # Initialize the sent search - QtCore.QObject.connect(self.ui.sentSearchLineEdit, QtCore.SIGNAL( - "returnPressed()"), self.sentSearchLineEditPressed) - # Initialize the Blacklist or Whitelist if shared.config.get('bitmessagesettings', 'blackwhitelist') == 'white': self.ui.tabWidget.setTabText(6, 'Whitelist') @@ -580,8 +584,6 @@ class MyForm(QtGui.QMainWindow): "itemChanged(QTableWidgetItem *)"), self.tableWidgetSubscriptionsItemChanged) QtCore.QObject.connect(self.ui.tableWidgetInbox, QtCore.SIGNAL( "itemSelectionChanged ()"), self.tableWidgetInboxItemClicked) - QtCore.QObject.connect(self.ui.tableWidgetSent, QtCore.SIGNAL( - "itemSelectionChanged ()"), self.tableWidgetSentItemClicked) # Put the colored icon on the status bar # self.ui.pushButtonStatusIcon.setIcon(QIcon(":/newPrefix/images/yellowicon.png")) @@ -596,8 +598,8 @@ class MyForm(QtGui.QMainWindow): # Set the icon sizes for the identicons identicon_size = 3*7 self.ui.tableWidgetInbox.setIconSize(QtCore.QSize(identicon_size, identicon_size)) - self.ui.tableWidgetSent.setIconSize(QtCore.QSize(identicon_size, identicon_size)) self.ui.tableWidgetYourIdentities.setIconSize(QtCore.QSize(identicon_size, identicon_size)) + self.ui.treeWidgetYourIdentities.setIconSize(QtCore.QSize(identicon_size, identicon_size)) self.ui.tableWidgetSubscriptions.setIconSize(QtCore.QSize(identicon_size, identicon_size)) self.ui.tableWidgetAddressBook.setIconSize(QtCore.QSize(identicon_size, identicon_size)) self.ui.tableWidgetBlacklist.setIconSize(QtCore.QSize(identicon_size, identicon_size)) @@ -657,11 +659,11 @@ class MyForm(QtGui.QMainWindow): options["user"] = shared.config.get('bitmessagesettings', 'namecoinrpcuser') options["password"] = shared.config.get('bitmessagesettings', 'namecoinrpcpassword') nc = namecoinConnection(options) - if nc.test()[0] == 'failed': - self.ui.pushButtonFetchNamecoinID.hide() + #if nc.test()[0] == 'failed': + #self.ui.pushButtonFetchNamecoinID.hide() except: print 'There was a problem testing for a Namecoin daemon. Hiding the Fetch Namecoin ID button' - self.ui.pushButtonFetchNamecoinID.hide() + #self.ui.pushButtonFetchNamecoinID.hide() # Show or hide the application window after clicking an item within the @@ -751,12 +753,12 @@ class MyForm(QtGui.QMainWindow): # Show the program window and select subscriptions tab def appIndicatorSubscribe(self): self.appIndicatorShow() - self.ui.tabWidget.setCurrentIndex(4) + self.ui.tabWidget.setCurrentIndex(2) - # Show the program window and select the address book tab - def appIndicatorAddressBook(self): + # Show the program window and select chanels tab + def appIndicatorChanel(self): self.appIndicatorShow() - self.ui.tabWidget.setCurrentIndex(5) + self.ui.tabWidget.setCurrentIndex(3) # Load Sent items from database def loadSent(self, where="", what=""): @@ -778,8 +780,8 @@ class MyForm(QtGui.QMainWindow): ORDER BY lastactiontime ''' % (where,) - while self.ui.tableWidgetSent.rowCount() > 0: - self.ui.tableWidgetSent.removeRow(0) + while self.ui.tableWidgetInbox.rowCount() > 0: + self.ui.tableWidgetInbox.removeRow(0) queryreturn = sqlQuery(sqlStatement, what) for row in queryreturn: @@ -813,14 +815,14 @@ class MyForm(QtGui.QMainWindow): if toLabel == '': toLabel = toAddress - self.ui.tableWidgetSent.insertRow(0) + self.ui.tableWidgetInbox.insertRow(0) toAddressItem = QtGui.QTableWidgetItem(unicode(toLabel, 'utf-8')) toAddressItem.setToolTip(unicode(toLabel, 'utf-8')) toAddressItem.setIcon(avatarize(toAddress)) toAddressItem.setData(Qt.UserRole, str(toAddress)) toAddressItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) - self.ui.tableWidgetSent.setItem(0, 0, toAddressItem) + self.ui.tableWidgetInbox.setItem(0, 0, toAddressItem) if fromLabel == '': fromLabel = fromAddress @@ -830,13 +832,13 @@ class MyForm(QtGui.QMainWindow): fromAddressItem.setData(Qt.UserRole, str(fromAddress)) fromAddressItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) - self.ui.tableWidgetSent.setItem(0, 1, fromAddressItem) + self.ui.tableWidgetInbox.setItem(0, 1, fromAddressItem) subjectItem = QtGui.QTableWidgetItem(unicode(subject, 'utf-8')) subjectItem.setToolTip(unicode(subject, 'utf-8')) subjectItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) - self.ui.tableWidgetSent.setItem(0, 2, subjectItem) + self.ui.tableWidgetInbox.setItem(0, 2, subjectItem) if status == 'awaitingpubkey': statusText = _translate( @@ -883,9 +885,9 @@ class MyForm(QtGui.QMainWindow): newItem.setData(33, int(lastactiontime)) newItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) - self.ui.tableWidgetSent.setItem(0, 3, newItem) - self.ui.tableWidgetSent.sortItems(3, Qt.DescendingOrder) - self.ui.tableWidgetSent.keyPressEvent = self.tableWidgetSentKeyPressEvent + self.ui.tableWidgetInbox.setItem(0, 3, newItem) + self.ui.tableWidgetInbox.sortItems(3, Qt.DescendingOrder) + self.ui.tableWidgetInbox.keyPressEvent = self.tableWidgetInboxKeyPressEvent # Load inbox from messages database file def loadInbox(self, where="", what=""): @@ -1035,11 +1037,11 @@ class MyForm(QtGui.QMainWindow): actionSubscribe.triggered.connect(self.appIndicatorSubscribe) m.addAction(actionSubscribe) - # Address book - actionAddressBook = QtGui.QAction(_translate( - "MainWindow", "Address Book"), m, checkable=False) - actionAddressBook.triggered.connect(self.appIndicatorAddressBook) - m.addAction(actionAddressBook) + # Chanels + actionSubscribe = QtGui.QAction(_translate( + "MainWindow", "Chanel"), m, checkable=False) + actionSubscribe.triggered.connect(self.appIndicatorChanel) + m.addAction(actionSubscribe) # separator actionSeparator = QtGui.QAction('', m, checkable=False) @@ -1316,16 +1318,13 @@ class MyForm(QtGui.QMainWindow): else: self.tray.showMessage(title, subtitle, 1, 2000) + # set delete key in inbox def tableWidgetInboxKeyPressEvent(self, event): if event.key() == QtCore.Qt.Key_Delete: self.on_action_InboxTrash() return QtGui.QTableWidget.keyPressEvent(self.ui.tableWidgetInbox, event) - def tableWidgetSentKeyPressEvent(self, event): - if event.key() == QtCore.Qt.Key_Delete: - self.on_action_SentTrash() - return QtGui.QTableWidget.keyPressEvent(self.ui.tableWidgetSent, event) - + # menu button 'manage keys' def click_actionManageKeys(self): if 'darwin' in sys.platform or 'linux' in sys.platform: if shared.appdata == '': @@ -1349,11 +1348,13 @@ class MyForm(QtGui.QMainWindow): if reply == QtGui.QMessageBox.Yes: shared.openKeysFile() + # menu button 'delete all treshed messages' def click_actionDeleteAllTrashedMessages(self): if QtGui.QMessageBox.question(self, _translate("MainWindow", "Delete trash?"), _translate("MainWindow", "Are you sure you want to delete all trashed messages?"), QtGui.QMessageBox.Yes, QtGui.QMessageBox.No) == QtGui.QMessageBox.No: return sqlStoredProcedure('deleteandvacuume') + # menu botton 'regenerate deterministic addresses' def click_actionRegenerateDeterministicAddresses(self): self.regenerateAddressesDialogInstance = regenerateAddressesDialog( self) @@ -1379,6 +1380,7 @@ class MyForm(QtGui.QMainWindow): ), self.regenerateAddressesDialogInstance.ui.lineEditPassphrase.text().toUtf8(), self.regenerateAddressesDialogInstance.ui.checkBoxEighteenByteRipe.isChecked())) self.ui.tabWidget.setCurrentIndex(3) + # opens 'join chan' dialog def click_actionJoinChan(self): self.newChanDialogInstance = newChanDialog(self) if self.newChanDialogInstance.exec_(): @@ -1505,7 +1507,6 @@ class MyForm(QtGui.QMainWindow): shared.numberOfBytesSent = 0 def updateNetworkStatusTab(self): - # print 'updating network status tab' totalNumberOfConnectionsFromAllStreams = 0 # One would think we could use len(sendDataQueues) for this but the number doesn't always match: just because we have a sendDataThread running doesn't mean that the connection has been fully established (with the exchange of version messages). streamNumberTotals = {} for host, streamNumber in shared.connectedHostsList.items(): @@ -1667,42 +1668,42 @@ class MyForm(QtGui.QMainWindow): return int(cnt) def updateSentItemStatusByHash(self, toRipe, textToDisplay): - for i in range(self.ui.tableWidgetSent.rowCount()): - toAddress = str(self.ui.tableWidgetSent.item( + for i in range(self.ui.tableWidgetInbox.rowCount()): + toAddress = str(self.ui.tableWidgetInbox.item( i, 0).data(Qt.UserRole).toPyObject()) status, addressVersionNumber, streamNumber, ripe = decodeAddress( toAddress) if ripe == toRipe: - self.ui.tableWidgetSent.item(i, 3).setToolTip(textToDisplay) + self.ui.tableWidgetInbox.item(i, 3).setToolTip(textToDisplay) try: newlinePosition = textToDisplay.indexOf('\n') except: # If someone misses adding a "_translate" to a string before passing it to this function, this function won't receive a qstring which will cause an exception. newlinePosition = 0 if newlinePosition > 1: - self.ui.tableWidgetSent.item(i, 3).setText( + self.ui.tableWidgetInbox.item(i, 3).setText( textToDisplay[:newlinePosition]) else: - self.ui.tableWidgetSent.item(i, 3).setText(textToDisplay) + self.ui.tableWidgetInbox.item(i, 3).setText(textToDisplay) def updateSentItemStatusByAckdata(self, ackdata, textToDisplay): - for i in range(self.ui.tableWidgetSent.rowCount()): - toAddress = str(self.ui.tableWidgetSent.item( + for i in range(self.ui.tableWidgetInbox.rowCount()): + toAddress = str(self.ui.tableWidgetInbox.item( i, 0).data(Qt.UserRole).toPyObject()) - tableAckdata = self.ui.tableWidgetSent.item( + tableAckdata = self.ui.tableWidgetInbox.item( i, 3).data(Qt.UserRole).toPyObject() status, addressVersionNumber, streamNumber, ripe = decodeAddress( toAddress) if ackdata == tableAckdata: - self.ui.tableWidgetSent.item(i, 3).setToolTip(textToDisplay) + self.ui.tableWidgetInbox.item(i, 3).setToolTip(textToDisplay) try: newlinePosition = textToDisplay.indexOf('\n') except: # If someone misses adding a "_translate" to a string before passing it to this function, this function won't receive a qstring which will cause an exception. newlinePosition = 0 if newlinePosition > 1: - self.ui.tableWidgetSent.item(i, 3).setText( + self.ui.tableWidgetInbox.item(i, 3).setText( textToDisplay[:newlinePosition]) else: - self.ui.tableWidgetSent.item(i, 3).setText(textToDisplay) + self.ui.tableWidgetInbox.item(i, 3).setText(textToDisplay) def removeInboxRowByMsgid(self, msgid): # msgid and inventoryHash are the same thing for i in range(self.ui.tableWidgetInbox.rowCount()): @@ -1783,22 +1784,22 @@ class MyForm(QtGui.QMainWindow): i, 0).setTextColor(QApplication.palette().text().color()) def rerenderSentFromLabels(self): - for i in range(self.ui.tableWidgetSent.rowCount()): - fromAddress = str(self.ui.tableWidgetSent.item( + for i in range(self.ui.tableWidgetInbox.rowCount()): + fromAddress = str(self.ui.tableWidgetInbox.item( i, 1).data(Qt.UserRole).toPyObject()) # Message might be from an address we own like a chan address. Let's look for that label. if shared.config.has_section(fromAddress): fromLabel = shared.config.get(fromAddress, 'label') else: fromLabel = fromAddress - self.ui.tableWidgetSent.item( + self.ui.tableWidgetInbox.item( i, 1).setText(unicode(fromLabel, 'utf-8')) - self.ui.tableWidgetSent.item( + self.ui.tableWidgetInbox.item( i, 1).setIcon(avatarize(fromAddress)) def rerenderSentToLabels(self): - for i in range(self.ui.tableWidgetSent.rowCount()): - addressToLookup = str(self.ui.tableWidgetSent.item( + for i in range(self.ui.tableWidgetInbox.rowCount()): + addressToLookup = str(self.ui.tableWidgetInbox.item( i, 0).data(Qt.UserRole).toPyObject()) toLabel = '' queryreturn = sqlQuery( @@ -1813,7 +1814,7 @@ class MyForm(QtGui.QMainWindow): toLabel = shared.config.get(addressToLookup, 'label') if toLabel == '': toLabel = addressToLookup - self.ui.tableWidgetSent.item( + self.ui.tableWidgetInbox.item( i, 0).setText(unicode(toLabel, 'utf-8')) def rerenderAddressBook(self): @@ -1873,7 +1874,6 @@ class MyForm(QtGui.QMainWindow): def click_pushButtonSend(self): self.statusBar().showMessage('') toAddresses = str(self.ui.lineEditTo.text()) - fromAddress = str(self.ui.labelFrom.text()) subject = str(self.ui.lineEditSubject.text().toUtf8()) message = str( self.ui.textEditMessage.document().toPlainText().toUtf8()) @@ -1972,12 +1972,11 @@ class MyForm(QtGui.QMainWindow): shared.workerQueue.put(('sendmessage', toAddress)) self.ui.comboBoxSendFrom.setCurrentIndex(0) - self.ui.labelFrom.setText('') self.ui.lineEditTo.setText('') self.ui.lineEditSubject.setText('') self.ui.textEditMessage.setText('') self.ui.tabWidget.setCurrentIndex(2) - self.ui.tableWidgetSent.setCurrentCell(0, 0) + self.ui.tableWidgetInbox.setCurrentCell(0, 0) else: self.statusBar().showMessage(_translate( "MainWindow", "Your \'To\' field is empty.")) @@ -2006,12 +2005,11 @@ class MyForm(QtGui.QMainWindow): shared.workerQueue.put(('sendbroadcast', '')) self.ui.comboBoxSendFrom.setCurrentIndex(0) - self.ui.labelFrom.setText('') self.ui.lineEditTo.setText('') self.ui.lineEditSubject.setText('') self.ui.textEditMessage.setText('') self.ui.tabWidget.setCurrentIndex(2) - self.ui.tableWidgetSent.setCurrentCell(0, 0) + self.ui.tableWidgetInbox.setCurrentCell(0, 0) def click_pushButtonLoadFromAddressBook(self): self.ui.tabWidget.setCurrentIndex(5) @@ -2033,11 +2031,6 @@ class MyForm(QtGui.QMainWindow): self.statusBar().showMessage(_translate( "MainWindow", "Fetched address from namecoin identity.")) - def redrawLabelFrom(self, index): - self.ui.labelFrom.setText( - self.ui.comboBoxSendFrom.itemData(index).toPyObject()) - self.setBroadcastEnablementDependingOnWhetherThisIsAChanAddress(self.ui.comboBoxSendFrom.itemData(index).toPyObject()) - def setBroadcastEnablementDependingOnWhetherThisIsAChanAddress(self, address): # If this is a chan then don't let people broadcast because no one # should subscribe to chan addresses. @@ -2049,7 +2042,6 @@ class MyForm(QtGui.QMainWindow): def rerenderComboBoxSendFrom(self): self.ui.comboBoxSendFrom.clear() - self.ui.labelFrom.setText('') configSections = shared.config.sections() for addressInKeysFile in configSections: if addressInKeysFile != 'bitmessagesettings': @@ -2061,7 +2053,6 @@ class MyForm(QtGui.QMainWindow): self.ui.comboBoxSendFrom.insertItem(0, '', '') if(self.ui.comboBoxSendFrom.count() == 2): self.ui.comboBoxSendFrom.setCurrentIndex(1) - self.redrawLabelFrom(self.ui.comboBoxSendFrom.currentIndex()) else: self.ui.comboBoxSendFrom.setCurrentIndex(0) @@ -2079,8 +2070,8 @@ class MyForm(QtGui.QMainWindow): if fromLabel == '': fromLabel = fromAddress - self.ui.tableWidgetSent.setSortingEnabled(False) - self.ui.tableWidgetSent.insertRow(0) + self.ui.tableWidgetInbox.setSortingEnabled(False) + self.ui.tableWidgetInbox.insertRow(0) if toLabel == '': newItem = QtGui.QTableWidgetItem(unicode(toAddress, 'utf-8')) newItem.setToolTip(unicode(toAddress, 'utf-8')) @@ -2089,7 +2080,7 @@ class MyForm(QtGui.QMainWindow): newItem.setToolTip(unicode(toLabel, 'utf-8')) newItem.setData(Qt.UserRole, str(toAddress)) newItem.setIcon(avatarize(toAddress)) - self.ui.tableWidgetSent.setItem(0, 0, newItem) + self.ui.tableWidgetInbox.setItem(0, 0, newItem) if fromLabel == '': newItem = QtGui.QTableWidgetItem(unicode(fromAddress, 'utf-8')) newItem.setToolTip(unicode(fromAddress, 'utf-8')) @@ -2098,11 +2089,11 @@ class MyForm(QtGui.QMainWindow): newItem.setToolTip(unicode(fromLabel, 'utf-8')) newItem.setData(Qt.UserRole, str(fromAddress)) newItem.setIcon(avatarize(fromAddress)) - self.ui.tableWidgetSent.setItem(0, 1, newItem) + self.ui.tableWidgetInbox.setItem(0, 1, newItem) newItem = QtGui.QTableWidgetItem(unicode(subject, 'utf-8)')) newItem.setToolTip(unicode(subject, 'utf-8)')) #newItem.setData(Qt.UserRole, unicode(message, 'utf-8)')) # No longer hold the message in the table; we'll use a SQL query to display it as needed. - self.ui.tableWidgetSent.setItem(0, 2, newItem) + self.ui.tableWidgetInbox.setItem(0, 2, newItem) # newItem = QtGui.QTableWidgetItem('Doing work necessary to send # broadcast...'+ # l10n.formatTimestamp()) @@ -2110,9 +2101,9 @@ class MyForm(QtGui.QMainWindow): newItem.setToolTip(_translate("MainWindow", "Work is queued. %1").arg(l10n.formatTimestamp())) newItem.setData(Qt.UserRole, QByteArray(ackdata)) newItem.setData(33, int(time.time())) - self.ui.tableWidgetSent.setItem(0, 3, newItem) + self.ui.tableWidgetInbox.setItem(0, 3, newItem) self.ui.textEditSentMessage.setPlainText(unicode(message, 'utf-8)')) - self.ui.tableWidgetSent.setSortingEnabled(True) + self.ui.tableWidgetInbox.setSortingEnabled(True) def displayNewInboxMessage(self, inventoryHash, toAddress, fromAddress, subject, message): subject = shared.fixPotentiallyInvalidUTF8Data(subject) @@ -2730,17 +2721,15 @@ class MyForm(QtGui.QMainWindow): for row in queryreturn: messageAtCurrentInboxRow, = row if toAddressAtCurrentInboxRow == self.str_broadcast_subscribers: - self.ui.labelFrom.setText('') + #TODO what does this if?.. + a = a elif not shared.config.has_section(toAddressAtCurrentInboxRow): QtGui.QMessageBox.information(self, _translate("MainWindow", "Address is gone"), _translate( "MainWindow", "Bitmessage cannot find your address %1. Perhaps you removed it?").arg(toAddressAtCurrentInboxRow), QMessageBox.Ok) - self.ui.labelFrom.setText('') elif not shared.config.getboolean(toAddressAtCurrentInboxRow, 'enabled'): QtGui.QMessageBox.information(self, _translate("MainWindow", "Address disabled"), _translate( "MainWindow", "Error: The address from which you are trying to send is disabled. You\'ll have to enable it on the \'Your Identities\' tab before using it."), QMessageBox.Ok) - self.ui.labelFrom.setText('') else: - self.ui.labelFrom.setText(toAddressAtCurrentInboxRow) self.setBroadcastEnablementDependingOnWhetherThisIsAChanAddress(toAddressAtCurrentInboxRow) self.ui.lineEditTo.setText(str(fromAddressAtCurrentInboxRow)) @@ -2844,23 +2833,23 @@ class MyForm(QtGui.QMainWindow): # Send item on the Sent tab to trash def on_action_SentTrash(self): - while self.ui.tableWidgetSent.selectedIndexes() != []: - currentRow = self.ui.tableWidgetSent.selectedIndexes()[0].row() - ackdataToTrash = str(self.ui.tableWidgetSent.item( + while self.ui.tableWidgetInbox.selectedIndexes() != []: + currentRow = self.ui.tableWidgetInbox.selectedIndexes()[0].row() + ackdataToTrash = str(self.ui.tableWidgetInbox.item( currentRow, 3).data(Qt.UserRole).toPyObject()) sqlExecute('''UPDATE sent SET folder='trash' WHERE ackdata=?''', ackdataToTrash) self.ui.textEditSentMessage.setPlainText("") - self.ui.tableWidgetSent.removeRow(currentRow) + self.ui.tableWidgetInbox.removeRow(currentRow) self.statusBar().showMessage(_translate( "MainWindow", "Moved items to trash. There is no user interface to view your trash, but it is still on disk if you are desperate to get it back.")) if currentRow == 0: - self.ui.tableWidgetSent.selectRow(currentRow) + self.ui.tableWidgetInbox.selectRow(currentRow) else: - self.ui.tableWidgetSent.selectRow(currentRow - 1) + self.ui.tableWidgetInbox.selectRow(currentRow - 1) def on_action_ForceSend(self): - currentRow = self.ui.tableWidgetSent.currentRow() - addressAtCurrentRow = str(self.ui.tableWidgetSent.item( + currentRow = self.ui.tableWidgetInbox.currentRow() + addressAtCurrentRow = str(self.ui.tableWidgetInbox.item( currentRow, 0).data(Qt.UserRole).toPyObject()) toRipe = decodeAddress(addressAtCurrentRow)[3] sqlExecute( @@ -2874,8 +2863,8 @@ class MyForm(QtGui.QMainWindow): shared.workerQueue.put(('sendmessage', '')) def on_action_SentClipboard(self): - currentRow = self.ui.tableWidgetSent.currentRow() - addressAtCurrentRow = str(self.ui.tableWidgetSent.item( + currentRow = self.ui.tableWidgetInbox.currentRow() + addressAtCurrentRow = str(self.ui.tableWidgetInbox.item( currentRow, 0).data(Qt.UserRole).toPyObject()) clipboard = QtGui.QApplication.clipboard() clipboard.setText(str(addressAtCurrentRow)) @@ -3213,15 +3202,15 @@ class MyForm(QtGui.QMainWindow): # Check to see if this item is toodifficult and display an additional # menu option (Force Send) if it is. - currentRow = self.ui.tableWidgetSent.currentRow() - ackData = str(self.ui.tableWidgetSent.item( + currentRow = self.ui.tableWidgetInbox.currentRow() + ackData = str(self.ui.tableWidgetInbox.item( currentRow, 3).data(Qt.UserRole).toPyObject()) queryreturn = sqlQuery('''SELECT status FROM sent where ackdata=?''', ackData) for row in queryreturn: status, = row if status == 'toodifficult': self.popMenuSent.addAction(self.actionForceSend) - self.popMenuSent.exec_(self.ui.tableWidgetSent.mapToGlobal(point)) + self.popMenuSent.exec_(self.ui.tableWidgetInbox.mapToGlobal(point)) def inboxSearchLineEditPressed(self): searchKeyword = self.ui.inboxSearchLineEdit.text().toUtf8().data() @@ -3230,13 +3219,6 @@ class MyForm(QtGui.QMainWindow): self.ui.textEditInboxMessage.setPlainText(QString("")) self.loadInbox(searchOption, searchKeyword) - def sentSearchLineEditPressed(self): - searchKeyword = self.ui.sentSearchLineEdit.text().toUtf8().data() - searchOption = self.ui.sentSearchOptionCB.currentText().toUtf8().data() - self.ui.sentSearchLineEdit.setText(QString("")) - self.ui.textEditInboxMessage.setPlainText(QString("")) - self.loadSent(searchOption, searchKeyword) - def tableWidgetInboxItemClicked(self): currentRow = self.ui.tableWidgetInbox.currentRow() if currentRow >= 0: @@ -3284,10 +3266,10 @@ class MyForm(QtGui.QMainWindow): sqlExecute('''update inbox set read=1 WHERE msgid=?''', inventoryHash) self.changedInboxUnread() - def tableWidgetSentItemClicked(self): - currentRow = self.ui.tableWidgetSent.currentRow() + def tableWidgetInboxItemClicked(self): + currentRow = self.ui.tableWidgetInbox.currentRow() if currentRow >= 0: - ackdata = str(self.ui.tableWidgetSent.item( + ackdata = str(self.ui.tableWidgetInbox.item( currentRow, 3).data(Qt.UserRole).toPyObject()) queryreturn = sqlQuery( '''select message from sent where ackdata=?''', ackdata) @@ -3615,8 +3597,8 @@ class settingsDialog(QtGui.QDialog): responseStatus = response[0] responseText = response[1] self.ui.labelNamecoinTestResult.setText(responseText) - if responseStatus== 'success': - self.parent.ui.pushButtonFetchNamecoinID.show() + #if responseStatus== 'success': + #self.parent.ui.pushButtonFetchNamecoinID.show() class SpecialAddressBehaviorDialog(QtGui.QDialog): diff --git a/src/bitmessageqt/bitmessageui.py b/src/bitmessageqt/bitmessageui.py index 318c1caf..f3a600f6 100644 --- a/src/bitmessageqt/bitmessageui.py +++ b/src/bitmessageqt/bitmessageui.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- -# Form implementation generated from reading ui file 'bitmessageui.ui' +# Form implementation generated from reading ui file 'bmail.ui' # -# Created: Mon Jan 05 16:21:20 2015 -# by: PyQt4 UI code generator 4.10.3 +# Created: Sun Mar 1 23:18:09 2015 +# by: PyQt4 UI code generator 4.10.4 # # WARNING! All changes made in this file will be lost! @@ -33,9 +33,8 @@ class Ui_MainWindow(object): MainWindow.setTabShape(QtGui.QTabWidget.Rounded) self.centralwidget = QtGui.QWidget(MainWindow) self.centralwidget.setObjectName(_fromUtf8("centralwidget")) - self.gridLayout = QtGui.QGridLayout(self.centralwidget) - self.gridLayout.setMargin(0) - self.gridLayout.setObjectName(_fromUtf8("gridLayout")) + self.gridLayout_10 = QtGui.QGridLayout(self.centralwidget) + self.gridLayout_10.setObjectName(_fromUtf8("gridLayout_10")) self.tabWidget = QtGui.QTabWidget(self.centralwidget) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) sizePolicy.setHorizontalStretch(0) @@ -52,27 +51,34 @@ class Ui_MainWindow(object): self.tabWidget.setObjectName(_fromUtf8("tabWidget")) self.inbox = QtGui.QWidget() self.inbox.setObjectName(_fromUtf8("inbox")) - self.verticalLayout_2 = QtGui.QVBoxLayout(self.inbox) - self.verticalLayout_2.setObjectName(_fromUtf8("verticalLayout_2")) + self.gridLayout = QtGui.QGridLayout(self.inbox) + self.gridLayout.setObjectName(_fromUtf8("gridLayout")) + self.horizontalLayout_3 = QtGui.QHBoxLayout() + self.horizontalLayout_3.setObjectName(_fromUtf8("horizontalLayout_3")) + self.verticalLayout_12 = QtGui.QVBoxLayout() + self.verticalLayout_12.setObjectName(_fromUtf8("verticalLayout_12")) + self.treeWidgetYourIdentities = QtGui.QTreeWidget(self.inbox) + self.treeWidgetYourIdentities.setMaximumSize(QtCore.QSize(200, 16777215)) + self.treeWidgetYourIdentities.setObjectName(_fromUtf8("treeWidgetYourIdentities")) + icon1 = QtGui.QIcon() + icon1.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/identities.png")), QtGui.QIcon.Selected, QtGui.QIcon.Off) + self.treeWidgetYourIdentities.headerItem().setIcon(0, icon1) + self.verticalLayout_12.addWidget(self.treeWidgetYourIdentities) + self.pushButtonNewAddress = QtGui.QPushButton(self.inbox) + self.pushButtonNewAddress.setMaximumSize(QtCore.QSize(200, 16777215)) + self.pushButtonNewAddress.setObjectName(_fromUtf8("pushButtonNewAddress")) + self.verticalLayout_12.addWidget(self.pushButtonNewAddress) + self.horizontalLayout_3.addLayout(self.verticalLayout_12) + self.verticalLayout_11 = QtGui.QVBoxLayout() + self.verticalLayout_11.setObjectName(_fromUtf8("verticalLayout_11")) self.horizontalLayoutSearch = QtGui.QHBoxLayout() self.horizontalLayoutSearch.setContentsMargins(-1, 0, -1, -1) self.horizontalLayoutSearch.setObjectName(_fromUtf8("horizontalLayoutSearch")) self.inboxSearchLineEdit = QtGui.QLineEdit(self.inbox) self.inboxSearchLineEdit.setObjectName(_fromUtf8("inboxSearchLineEdit")) self.horizontalLayoutSearch.addWidget(self.inboxSearchLineEdit) - self.inboxSearchOptionCB = QtGui.QComboBox(self.inbox) - self.inboxSearchOptionCB.setObjectName(_fromUtf8("inboxSearchOptionCB")) - self.inboxSearchOptionCB.addItem(_fromUtf8("")) - self.inboxSearchOptionCB.addItem(_fromUtf8("")) - self.inboxSearchOptionCB.addItem(_fromUtf8("")) - self.inboxSearchOptionCB.addItem(_fromUtf8("")) - self.inboxSearchOptionCB.addItem(_fromUtf8("")) - self.horizontalLayoutSearch.addWidget(self.inboxSearchOptionCB) - self.verticalLayout_2.addLayout(self.horizontalLayoutSearch) - self.splitter = QtGui.QSplitter(self.inbox) - self.splitter.setOrientation(QtCore.Qt.Vertical) - self.splitter.setObjectName(_fromUtf8("splitter")) - self.tableWidgetInbox = QtGui.QTableWidget(self.splitter) + self.verticalLayout_11.addLayout(self.horizontalLayoutSearch) + self.tableWidgetInbox = QtGui.QTableWidget(self.inbox) self.tableWidgetInbox.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers) self.tableWidgetInbox.setAlternatingRowColors(True) self.tableWidgetInbox.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection) @@ -97,151 +103,212 @@ class Ui_MainWindow(object): self.tableWidgetInbox.horizontalHeader().setStretchLastSection(True) self.tableWidgetInbox.verticalHeader().setVisible(False) self.tableWidgetInbox.verticalHeader().setDefaultSectionSize(26) - self.textEditInboxMessage = QtGui.QTextEdit(self.splitter) + self.verticalLayout_11.addWidget(self.tableWidgetInbox) + self.textEditInboxMessage = QtGui.QTextEdit(self.inbox) self.textEditInboxMessage.setBaseSize(QtCore.QSize(0, 500)) self.textEditInboxMessage.setReadOnly(True) self.textEditInboxMessage.setObjectName(_fromUtf8("textEditInboxMessage")) - self.verticalLayout_2.addWidget(self.splitter) - icon1 = QtGui.QIcon() - icon1.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/inbox.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) - self.tabWidget.addTab(self.inbox, icon1, _fromUtf8("")) + self.verticalLayout_11.addWidget(self.textEditInboxMessage) + self.horizontalLayout_3.addLayout(self.verticalLayout_11) + self.gridLayout.addLayout(self.horizontalLayout_3, 0, 0, 1, 1) + icon2 = QtGui.QIcon() + icon2.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/inbox.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.tabWidget.addTab(self.inbox, icon2, _fromUtf8("")) self.send = QtGui.QWidget() self.send.setObjectName(_fromUtf8("send")) - self.gridLayout_2 = QtGui.QGridLayout(self.send) + self.gridLayout_11 = QtGui.QGridLayout(self.send) + self.gridLayout_11.setObjectName(_fromUtf8("gridLayout_11")) + self.horizontalLayout = QtGui.QHBoxLayout() + self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout")) + self.verticalLayout_2 = QtGui.QVBoxLayout() + self.verticalLayout_2.setObjectName(_fromUtf8("verticalLayout_2")) + self.tableWidgetAddressBook = QtGui.QTableWidget(self.send) + self.tableWidgetAddressBook.setMaximumSize(QtCore.QSize(200, 16777215)) + self.tableWidgetAddressBook.setAlternatingRowColors(True) + self.tableWidgetAddressBook.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection) + self.tableWidgetAddressBook.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) + self.tableWidgetAddressBook.setObjectName(_fromUtf8("tableWidgetAddressBook")) + self.tableWidgetAddressBook.setColumnCount(2) + self.tableWidgetAddressBook.setRowCount(0) + item = QtGui.QTableWidgetItem() + icon3 = QtGui.QIcon() + icon3.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/addressbook.png")), QtGui.QIcon.Selected, QtGui.QIcon.Off) + item.setIcon(icon3) + self.tableWidgetAddressBook.setHorizontalHeaderItem(0, item) + item = QtGui.QTableWidgetItem() + self.tableWidgetAddressBook.setHorizontalHeaderItem(1, item) + self.tableWidgetAddressBook.horizontalHeader().setCascadingSectionResizes(True) + self.tableWidgetAddressBook.horizontalHeader().setDefaultSectionSize(200) + self.tableWidgetAddressBook.horizontalHeader().setHighlightSections(False) + self.tableWidgetAddressBook.horizontalHeader().setStretchLastSection(True) + self.tableWidgetAddressBook.verticalHeader().setVisible(False) + self.verticalLayout_2.addWidget(self.tableWidgetAddressBook) + self.pushButtonAddAddressBook = QtGui.QPushButton(self.send) + self.pushButtonAddAddressBook.setMaximumSize(QtCore.QSize(200, 16777215)) + self.pushButtonAddAddressBook.setObjectName(_fromUtf8("pushButtonAddAddressBook")) + self.verticalLayout_2.addWidget(self.pushButtonAddAddressBook) + self.horizontalLayout.addLayout(self.verticalLayout_2) + self.tabWidget_2 = QtGui.QTabWidget(self.send) + self.tabWidget_2.setObjectName(_fromUtf8("tabWidget_2")) + self.tab = QtGui.QWidget() + self.tab.setObjectName(_fromUtf8("tab")) + self.gridLayout_8 = QtGui.QGridLayout(self.tab) + self.gridLayout_8.setObjectName(_fromUtf8("gridLayout_8")) + self.verticalLayout = QtGui.QVBoxLayout() + self.verticalLayout.setObjectName(_fromUtf8("verticalLayout")) + self.gridLayout_2 = QtGui.QGridLayout() self.gridLayout_2.setObjectName(_fromUtf8("gridLayout_2")) - self.pushButtonLoadFromAddressBook = QtGui.QPushButton(self.send) - font = QtGui.QFont() - font.setPointSize(7) - self.pushButtonLoadFromAddressBook.setFont(font) - self.pushButtonLoadFromAddressBook.setObjectName(_fromUtf8("pushButtonLoadFromAddressBook")) - self.gridLayout_2.addWidget(self.pushButtonLoadFromAddressBook, 3, 2, 1, 1) - self.pushButtonFetchNamecoinID = QtGui.QPushButton(self.send) - font = QtGui.QFont() - font.setPointSize(7) - self.pushButtonFetchNamecoinID.setFont(font) - self.pushButtonFetchNamecoinID.setObjectName(_fromUtf8("pushButtonFetchNamecoinID")) - self.gridLayout_2.addWidget(self.pushButtonFetchNamecoinID, 3, 3, 1, 1) - self.label_4 = QtGui.QLabel(self.send) - self.label_4.setObjectName(_fromUtf8("label_4")) - self.gridLayout_2.addWidget(self.label_4, 5, 0, 1, 1) - self.comboBoxSendFrom = QtGui.QComboBox(self.send) + self.comboBoxSendFrom = QtGui.QComboBox(self.tab) self.comboBoxSendFrom.setMinimumSize(QtCore.QSize(300, 0)) self.comboBoxSendFrom.setObjectName(_fromUtf8("comboBoxSendFrom")) - self.gridLayout_2.addWidget(self.comboBoxSendFrom, 2, 1, 1, 1) - self.label_3 = QtGui.QLabel(self.send) - self.label_3.setObjectName(_fromUtf8("label_3")) - self.gridLayout_2.addWidget(self.label_3, 4, 0, 1, 1) - self.labelFrom = QtGui.QLabel(self.send) - self.labelFrom.setText(_fromUtf8("")) - self.labelFrom.setObjectName(_fromUtf8("labelFrom")) - self.gridLayout_2.addWidget(self.labelFrom, 2, 2, 1, 3) - self.radioButtonSpecific = QtGui.QRadioButton(self.send) - self.radioButtonSpecific.setChecked(True) - self.radioButtonSpecific.setObjectName(_fromUtf8("radioButtonSpecific")) - self.gridLayout_2.addWidget(self.radioButtonSpecific, 0, 1, 1, 1) - self.lineEditTo = QtGui.QLineEdit(self.send) - self.lineEditTo.setObjectName(_fromUtf8("lineEditTo")) - self.gridLayout_2.addWidget(self.lineEditTo, 3, 1, 1, 1) - self.textEditMessage = QtGui.QTextEdit(self.send) - self.textEditMessage.setObjectName(_fromUtf8("textEditMessage")) - self.gridLayout_2.addWidget(self.textEditMessage, 5, 1, 2, 5) - self.label = QtGui.QLabel(self.send) + self.gridLayout_2.addWidget(self.comboBoxSendFrom, 0, 1, 1, 1) + self.label = QtGui.QLabel(self.tab) self.label.setObjectName(_fromUtf8("label")) - self.gridLayout_2.addWidget(self.label, 3, 0, 1, 1) - self.label_2 = QtGui.QLabel(self.send) + self.gridLayout_2.addWidget(self.label, 1, 0, 1, 1) + self.lineEditTo = QtGui.QLineEdit(self.tab) + self.lineEditTo.setObjectName(_fromUtf8("lineEditTo")) + self.gridLayout_2.addWidget(self.lineEditTo, 1, 1, 1, 1) + self.label_3 = QtGui.QLabel(self.tab) + self.label_3.setObjectName(_fromUtf8("label_3")) + self.gridLayout_2.addWidget(self.label_3, 2, 0, 1, 1) + self.label_2 = QtGui.QLabel(self.tab) self.label_2.setObjectName(_fromUtf8("label_2")) - self.gridLayout_2.addWidget(self.label_2, 2, 0, 1, 1) - spacerItem = QtGui.QSpacerItem(20, 297, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) - self.gridLayout_2.addItem(spacerItem, 6, 0, 1, 1) - self.radioButtonBroadcast = QtGui.QRadioButton(self.send) - self.radioButtonBroadcast.setObjectName(_fromUtf8("radioButtonBroadcast")) - self.gridLayout_2.addWidget(self.radioButtonBroadcast, 1, 1, 1, 3) - self.lineEditSubject = QtGui.QLineEdit(self.send) + self.gridLayout_2.addWidget(self.label_2, 0, 0, 1, 1) + self.lineEditSubject = QtGui.QLineEdit(self.tab) self.lineEditSubject.setText(_fromUtf8("")) self.lineEditSubject.setObjectName(_fromUtf8("lineEditSubject")) - self.gridLayout_2.addWidget(self.lineEditSubject, 4, 1, 1, 5) - spacerItem1 = QtGui.QSpacerItem(20, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) - self.gridLayout_2.addItem(spacerItem1, 3, 4, 1, 1) - self.pushButtonSend = QtGui.QPushButton(self.send) + self.gridLayout_2.addWidget(self.lineEditSubject, 2, 1, 1, 1) + self.verticalLayout.addLayout(self.gridLayout_2) + self.textEditMessage = QtGui.QTextEdit(self.tab) + self.textEditMessage.setObjectName(_fromUtf8("textEditMessage")) + self.verticalLayout.addWidget(self.textEditMessage) + self.pushButtonSend = QtGui.QPushButton(self.tab) self.pushButtonSend.setObjectName(_fromUtf8("pushButtonSend")) - self.gridLayout_2.addWidget(self.pushButtonSend, 7, 5, 1, 1) - self.labelSendBroadcastWarning = QtGui.QLabel(self.send) - self.labelSendBroadcastWarning.setEnabled(True) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Ignored, QtGui.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.labelSendBroadcastWarning.sizePolicy().hasHeightForWidth()) - self.labelSendBroadcastWarning.setSizePolicy(sizePolicy) - self.labelSendBroadcastWarning.setIndent(-1) - self.labelSendBroadcastWarning.setObjectName(_fromUtf8("labelSendBroadcastWarning")) - self.gridLayout_2.addWidget(self.labelSendBroadcastWarning, 7, 1, 1, 4) - icon2 = QtGui.QIcon() - icon2.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/send.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) - self.tabWidget.addTab(self.send, icon2, _fromUtf8("")) - self.sent = QtGui.QWidget() - self.sent.setObjectName(_fromUtf8("sent")) - self.verticalLayout = QtGui.QVBoxLayout(self.sent) - self.verticalLayout.setObjectName(_fromUtf8("verticalLayout")) - self.horizontalLayout = QtGui.QHBoxLayout() - self.horizontalLayout.setContentsMargins(-1, 0, -1, -1) - self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout")) - self.sentSearchLineEdit = QtGui.QLineEdit(self.sent) - self.sentSearchLineEdit.setObjectName(_fromUtf8("sentSearchLineEdit")) - self.horizontalLayout.addWidget(self.sentSearchLineEdit) - self.sentSearchOptionCB = QtGui.QComboBox(self.sent) - self.sentSearchOptionCB.setObjectName(_fromUtf8("sentSearchOptionCB")) - self.sentSearchOptionCB.addItem(_fromUtf8("")) - self.sentSearchOptionCB.addItem(_fromUtf8("")) - self.sentSearchOptionCB.addItem(_fromUtf8("")) - self.sentSearchOptionCB.addItem(_fromUtf8("")) - self.sentSearchOptionCB.addItem(_fromUtf8("")) - self.horizontalLayout.addWidget(self.sentSearchOptionCB) - self.verticalLayout.addLayout(self.horizontalLayout) - self.splitter_2 = QtGui.QSplitter(self.sent) - self.splitter_2.setOrientation(QtCore.Qt.Vertical) - self.splitter_2.setObjectName(_fromUtf8("splitter_2")) - self.tableWidgetSent = QtGui.QTableWidget(self.splitter_2) - self.tableWidgetSent.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers) - self.tableWidgetSent.setDragDropMode(QtGui.QAbstractItemView.DragDrop) - self.tableWidgetSent.setAlternatingRowColors(True) - self.tableWidgetSent.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection) - self.tableWidgetSent.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) - self.tableWidgetSent.setWordWrap(False) - self.tableWidgetSent.setObjectName(_fromUtf8("tableWidgetSent")) - self.tableWidgetSent.setColumnCount(4) - self.tableWidgetSent.setRowCount(0) + self.verticalLayout.addWidget(self.pushButtonSend) + self.gridLayout_8.addLayout(self.verticalLayout, 0, 0, 1, 1) + self.tabWidget_2.addTab(self.tab, _fromUtf8("")) + self.tab_2 = QtGui.QWidget() + self.tab_2.setObjectName(_fromUtf8("tab_2")) + self.gridLayout_9 = QtGui.QGridLayout(self.tab_2) + self.gridLayout_9.setObjectName(_fromUtf8("gridLayout_9")) + self.verticalLayout_5 = QtGui.QVBoxLayout() + self.verticalLayout_5.setObjectName(_fromUtf8("verticalLayout_5")) + self.gridLayout_5 = QtGui.QGridLayout() + self.gridLayout_5.setObjectName(_fromUtf8("gridLayout_5")) + self.label_8 = QtGui.QLabel(self.tab_2) + self.label_8.setObjectName(_fromUtf8("label_8")) + self.gridLayout_5.addWidget(self.label_8, 0, 0, 1, 1) + self.lineEditSubject_2 = QtGui.QLineEdit(self.tab_2) + self.lineEditSubject_2.setText(_fromUtf8("")) + self.lineEditSubject_2.setObjectName(_fromUtf8("lineEditSubject_2")) + self.gridLayout_5.addWidget(self.lineEditSubject_2, 1, 1, 1, 1) + self.label_7 = QtGui.QLabel(self.tab_2) + self.label_7.setObjectName(_fromUtf8("label_7")) + self.gridLayout_5.addWidget(self.label_7, 1, 0, 1, 1) + self.comboBoxSendFrom_2 = QtGui.QComboBox(self.tab_2) + self.comboBoxSendFrom_2.setMinimumSize(QtCore.QSize(300, 0)) + self.comboBoxSendFrom_2.setObjectName(_fromUtf8("comboBoxSendFrom_2")) + self.gridLayout_5.addWidget(self.comboBoxSendFrom_2, 0, 1, 1, 1) + self.verticalLayout_5.addLayout(self.gridLayout_5) + self.textEditMessage_2 = QtGui.QTextEdit(self.tab_2) + self.textEditMessage_2.setObjectName(_fromUtf8("textEditMessage_2")) + self.verticalLayout_5.addWidget(self.textEditMessage_2) + self.pushButtonSend_3 = QtGui.QPushButton(self.tab_2) + self.pushButtonSend_3.setObjectName(_fromUtf8("pushButtonSend_3")) + self.verticalLayout_5.addWidget(self.pushButtonSend_3) + self.gridLayout_9.addLayout(self.verticalLayout_5, 0, 0, 1, 1) + self.tabWidget_2.addTab(self.tab_2, _fromUtf8("")) + self.horizontalLayout.addWidget(self.tabWidget_2) + self.gridLayout_11.addLayout(self.horizontalLayout, 0, 0, 1, 1) + icon4 = QtGui.QIcon() + icon4.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/send.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.tabWidget.addTab(self.send, icon4, _fromUtf8("")) + self.subscriptions = QtGui.QWidget() + self.subscriptions.setObjectName(_fromUtf8("subscriptions")) + self.gridLayout_4 = QtGui.QGridLayout(self.subscriptions) + self.gridLayout_4.setObjectName(_fromUtf8("gridLayout_4")) + self.horizontalLayout_2 = QtGui.QHBoxLayout() + self.horizontalLayout_2.setObjectName(_fromUtf8("horizontalLayout_2")) + self.verticalLayout_3 = QtGui.QVBoxLayout() + self.verticalLayout_3.setObjectName(_fromUtf8("verticalLayout_3")) + self.tableWidgetSubscriptions = QtGui.QTableWidget(self.subscriptions) + self.tableWidgetSubscriptions.setMaximumSize(QtCore.QSize(200, 16777215)) + self.tableWidgetSubscriptions.setAlternatingRowColors(True) + self.tableWidgetSubscriptions.setSelectionMode(QtGui.QAbstractItemView.SingleSelection) + self.tableWidgetSubscriptions.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) + self.tableWidgetSubscriptions.setObjectName(_fromUtf8("tableWidgetSubscriptions")) + self.tableWidgetSubscriptions.setColumnCount(2) + self.tableWidgetSubscriptions.setRowCount(0) item = QtGui.QTableWidgetItem() - self.tableWidgetSent.setHorizontalHeaderItem(0, item) + icon5 = QtGui.QIcon() + icon5.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/subscriptions.png")), QtGui.QIcon.Selected, QtGui.QIcon.Off) + item.setIcon(icon5) + self.tableWidgetSubscriptions.setHorizontalHeaderItem(0, item) item = QtGui.QTableWidgetItem() - self.tableWidgetSent.setHorizontalHeaderItem(1, item) + self.tableWidgetSubscriptions.setHorizontalHeaderItem(1, item) + self.tableWidgetSubscriptions.horizontalHeader().setCascadingSectionResizes(True) + self.tableWidgetSubscriptions.horizontalHeader().setDefaultSectionSize(200) + self.tableWidgetSubscriptions.horizontalHeader().setHighlightSections(False) + self.tableWidgetSubscriptions.horizontalHeader().setSortIndicatorShown(False) + self.tableWidgetSubscriptions.horizontalHeader().setStretchLastSection(True) + self.tableWidgetSubscriptions.verticalHeader().setVisible(False) + self.verticalLayout_3.addWidget(self.tableWidgetSubscriptions) + self.pushButtonAddSubscription = QtGui.QPushButton(self.subscriptions) + self.pushButtonAddSubscription.setMaximumSize(QtCore.QSize(200, 16777215)) + self.pushButtonAddSubscription.setObjectName(_fromUtf8("pushButtonAddSubscription")) + self.verticalLayout_3.addWidget(self.pushButtonAddSubscription) + self.horizontalLayout_2.addLayout(self.verticalLayout_3) + self.verticalLayout_4 = QtGui.QVBoxLayout() + self.verticalLayout_4.setObjectName(_fromUtf8("verticalLayout_4")) + self.inboxSearchLineSubscriptions = QtGui.QLineEdit(self.subscriptions) + self.inboxSearchLineSubscriptions.setObjectName(_fromUtf8("inboxSearchLineSubscriptions")) + self.verticalLayout_4.addWidget(self.inboxSearchLineSubscriptions) + self.tableWidgetInboxSubscriptions = QtGui.QTableWidget(self.subscriptions) + self.tableWidgetInboxSubscriptions.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers) + self.tableWidgetInboxSubscriptions.setAlternatingRowColors(True) + self.tableWidgetInboxSubscriptions.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection) + self.tableWidgetInboxSubscriptions.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) + self.tableWidgetInboxSubscriptions.setWordWrap(False) + self.tableWidgetInboxSubscriptions.setObjectName(_fromUtf8("tableWidgetInboxSubscriptions")) + self.tableWidgetInboxSubscriptions.setColumnCount(4) + self.tableWidgetInboxSubscriptions.setRowCount(0) item = QtGui.QTableWidgetItem() - self.tableWidgetSent.setHorizontalHeaderItem(2, item) + self.tableWidgetInboxSubscriptions.setHorizontalHeaderItem(0, item) item = QtGui.QTableWidgetItem() - self.tableWidgetSent.setHorizontalHeaderItem(3, item) - self.tableWidgetSent.horizontalHeader().setCascadingSectionResizes(True) - self.tableWidgetSent.horizontalHeader().setDefaultSectionSize(130) - self.tableWidgetSent.horizontalHeader().setHighlightSections(False) - self.tableWidgetSent.horizontalHeader().setSortIndicatorShown(False) - self.tableWidgetSent.horizontalHeader().setStretchLastSection(True) - self.tableWidgetSent.verticalHeader().setVisible(False) - self.tableWidgetSent.verticalHeader().setStretchLastSection(False) - self.textEditSentMessage = QtGui.QTextEdit(self.splitter_2) - self.textEditSentMessage.setReadOnly(True) - self.textEditSentMessage.setObjectName(_fromUtf8("textEditSentMessage")) - self.verticalLayout.addWidget(self.splitter_2) - icon3 = QtGui.QIcon() - icon3.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/sent.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) - self.tabWidget.addTab(self.sent, icon3, _fromUtf8("")) - self.youridentities = QtGui.QWidget() - self.youridentities.setObjectName(_fromUtf8("youridentities")) - self.gridLayout_3 = QtGui.QGridLayout(self.youridentities) + self.tableWidgetInboxSubscriptions.setHorizontalHeaderItem(1, item) + item = QtGui.QTableWidgetItem() + self.tableWidgetInboxSubscriptions.setHorizontalHeaderItem(2, item) + item = QtGui.QTableWidgetItem() + self.tableWidgetInboxSubscriptions.setHorizontalHeaderItem(3, item) + self.tableWidgetInboxSubscriptions.horizontalHeader().setCascadingSectionResizes(True) + self.tableWidgetInboxSubscriptions.horizontalHeader().setDefaultSectionSize(200) + self.tableWidgetInboxSubscriptions.horizontalHeader().setHighlightSections(False) + self.tableWidgetInboxSubscriptions.horizontalHeader().setMinimumSectionSize(27) + self.tableWidgetInboxSubscriptions.horizontalHeader().setSortIndicatorShown(False) + self.tableWidgetInboxSubscriptions.horizontalHeader().setStretchLastSection(True) + self.tableWidgetInboxSubscriptions.verticalHeader().setVisible(False) + self.tableWidgetInboxSubscriptions.verticalHeader().setDefaultSectionSize(26) + self.verticalLayout_4.addWidget(self.tableWidgetInboxSubscriptions) + self.textEditInboxSubscriptions = QtGui.QTextEdit(self.subscriptions) + self.textEditInboxSubscriptions.setBaseSize(QtCore.QSize(0, 500)) + self.textEditInboxSubscriptions.setReadOnly(True) + self.textEditInboxSubscriptions.setObjectName(_fromUtf8("textEditInboxSubscriptions")) + self.verticalLayout_4.addWidget(self.textEditInboxSubscriptions) + self.horizontalLayout_2.addLayout(self.verticalLayout_4) + self.gridLayout_4.addLayout(self.horizontalLayout_2, 0, 0, 1, 1) + icon6 = QtGui.QIcon() + icon6.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/subscriptions.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.tabWidget.addTab(self.subscriptions, icon6, _fromUtf8("")) + self.tab_3 = QtGui.QWidget() + self.tab_3.setObjectName(_fromUtf8("tab_3")) + self.gridLayout_3 = QtGui.QGridLayout(self.tab_3) self.gridLayout_3.setObjectName(_fromUtf8("gridLayout_3")) - self.pushButtonNewAddress = QtGui.QPushButton(self.youridentities) - self.pushButtonNewAddress.setObjectName(_fromUtf8("pushButtonNewAddress")) - self.gridLayout_3.addWidget(self.pushButtonNewAddress, 0, 0, 1, 1) - spacerItem2 = QtGui.QSpacerItem(689, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) - self.gridLayout_3.addItem(spacerItem2, 0, 1, 1, 1) - self.tableWidgetYourIdentities = QtGui.QTableWidget(self.youridentities) + self.horizontalLayout_4 = QtGui.QHBoxLayout() + self.horizontalLayout_4.setObjectName(_fromUtf8("horizontalLayout_4")) + self.verticalLayout_17 = QtGui.QVBoxLayout() + self.verticalLayout_17.setObjectName(_fromUtf8("verticalLayout_17")) + self.tableWidgetYourIdentities = QtGui.QTableWidget(self.tab_3) + self.tableWidgetYourIdentities.setMaximumSize(QtCore.QSize(200, 16777215)) self.tableWidgetYourIdentities.setFrameShadow(QtGui.QFrame.Sunken) self.tableWidgetYourIdentities.setLineWidth(1) self.tableWidgetYourIdentities.setAlternatingRowColors(True) @@ -254,13 +321,14 @@ class Ui_MainWindow(object): font = QtGui.QFont() font.setKerning(True) item.setFont(font) + item.setIcon(icon1) self.tableWidgetYourIdentities.setHorizontalHeaderItem(0, item) item = QtGui.QTableWidgetItem() self.tableWidgetYourIdentities.setHorizontalHeaderItem(1, item) item = QtGui.QTableWidgetItem() self.tableWidgetYourIdentities.setHorizontalHeaderItem(2, item) self.tableWidgetYourIdentities.horizontalHeader().setCascadingSectionResizes(True) - self.tableWidgetYourIdentities.horizontalHeader().setDefaultSectionSize(346) + self.tableWidgetYourIdentities.horizontalHeader().setDefaultSectionSize(200) self.tableWidgetYourIdentities.horizontalHeader().setMinimumSectionSize(52) self.tableWidgetYourIdentities.horizontalHeader().setSortIndicatorShown(True) self.tableWidgetYourIdentities.horizontalHeader().setStretchLastSection(True) @@ -268,77 +336,57 @@ class Ui_MainWindow(object): self.tableWidgetYourIdentities.verticalHeader().setDefaultSectionSize(26) self.tableWidgetYourIdentities.verticalHeader().setSortIndicatorShown(False) self.tableWidgetYourIdentities.verticalHeader().setStretchLastSection(False) - self.gridLayout_3.addWidget(self.tableWidgetYourIdentities, 1, 0, 1, 2) - icon4 = QtGui.QIcon() - icon4.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/identities.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) - self.tabWidget.addTab(self.youridentities, icon4, _fromUtf8("")) - self.subscriptions = QtGui.QWidget() - self.subscriptions.setObjectName(_fromUtf8("subscriptions")) - self.gridLayout_4 = QtGui.QGridLayout(self.subscriptions) - self.gridLayout_4.setObjectName(_fromUtf8("gridLayout_4")) - self.label_5 = QtGui.QLabel(self.subscriptions) - self.label_5.setWordWrap(True) - self.label_5.setObjectName(_fromUtf8("label_5")) - self.gridLayout_4.addWidget(self.label_5, 0, 0, 1, 2) - self.pushButtonAddSubscription = QtGui.QPushButton(self.subscriptions) - self.pushButtonAddSubscription.setObjectName(_fromUtf8("pushButtonAddSubscription")) - self.gridLayout_4.addWidget(self.pushButtonAddSubscription, 1, 0, 1, 1) - spacerItem3 = QtGui.QSpacerItem(689, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) - self.gridLayout_4.addItem(spacerItem3, 1, 1, 1, 1) - self.tableWidgetSubscriptions = QtGui.QTableWidget(self.subscriptions) - self.tableWidgetSubscriptions.setAlternatingRowColors(True) - self.tableWidgetSubscriptions.setSelectionMode(QtGui.QAbstractItemView.SingleSelection) - self.tableWidgetSubscriptions.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) - self.tableWidgetSubscriptions.setObjectName(_fromUtf8("tableWidgetSubscriptions")) - self.tableWidgetSubscriptions.setColumnCount(2) - self.tableWidgetSubscriptions.setRowCount(0) + self.verticalLayout_17.addWidget(self.tableWidgetYourIdentities) + self.pushButtonAddChanel = QtGui.QPushButton(self.tab_3) + self.pushButtonAddChanel.setMaximumSize(QtCore.QSize(200, 16777215)) + self.pushButtonAddChanel.setObjectName(_fromUtf8("pushButtonAddChanel")) + self.verticalLayout_17.addWidget(self.pushButtonAddChanel) + self.horizontalLayout_4.addLayout(self.verticalLayout_17) + self.verticalLayout_13 = QtGui.QVBoxLayout() + self.verticalLayout_13.setObjectName(_fromUtf8("verticalLayout_13")) + self.horizontalLayoutSearch_2 = QtGui.QHBoxLayout() + self.horizontalLayoutSearch_2.setContentsMargins(-1, 0, -1, -1) + self.horizontalLayoutSearch_2.setObjectName(_fromUtf8("horizontalLayoutSearch_2")) + self.inboxSearchLineEdit_2 = QtGui.QLineEdit(self.tab_3) + self.inboxSearchLineEdit_2.setObjectName(_fromUtf8("inboxSearchLineEdit_2")) + self.horizontalLayoutSearch_2.addWidget(self.inboxSearchLineEdit_2) + self.verticalLayout_13.addLayout(self.horizontalLayoutSearch_2) + self.tableWidgetInbox_2 = QtGui.QTableWidget(self.tab_3) + self.tableWidgetInbox_2.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers) + self.tableWidgetInbox_2.setAlternatingRowColors(True) + self.tableWidgetInbox_2.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection) + self.tableWidgetInbox_2.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) + self.tableWidgetInbox_2.setWordWrap(False) + self.tableWidgetInbox_2.setObjectName(_fromUtf8("tableWidgetInbox_2")) + self.tableWidgetInbox_2.setColumnCount(4) + self.tableWidgetInbox_2.setRowCount(0) item = QtGui.QTableWidgetItem() - self.tableWidgetSubscriptions.setHorizontalHeaderItem(0, item) + self.tableWidgetInbox_2.setHorizontalHeaderItem(0, item) item = QtGui.QTableWidgetItem() - self.tableWidgetSubscriptions.setHorizontalHeaderItem(1, item) - self.tableWidgetSubscriptions.horizontalHeader().setCascadingSectionResizes(True) - self.tableWidgetSubscriptions.horizontalHeader().setDefaultSectionSize(400) - self.tableWidgetSubscriptions.horizontalHeader().setHighlightSections(False) - self.tableWidgetSubscriptions.horizontalHeader().setSortIndicatorShown(False) - self.tableWidgetSubscriptions.horizontalHeader().setStretchLastSection(True) - self.tableWidgetSubscriptions.verticalHeader().setVisible(False) - self.gridLayout_4.addWidget(self.tableWidgetSubscriptions, 2, 0, 1, 2) - icon5 = QtGui.QIcon() - icon5.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/subscriptions.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) - self.tabWidget.addTab(self.subscriptions, icon5, _fromUtf8("")) - self.addressbook = QtGui.QWidget() - self.addressbook.setObjectName(_fromUtf8("addressbook")) - self.gridLayout_5 = QtGui.QGridLayout(self.addressbook) - self.gridLayout_5.setObjectName(_fromUtf8("gridLayout_5")) - self.label_6 = QtGui.QLabel(self.addressbook) - self.label_6.setWordWrap(True) - self.label_6.setObjectName(_fromUtf8("label_6")) - self.gridLayout_5.addWidget(self.label_6, 0, 0, 1, 2) - self.pushButtonAddAddressBook = QtGui.QPushButton(self.addressbook) - self.pushButtonAddAddressBook.setObjectName(_fromUtf8("pushButtonAddAddressBook")) - self.gridLayout_5.addWidget(self.pushButtonAddAddressBook, 1, 0, 1, 1) - spacerItem4 = QtGui.QSpacerItem(689, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) - self.gridLayout_5.addItem(spacerItem4, 1, 1, 1, 1) - self.tableWidgetAddressBook = QtGui.QTableWidget(self.addressbook) - self.tableWidgetAddressBook.setAlternatingRowColors(True) - self.tableWidgetAddressBook.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection) - self.tableWidgetAddressBook.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) - self.tableWidgetAddressBook.setObjectName(_fromUtf8("tableWidgetAddressBook")) - self.tableWidgetAddressBook.setColumnCount(2) - self.tableWidgetAddressBook.setRowCount(0) + self.tableWidgetInbox_2.setHorizontalHeaderItem(1, item) item = QtGui.QTableWidgetItem() - self.tableWidgetAddressBook.setHorizontalHeaderItem(0, item) + self.tableWidgetInbox_2.setHorizontalHeaderItem(2, item) item = QtGui.QTableWidgetItem() - self.tableWidgetAddressBook.setHorizontalHeaderItem(1, item) - self.tableWidgetAddressBook.horizontalHeader().setCascadingSectionResizes(True) - self.tableWidgetAddressBook.horizontalHeader().setDefaultSectionSize(400) - self.tableWidgetAddressBook.horizontalHeader().setHighlightSections(False) - self.tableWidgetAddressBook.horizontalHeader().setStretchLastSection(True) - self.tableWidgetAddressBook.verticalHeader().setVisible(False) - self.gridLayout_5.addWidget(self.tableWidgetAddressBook, 2, 0, 1, 2) - icon6 = QtGui.QIcon() - icon6.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/addressbook.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) - self.tabWidget.addTab(self.addressbook, icon6, _fromUtf8("")) + self.tableWidgetInbox_2.setHorizontalHeaderItem(3, item) + self.tableWidgetInbox_2.horizontalHeader().setCascadingSectionResizes(True) + self.tableWidgetInbox_2.horizontalHeader().setDefaultSectionSize(200) + self.tableWidgetInbox_2.horizontalHeader().setHighlightSections(False) + self.tableWidgetInbox_2.horizontalHeader().setMinimumSectionSize(27) + self.tableWidgetInbox_2.horizontalHeader().setSortIndicatorShown(False) + self.tableWidgetInbox_2.horizontalHeader().setStretchLastSection(True) + self.tableWidgetInbox_2.verticalHeader().setVisible(False) + self.tableWidgetInbox_2.verticalHeader().setDefaultSectionSize(26) + self.verticalLayout_13.addWidget(self.tableWidgetInbox_2) + self.textEditInboxMessage_2 = QtGui.QTextEdit(self.tab_3) + self.textEditInboxMessage_2.setBaseSize(QtCore.QSize(0, 500)) + self.textEditInboxMessage_2.setReadOnly(True) + self.textEditInboxMessage_2.setObjectName(_fromUtf8("textEditInboxMessage_2")) + self.verticalLayout_13.addWidget(self.textEditInboxMessage_2) + self.horizontalLayout_4.addLayout(self.verticalLayout_13) + self.gridLayout_3.addLayout(self.horizontalLayout_4, 0, 0, 1, 1) + icon7 = QtGui.QIcon() + icon7.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/can-icon-16px.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.tabWidget.addTab(self.tab_3, icon7, _fromUtf8("")) self.blackwhitelist = QtGui.QWidget() self.blackwhitelist.setObjectName(_fromUtf8("blackwhitelist")) self.gridLayout_6 = QtGui.QGridLayout(self.blackwhitelist) @@ -353,8 +401,8 @@ class Ui_MainWindow(object): self.pushButtonAddBlacklist = QtGui.QPushButton(self.blackwhitelist) self.pushButtonAddBlacklist.setObjectName(_fromUtf8("pushButtonAddBlacklist")) self.gridLayout_6.addWidget(self.pushButtonAddBlacklist, 2, 0, 1, 1) - spacerItem5 = QtGui.QSpacerItem(689, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) - self.gridLayout_6.addItem(spacerItem5, 2, 1, 1, 1) + spacerItem = QtGui.QSpacerItem(689, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) + self.gridLayout_6.addItem(spacerItem, 2, 1, 1, 1) self.tableWidgetBlacklist = QtGui.QTableWidget(self.blackwhitelist) self.tableWidgetBlacklist.setAlternatingRowColors(True) self.tableWidgetBlacklist.setSelectionMode(QtGui.QAbstractItemView.SingleSelection) @@ -373,17 +421,17 @@ class Ui_MainWindow(object): self.tableWidgetBlacklist.horizontalHeader().setStretchLastSection(True) self.tableWidgetBlacklist.verticalHeader().setVisible(False) self.gridLayout_6.addWidget(self.tableWidgetBlacklist, 3, 0, 1, 2) - icon7 = QtGui.QIcon() - icon7.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/blacklist.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) - self.tabWidget.addTab(self.blackwhitelist, icon7, _fromUtf8("")) + icon8 = QtGui.QIcon() + icon8.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/blacklist.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.tabWidget.addTab(self.blackwhitelist, icon8, _fromUtf8("")) self.networkstatus = QtGui.QWidget() self.networkstatus.setObjectName(_fromUtf8("networkstatus")) self.pushButtonStatusIcon = QtGui.QPushButton(self.networkstatus) self.pushButtonStatusIcon.setGeometry(QtCore.QRect(680, 440, 21, 23)) self.pushButtonStatusIcon.setText(_fromUtf8("")) - icon8 = QtGui.QIcon() - icon8.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/redicon.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) - self.pushButtonStatusIcon.setIcon(icon8) + icon9 = QtGui.QIcon() + icon9.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/redicon.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.pushButtonStatusIcon.setIcon(icon9) self.pushButtonStatusIcon.setFlat(True) self.pushButtonStatusIcon.setObjectName(_fromUtf8("pushButtonStatusIcon")) self.tableWidgetConnectionCount = QtGui.QTableWidget(self.networkstatus) @@ -439,13 +487,13 @@ class Ui_MainWindow(object): self.labelBytesSentCount = QtGui.QLabel(self.networkstatus) self.labelBytesSentCount.setGeometry(QtCore.QRect(350, 230, 251, 16)) self.labelBytesSentCount.setObjectName(_fromUtf8("labelBytesSentCount")) - icon9 = QtGui.QIcon() - icon9.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/networkstatus.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) - self.tabWidget.addTab(self.networkstatus, icon9, _fromUtf8("")) - self.gridLayout.addWidget(self.tabWidget, 0, 0, 1, 1) + icon10 = QtGui.QIcon() + icon10.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/networkstatus.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.tabWidget.addTab(self.networkstatus, icon10, _fromUtf8("")) + self.gridLayout_10.addWidget(self.tabWidget, 0, 0, 1, 1) MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtGui.QMenuBar(MainWindow) - self.menubar.setGeometry(QtCore.QRect(0, 0, 885, 21)) + self.menubar.setGeometry(QtCore.QRect(0, 0, 885, 27)) self.menubar.setObjectName(_fromUtf8("menubar")) self.menuFile = QtGui.QMenu(self.menubar) self.menuFile.setObjectName(_fromUtf8("menuFile")) @@ -497,7 +545,6 @@ class Ui_MainWindow(object): self.menuFile.addAction(self.actionManageKeys) self.menuFile.addAction(self.actionDeleteAllTrashedMessages) self.menuFile.addAction(self.actionRegenerateDeterministicAddresses) - self.menuFile.addAction(self.actionJoinChan) self.menuFile.addAction(self.actionExit) self.menuSettings.addAction(self.actionSettings) self.menuHelp.addAction(self.actionHelp) @@ -508,29 +555,16 @@ class Ui_MainWindow(object): self.retranslateUi(MainWindow) self.tabWidget.setCurrentIndex(0) - QtCore.QObject.connect(self.radioButtonSpecific, QtCore.SIGNAL(_fromUtf8("toggled(bool)")), self.lineEditTo.setEnabled) - QtCore.QObject.connect(self.radioButtonSpecific, QtCore.SIGNAL(_fromUtf8("clicked(bool)")), self.labelSendBroadcastWarning.hide) - QtCore.QObject.connect(self.radioButtonBroadcast, QtCore.SIGNAL(_fromUtf8("clicked()")), self.labelSendBroadcastWarning.show) + self.tabWidget_2.setCurrentIndex(0) QtCore.QMetaObject.connectSlotsByName(MainWindow) - MainWindow.setTabOrder(self.tabWidget, self.tableWidgetInbox) MainWindow.setTabOrder(self.tableWidgetInbox, self.textEditInboxMessage) - MainWindow.setTabOrder(self.textEditInboxMessage, self.radioButtonSpecific) - MainWindow.setTabOrder(self.radioButtonSpecific, self.radioButtonBroadcast) - MainWindow.setTabOrder(self.radioButtonBroadcast, self.comboBoxSendFrom) + MainWindow.setTabOrder(self.textEditInboxMessage, self.comboBoxSendFrom) MainWindow.setTabOrder(self.comboBoxSendFrom, self.lineEditTo) - MainWindow.setTabOrder(self.lineEditTo, self.pushButtonLoadFromAddressBook) - MainWindow.setTabOrder(self.pushButtonLoadFromAddressBook, self.lineEditSubject) + MainWindow.setTabOrder(self.lineEditTo, self.lineEditSubject) MainWindow.setTabOrder(self.lineEditSubject, self.textEditMessage) MainWindow.setTabOrder(self.textEditMessage, self.pushButtonSend) - MainWindow.setTabOrder(self.pushButtonSend, self.tableWidgetSent) - MainWindow.setTabOrder(self.tableWidgetSent, self.textEditSentMessage) - MainWindow.setTabOrder(self.textEditSentMessage, self.pushButtonNewAddress) - MainWindow.setTabOrder(self.pushButtonNewAddress, self.tableWidgetYourIdentities) - MainWindow.setTabOrder(self.tableWidgetYourIdentities, self.pushButtonAddSubscription) - MainWindow.setTabOrder(self.pushButtonAddSubscription, self.tableWidgetSubscriptions) - MainWindow.setTabOrder(self.tableWidgetSubscriptions, self.pushButtonAddAddressBook) - MainWindow.setTabOrder(self.pushButtonAddAddressBook, self.tableWidgetAddressBook) - MainWindow.setTabOrder(self.tableWidgetAddressBook, self.radioButtonBlacklist) + MainWindow.setTabOrder(self.pushButtonSend, self.pushButtonAddSubscription) + MainWindow.setTabOrder(self.pushButtonAddSubscription, self.radioButtonBlacklist) MainWindow.setTabOrder(self.radioButtonBlacklist, self.radioButtonWhitelist) MainWindow.setTabOrder(self.radioButtonWhitelist, self.pushButtonAddBlacklist) MainWindow.setTabOrder(self.pushButtonAddBlacklist, self.tableWidgetBlacklist) @@ -538,13 +572,10 @@ class Ui_MainWindow(object): MainWindow.setTabOrder(self.tableWidgetConnectionCount, self.pushButtonStatusIcon) def retranslateUi(self, MainWindow): - MainWindow.setWindowTitle(_translate("MainWindow", "Bitmessage", None)) + MainWindow.setWindowTitle(_translate("MainWindow", "B-Mail", None)) + self.treeWidgetYourIdentities.headerItem().setText(0, _translate("MainWindow", "Identities", None)) + self.pushButtonNewAddress.setText(_translate("MainWindow", "New Indentitiy", None)) self.inboxSearchLineEdit.setPlaceholderText(_translate("MainWindow", "Search", None)) - self.inboxSearchOptionCB.setItemText(0, _translate("MainWindow", "All", None)) - self.inboxSearchOptionCB.setItemText(1, _translate("MainWindow", "To", None)) - self.inboxSearchOptionCB.setItemText(2, _translate("MainWindow", "From", None)) - self.inboxSearchOptionCB.setItemText(3, _translate("MainWindow", "Subject", None)) - self.inboxSearchOptionCB.setItemText(4, _translate("MainWindow", "Message", None)) self.tableWidgetInbox.setSortingEnabled(True) item = self.tableWidgetInbox.horizontalHeaderItem(0) item.setText(_translate("MainWindow", "To", None)) @@ -554,64 +585,69 @@ class Ui_MainWindow(object): item.setText(_translate("MainWindow", "Subject", None)) item = self.tableWidgetInbox.horizontalHeaderItem(3) item.setText(_translate("MainWindow", "Received", None)) - self.tabWidget.setTabText(self.tabWidget.indexOf(self.inbox), _translate("MainWindow", "Inbox", None)) - self.pushButtonLoadFromAddressBook.setText(_translate("MainWindow", "Load from Address book", None)) - self.pushButtonFetchNamecoinID.setText(_translate("MainWindow", "Fetch Namecoin ID", None)) - self.label_4.setText(_translate("MainWindow", "Message:", None)) + self.tabWidget.setTabText(self.tabWidget.indexOf(self.inbox), _translate("MainWindow", "Received", None)) + self.tableWidgetAddressBook.setSortingEnabled(True) + item = self.tableWidgetAddressBook.horizontalHeaderItem(0) + item.setText(_translate("MainWindow", "Name", None)) + item = self.tableWidgetAddressBook.horizontalHeaderItem(1) + item.setText(_translate("MainWindow", "Address", None)) + self.pushButtonAddAddressBook.setText(_translate("MainWindow", "Add Contact", None)) + self.label.setText(_translate("MainWindow", "To:", None)) self.label_3.setText(_translate("MainWindow", "Subject:", None)) - self.radioButtonSpecific.setText(_translate("MainWindow", "Send to one or more specific people", None)) + self.label_2.setText(_translate("MainWindow", "From:", None)) self.textEditMessage.setHtml(_translate("MainWindow", "\n" "\n" -"


", None)) - self.label.setText(_translate("MainWindow", "To:", None)) - self.label_2.setText(_translate("MainWindow", "From:", None)) - self.radioButtonBroadcast.setText(_translate("MainWindow", "Broadcast to everyone who is subscribed to your address", None)) +"\n" +"


", None)) self.pushButtonSend.setText(_translate("MainWindow", "Send", None)) - self.labelSendBroadcastWarning.setText(_translate("MainWindow", "Be aware that broadcasts are only encrypted with your address. Anyone who knows your address can read them.", None)) + self.tabWidget_2.setTabText(self.tabWidget_2.indexOf(self.tab), _translate("MainWindow", "Send ordinary Message", None)) + self.label_8.setText(_translate("MainWindow", "From:", None)) + self.label_7.setText(_translate("MainWindow", "Subject:", None)) + self.textEditMessage_2.setHtml(_translate("MainWindow", "\n" +"\n" +"


", None)) + self.pushButtonSend_3.setText(_translate("MainWindow", "Send", None)) + self.tabWidget_2.setTabText(self.tabWidget_2.indexOf(self.tab_2), _translate("MainWindow", "Send Message to your Subscribers", None)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.send), _translate("MainWindow", "Send", None)) - self.sentSearchLineEdit.setPlaceholderText(_translate("MainWindow", "Search", None)) - self.sentSearchOptionCB.setItemText(0, _translate("MainWindow", "All", None)) - self.sentSearchOptionCB.setItemText(1, _translate("MainWindow", "To", None)) - self.sentSearchOptionCB.setItemText(2, _translate("MainWindow", "From", None)) - self.sentSearchOptionCB.setItemText(3, _translate("MainWindow", "Subject", None)) - self.sentSearchOptionCB.setItemText(4, _translate("MainWindow", "Message", None)) - self.tableWidgetSent.setSortingEnabled(True) - item = self.tableWidgetSent.horizontalHeaderItem(0) + self.tableWidgetSubscriptions.setSortingEnabled(True) + item = self.tableWidgetSubscriptions.horizontalHeaderItem(0) + item.setText(_translate("MainWindow", "Name", None)) + item = self.tableWidgetSubscriptions.horizontalHeaderItem(1) + item.setText(_translate("MainWindow", "Address", None)) + self.pushButtonAddSubscription.setText(_translate("MainWindow", "Add new Subscription", None)) + self.inboxSearchLineSubscriptions.setPlaceholderText(_translate("MainWindow", "Search", None)) + self.tableWidgetInboxSubscriptions.setSortingEnabled(True) + item = self.tableWidgetInboxSubscriptions.horizontalHeaderItem(0) item.setText(_translate("MainWindow", "To", None)) - item = self.tableWidgetSent.horizontalHeaderItem(1) + item = self.tableWidgetInboxSubscriptions.horizontalHeaderItem(1) item.setText(_translate("MainWindow", "From", None)) - item = self.tableWidgetSent.horizontalHeaderItem(2) + item = self.tableWidgetInboxSubscriptions.horizontalHeaderItem(2) item.setText(_translate("MainWindow", "Subject", None)) - item = self.tableWidgetSent.horizontalHeaderItem(3) - item.setText(_translate("MainWindow", "Status", None)) - self.tabWidget.setTabText(self.tabWidget.indexOf(self.sent), _translate("MainWindow", "Sent", None)) - self.pushButtonNewAddress.setText(_translate("MainWindow", "New", None)) + item = self.tableWidgetInboxSubscriptions.horizontalHeaderItem(3) + item.setText(_translate("MainWindow", "Received", None)) + self.tabWidget.setTabText(self.tabWidget.indexOf(self.subscriptions), _translate("MainWindow", "Subscriptions", None)) self.tableWidgetYourIdentities.setSortingEnabled(True) item = self.tableWidgetYourIdentities.horizontalHeaderItem(0) - item.setText(_translate("MainWindow", "Label (not shown to anyone)", None)) + item.setText(_translate("MainWindow", "Name", None)) item = self.tableWidgetYourIdentities.horizontalHeaderItem(1) item.setText(_translate("MainWindow", "Address", None)) item = self.tableWidgetYourIdentities.horizontalHeaderItem(2) item.setText(_translate("MainWindow", "Stream", None)) - self.tabWidget.setTabText(self.tabWidget.indexOf(self.youridentities), _translate("MainWindow", "Your Identities", None)) - self.label_5.setText(_translate("MainWindow", "Here you can subscribe to \'broadcast messages\' that are sent by other users. Messages will appear in your Inbox. Addresses here override those on the Blacklist tab.", None)) - self.pushButtonAddSubscription.setText(_translate("MainWindow", "Add new Subscription", None)) - self.tableWidgetSubscriptions.setSortingEnabled(True) - item = self.tableWidgetSubscriptions.horizontalHeaderItem(0) - item.setText(_translate("MainWindow", "Label", None)) - item = self.tableWidgetSubscriptions.horizontalHeaderItem(1) - item.setText(_translate("MainWindow", "Address", None)) - self.tabWidget.setTabText(self.tabWidget.indexOf(self.subscriptions), _translate("MainWindow", "Subscriptions", None)) - self.label_6.setText(_translate("MainWindow", "The Address book is useful for adding names or labels to other people\'s Bitmessage addresses so that you can recognize them more easily in your inbox. You can add entries here using the \'Add\' button, or from your inbox by right-clicking on a message.", None)) - self.pushButtonAddAddressBook.setText(_translate("MainWindow", "Add new entry", None)) - self.tableWidgetAddressBook.setSortingEnabled(True) - item = self.tableWidgetAddressBook.horizontalHeaderItem(0) - item.setText(_translate("MainWindow", "Name or Label", None)) - item = self.tableWidgetAddressBook.horizontalHeaderItem(1) - item.setText(_translate("MainWindow", "Address", None)) - self.tabWidget.setTabText(self.tabWidget.indexOf(self.addressbook), _translate("MainWindow", "Address Book", None)) + self.pushButtonAddChanel.setText(_translate("MainWindow", "Add Chanel", None)) + self.inboxSearchLineEdit_2.setPlaceholderText(_translate("MainWindow", "Search", None)) + self.tableWidgetInbox_2.setSortingEnabled(True) + item = self.tableWidgetInbox_2.horizontalHeaderItem(0) + item.setText(_translate("MainWindow", "To", None)) + item = self.tableWidgetInbox_2.horizontalHeaderItem(1) + item.setText(_translate("MainWindow", "From", None)) + item = self.tableWidgetInbox_2.horizontalHeaderItem(2) + item.setText(_translate("MainWindow", "Subject", None)) + item = self.tableWidgetInbox_2.horizontalHeaderItem(3) + item.setText(_translate("MainWindow", "Received", None)) + self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_3), _translate("MainWindow", "Chanels", None)) self.radioButtonBlacklist.setText(_translate("MainWindow", "Use a Blacklist (Allow all incoming messages except those on the Blacklist)", None)) self.radioButtonWhitelist.setText(_translate("MainWindow", "Use a Whitelist (Block all incoming messages except those on the Whitelist)", None)) self.pushButtonAddBlacklist.setText(_translate("MainWindow", "Add new entry", None)) @@ -650,3 +686,13 @@ class Ui_MainWindow(object): self.actionJoinChan.setText(_translate("MainWindow", "Join / Create chan", None)) import bitmessage_icons_rc + +if __name__ == "__main__": + import sys + app = QtGui.QApplication(sys.argv) + MainWindow = QtGui.QMainWindow() + ui = Ui_MainWindow() + ui.setupUi(MainWindow) + MainWindow.show() + sys.exit(app.exec_()) + diff --git a/src/bitmessageqt/bitmessageui.ui b/src/bitmessageqt/bitmessageui.ui index 58e115d7..9340f531 100644 --- a/src/bitmessageqt/bitmessageui.ui +++ b/src/bitmessageqt/bitmessageui.ui @@ -21,10 +21,7 @@ QTabWidget::Rounded - - - 0 - + @@ -65,134 +62,147 @@ :/newPrefix/images/inbox.png:/newPrefix/images/inbox.png - Inbox + Received - - - - - 0 - + + + - - - Search - - + + + + + + 200 + 16777215 + + + + + Identities + + + + :/newPrefix/images/identities.png + + + + + + + + + + 200 + 16777215 + + + + New Indentitiy + + + + - + - - All - + + + 0 + + + + + Search + + + + - - To - + + + QAbstractItemView::NoEditTriggers + + + true + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::SelectRows + + + true + + + false + + + true + + + 200 + + + false + + + 27 + + + false + + + true + + + false + + + 26 + + + + To + + + + + From + + + + + Subject + + + + + Received + + + - - From - + + + + 0 + 500 + + + + true + + - - - Subject - - - - - Message - - - + - - - - Qt::Vertical - - - - QAbstractItemView::NoEditTriggers - - - true - - - QAbstractItemView::ExtendedSelection - - - QAbstractItemView::SelectRows - - - true - - - false - - - true - - - 200 - - - false - - - 27 - - - false - - - true - - - false - - - 26 - - - - To - - - - - From - - - - - Subject - - - - - Received - - - - - - - 0 - 500 - - - - true - - - - @@ -203,396 +213,225 @@ Send - - - - - - 7 - - - - Load from Address book - - - - - - - - 7 - - - - Fetch Namecoin ID - - - - - - - Message: - - - - - - - - 300 - 0 - - - - - - - - Subject: - - - - - - - - - - - - - - Send to one or more specific people - - - true - - - - - - - - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:9pt; font-weight:400; font-style:normal;"> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> - - - - - - - To: - - - - - - - From: - - - - - - - Qt::Vertical - - - - 20 - 297 - - - - - - - - Broadcast to everyone who is subscribed to your address - - - - - - - - - - - - - - Qt::Horizontal - - - - 20 - 20 - - - - - - - - Send - - - - - - - true - - - - 0 - 0 - - - - Be aware that broadcasts are only encrypted with your address. Anyone who knows your address can read them. - - - -1 - - - - - - - - - :/newPrefix/images/sent.png:/newPrefix/images/sent.png - - - Sent - - - + + - - 0 - - - - Search - - + + + + + + 200 + 16777215 + + + + true + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::SelectRows + + + true + + + true + + + 200 + + + false + + + true + + + false + + + + Name + + + + :/newPrefix/images/addressbook.png + + + + + + Address + + + + + + + + + 200 + 16777215 + + + + Add Contact + + + + - - - - All - - - - - To - - - - - From - - - - - Subject - - - - - Message - - + + + 0 + + + + Send ordinary Message + + + + + + + + + + + 300 + 0 + + + + + + + + To: + + + + + + + + + + Subject: + + + + + + + From: + + + + + + + + + + + + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Droid Sans'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2';"><br /></p></body></html> + + + + + + + Send + + + + + + + + + + Send Message to your Subscribers + + + + + + + + + + From: + + + + + + + + + + + + + + Subject: + + + + + + + + 300 + 0 + + + + + + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Droid Sans'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2';"><br /></p></body></html> + + + + + + + Send + + + + + + + - - - - Qt::Vertical - - - - QAbstractItemView::NoEditTriggers - - - QAbstractItemView::DragDrop - - - true - - - QAbstractItemView::ExtendedSelection - - - QAbstractItemView::SelectRows - - - true - - - false - - - true - - - 130 - - - false - - - false - - - true - - - false - - - false - - - - To - - - - - From - - - - - Subject - - - - - Status - - - - - - true - - - - - - - - - - :/newPrefix/images/identities.png:/newPrefix/images/identities.png - - - Your Identities - - - - - - New - - - - - - - Qt::Horizontal - - - - 689 - 20 - - - - - - - - QFrame::Sunken - - - 1 - - - true - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectRows - - - true - - - true - - - 346 - - - 52 - - - true - - - true - - - false - - - 26 - - - false - - - false - - - - Label (not shown to anyone) - - - - true - - - - - - Address - - - - - Stream - - - - @@ -604,161 +443,380 @@ p, li { white-space: pre-wrap; } Subscriptions - - - - Here you can subscribe to 'broadcast messages' that are sent by other users. Messages will appear in your Inbox. Addresses here override those on the Blacklist tab. - - - true - - - - - - - Add new Subscription - - - - - - - Qt::Horizontal - - - - 689 - 20 - - - - - - - - true - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectRows - - - true - - - true - - - 400 - - - false - - - false - - - true - - - false - - - - Label - - - - - Address - - - + + + + + + + + + 200 + 16777215 + + + + true + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + true + + + true + + + 200 + + + false + + + false + + + true + + + false + + + + Name + + + + :/newPrefix/images/subscriptions.png + + + + + + Address + + + + + + + + + 200 + 16777215 + + + + Add new Subscription + + + + + + + + + + + Search + + + + + + + QAbstractItemView::NoEditTriggers + + + true + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::SelectRows + + + true + + + false + + + true + + + 200 + + + false + + + 27 + + + false + + + true + + + false + + + 26 + + + + To + + + + + From + + + + + Subject + + + + + Received + + + + + + + + + 0 + 500 + + + + true + + + + + + - + - :/newPrefix/images/addressbook.png:/newPrefix/images/addressbook.png + :/newPrefix/images/can-icon-16px.png:/newPrefix/images/can-icon-16px.png - Address Book + Chanels - - - - - The Address book is useful for adding names or labels to other people's Bitmessage addresses so that you can recognize them more easily in your inbox. You can add entries here using the 'Add' button, or from your inbox by right-clicking on a message. - - - true - - - - - - - Add new entry - - - - - - - Qt::Horizontal - - - - 689 - 20 - - - - - - - - true - - - QAbstractItemView::ExtendedSelection - - - QAbstractItemView::SelectRows - - - true - - - true - - - 400 - - - false - - - true - - - false - - - - Name or Label - - - - - Address - - - + + + + + + + + + + 200 + 16777215 + + + + QFrame::Sunken + + + 1 + + + true + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + true + + + true + + + 200 + + + 52 + + + true + + + true + + + false + + + 26 + + + false + + + false + + + + Name + + + + true + + + + + :/newPrefix/images/identities.png + + + + + + Address + + + + + Stream + + + + + + + + + 200 + 16777215 + + + + Add Chanel + + + + + + + + + + + 0 + + + + + Search + + + + + + + + + QAbstractItemView::NoEditTriggers + + + true + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::SelectRows + + + true + + + false + + + true + + + 200 + + + false + + + 27 + + + false + + + true + + + false + + + 26 + + + + To + + + + + From + + + + + Subject + + + + + Received + + + + + + + + + 0 + 500 + + + + true + + + + + + @@ -1081,7 +1139,7 @@ p, li { white-space: pre-wrap; } 0 0 885 - 21 + 27 @@ -1091,7 +1149,6 @@ p, li { white-space: pre-wrap; } - @@ -1218,25 +1275,14 @@ p, li { white-space: pre-wrap; } - tabWidget tableWidgetInbox textEditInboxMessage - radioButtonSpecific - radioButtonBroadcast comboBoxSendFrom lineEditTo - pushButtonLoadFromAddressBook lineEditSubject textEditMessage pushButtonSend - tableWidgetSent - textEditSentMessage - pushButtonNewAddress - tableWidgetYourIdentities pushButtonAddSubscription - tableWidgetSubscriptions - pushButtonAddAddressBook - tableWidgetAddressBook radioButtonBlacklist radioButtonWhitelist pushButtonAddBlacklist @@ -1247,54 +1293,5 @@ p, li { white-space: pre-wrap; } - - - radioButtonSpecific - toggled(bool) - lineEditTo - setEnabled(bool) - - - 121 - 60 - - - 175 - 147 - - - - - radioButtonSpecific - clicked(bool) - labelSendBroadcastWarning - hide() - - - 95 - 59 - - - 129 - 528 - - - - - radioButtonBroadcast - clicked() - labelSendBroadcastWarning - show() - - - 108 - 84 - - - 177 - 519 - - - - + From b86cf4b5cf81ec107ccbe84f4b48187bd5288df8 Mon Sep 17 00:00:00 2001 From: sbkaf Date: Thu, 19 Mar 2015 18:25:50 +0100 Subject: [PATCH 004/210] interface --- src/bitmessagemain.py | 2 +- src/bitmessageqt/__init__.py | 122 +++++++++++++++++++++++++++++-- src/bitmessageqt/bitmessageui.py | 6 +- 3 files changed, 121 insertions(+), 9 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 732c16c5..4454b031 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -202,7 +202,7 @@ class Main: singleAPIThread.daemon = True # close the main program even if there are threads left singleAPIThread.start() - connectToStream(1) + #connectToStream(1) singleListenerThread = singleListener() singleListenerThread.setup(selfInitiatedConnections) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 4596525e..d90234a7 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -555,10 +555,8 @@ class MyForm(QtGui.QMainWindow): newSubItem.setText(0, _translate("MainWindow", "trash")) # Load inbox from messages database file - self.loadInbox() - - # Load Sent items from database - self.loadSent() + #self.loadInbox() + self.loadMessagelist("BM-2cTxBr9RxjorkjvkgTkD1AgjhUJBwDnVKY"); # Initialize the address book self.rerenderAddressBook() @@ -889,6 +887,120 @@ class MyForm(QtGui.QMainWindow): self.ui.tableWidgetInbox.sortItems(3, Qt.DescendingOrder) self.ui.tableWidgetInbox.keyPressEvent = self.tableWidgetInboxKeyPressEvent + # Load inbox from messages database file + def loadMessagelist(self, account, folder="inbox", where="", what=""): + what = "%" + what + "%" + if where == "To": + where = "toaddress" + elif where == "From": + where = "fromaddress" + elif where == "Subject": + where = "subject" + elif where == "Message": + where = "message" + else: + where = "toaddress || fromaddress || subject || message" + + if folder == "sent": + accountaddress = "fromaddress" + else: + accountaddress = "toaddress" + + sqlStatement = ''' + SELECT msgid, toaddress, fromaddress, subject, received, read + FROM inbox WHERE %s=? AND folder=? AND %s LIKE ? + ORDER BY received + ''' % (accounttype, where) + + while self.ui.tableWidgetInbox.rowCount() > 0: + self.ui.tableWidgetInbox.removeRow(0) + + font = QFont() + font.setBold(True) + queryreturn = sqlQuery(sqlStatement, account, folder, what) + for row in queryreturn: + msgid, toAddress, fromAddress, subject, received, read = row + subject = shared.fixPotentiallyInvalidUTF8Data(subject) + try: + if toAddress == self.str_broadcast_subscribers: + toLabel = self.str_broadcast_subscribers + else: + toLabel = shared.config.get(toAddress, 'label') + except: + toLabel = '' + if toLabel == '': + toLabel = toAddress + + fromLabel = '' + if shared.config.has_section(fromAddress): + fromLabel = shared.config.get(fromAddress, 'label') + + if fromLabel == '': # If the fromAddress isn't one of our addresses and isn't a chan + queryreturn = sqlQuery( + '''select label from addressbook where address=?''', fromAddress) + if queryreturn != []: + for row in queryreturn: + fromLabel, = row + + if fromLabel == '': # If this address wasn't in our address book... + queryreturn = sqlQuery( + '''select label from subscriptions where address=?''', fromAddress) + if queryreturn != []: + for row in queryreturn: + fromLabel, = row + if fromLabel == '': + fromLabel = fromAddress + + # message row + self.ui.tableWidgetInbox.insertRow(0) + # to + to_item = QtGui.QTableWidgetItem(unicode(toLabel, 'utf-8')) + to_item.setToolTip(unicode(toLabel, 'utf-8')) + to_item.setFlags( + QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) + if not read: + to_item.setFont(font) + to_item.setData(Qt.UserRole, str(toAddress)) + if shared.safeConfigGetBoolean(toAddress, 'mailinglist'): + to_item.setTextColor(QtGui.QColor(137, 04, 177)) # magenta + if shared.safeConfigGetBoolean(str(toAddress), 'chan'): + to_item.setTextColor(QtGui.QColor(216, 119, 0)) # orange + to_item.setIcon(avatarize(toAddress)) + self.ui.tableWidgetInbox.setItem(0, 0, to_item) + # from + from_item = QtGui.QTableWidgetItem(unicode(fromLabel, 'utf-8')) + from_item.setToolTip(unicode(fromLabel, 'utf-8')) + from_item.setFlags( + QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) + if not read: + from_item.setFont(font) + from_item.setData(Qt.UserRole, str(fromAddress)) + if shared.safeConfigGetBoolean(str(fromAddress), 'chan'): + from_item.setTextColor(QtGui.QColor(216, 119, 0)) # orange + from_item.setIcon(avatarize(fromAddress)) + self.ui.tableWidgetInbox.setItem(0, 1, from_item) + # subject + subject_item = QtGui.QTableWidgetItem(unicode(subject, 'utf-8')) + subject_item.setToolTip(unicode(subject, 'utf-8')) + subject_item.setFlags( + QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) + if not read: + subject_item.setFont(font) + self.ui.tableWidgetInbox.setItem(0, 2, subject_item) + # time received + time_item = myTableWidgetItem(l10n.formatTimestamp(received)) + time_item.setToolTip(l10n.formatTimestamp(received)) + time_item.setData(Qt.UserRole, QByteArray(msgid)) + time_item.setData(33, int(received)) + time_item.setFlags( + QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) + if not read: + time_item.setFont(font) + self.ui.tableWidgetInbox.setItem(0, 3, time_item) + + self.ui.tableWidgetInbox.sortItems(3, Qt.DescendingOrder) + self.ui.tableWidgetInbox.keyPressEvent = self.tableWidgetInboxKeyPressEvent + # Load inbox from messages database file def loadInbox(self, where="", what=""): what = "%" + what + "%" @@ -3279,7 +3391,7 @@ class MyForm(QtGui.QMainWindow): else: message = "Error occurred: could not load message from disk." message = unicode(message, 'utf-8)') - self.ui.textEditSentMessage.setPlainText(message) + self.ui.textEditInboxMessage.setPlainText(message) def tableWidgetYourIdentitiesItemChanged(self): currentRow = self.ui.tableWidgetYourIdentities.currentRow() diff --git a/src/bitmessageqt/bitmessageui.py b/src/bitmessageqt/bitmessageui.py index f3a600f6..b4762f43 100644 --- a/src/bitmessageqt/bitmessageui.py +++ b/src/bitmessageqt/bitmessageui.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- -# Form implementation generated from reading ui file 'bmail.ui' +# Form implementation generated from reading ui file 'bitmessageui.ui' # -# Created: Sun Mar 1 23:18:09 2015 +# Created: Wed Mar 4 00:11:02 2015 # by: PyQt4 UI code generator 4.10.4 # # WARNING! All changes made in this file will be lost! @@ -572,7 +572,7 @@ class Ui_MainWindow(object): MainWindow.setTabOrder(self.tableWidgetConnectionCount, self.pushButtonStatusIcon) def retranslateUi(self, MainWindow): - MainWindow.setWindowTitle(_translate("MainWindow", "B-Mail", None)) + MainWindow.setWindowTitle(_translate("MainWindow", "Bitmessage", None)) self.treeWidgetYourIdentities.headerItem().setText(0, _translate("MainWindow", "Identities", None)) self.pushButtonNewAddress.setText(_translate("MainWindow", "New Indentitiy", None)) self.inboxSearchLineEdit.setPlaceholderText(_translate("MainWindow", "Search", None)) From 2d5905a211442171c817f0ee38792d570a1ff370 Mon Sep 17 00:00:00 2001 From: sbkaf Date: Thu, 19 Mar 2015 18:44:10 +0100 Subject: [PATCH 005/210] merge --- src/bitmessageqt/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 3402676a..2dc0d942 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -693,7 +693,6 @@ class MyForm(QtGui.QMainWindow): else: numberOfDays = int(round(TTL / (24*60*60))) self.ui.labelHumanFriendlyTTLDescription.setText(_translate("MainWindow", "%1 days").arg(numberOfDays)) ->>>>>>> bbb8c645afd8900b15ec35a1f1b632e095f41f6e # Show or hide the application window after clicking an item within the # tray icon or, on Windows, the try icon itself. From ee0fce8f8f26ccd081aad9d366c2d670a4191067 Mon Sep 17 00:00:00 2001 From: sbkaf Date: Sat, 21 Mar 2015 11:37:08 +0100 Subject: [PATCH 006/210] start changing QTreeWiget --- src/bitmessageqt/__init__.py | 726 +++++++++++++++++++++---------- src/bitmessageqt/bitmessageui.py | 536 +++++++---------------- src/bitmessageqt/bitmessageui.ui | 600 ++++++++++++------------- 3 files changed, 929 insertions(+), 933 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 2dc0d942..acee89e4 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -214,8 +214,8 @@ class MyForm(QtGui.QMainWindow): "clicked()"), self.click_pushButtonTTL) QtCore.QObject.connect(self.ui.pushButtonSend, QtCore.SIGNAL( "clicked()"), self.click_pushButtonSend) - #QtCore.QObject.connect(self.ui.pushButtonFetchNamecoinID, QtCore.SIGNAL( - #"clicked()"), self.click_pushButtonFetchNamecoinID) + QtCore.QObject.connect(self.ui.pushButtonFetchNamecoinID, QtCore.SIGNAL( + "clicked()"), self.click_pushButtonFetchNamecoinID) QtCore.QObject.connect(self.ui.radioButtonBlacklist, QtCore.SIGNAL( "clicked()"), self.click_radioButtonBlacklist) QtCore.QObject.connect(self.ui.radioButtonWhitelist, QtCore.SIGNAL( @@ -271,35 +271,77 @@ class MyForm(QtGui.QMainWindow): def init_identities_popup_menu(self, connectSignal=True): # Popup menu for the Your Identities tab + self.ui.addressContextMenuToolbarYourIdentities = QtGui.QToolBar() + # Actions + self.actionNewYourIdentities = self.ui.addressContextMenuToolbarYourIdentities.addAction(_translate( + "MainWindow", "New"), self.on_action_YourIdentitiesNew) + self.actionEnableYourIdentities = self.ui.addressContextMenuToolbarYourIdentities.addAction( + _translate( + "MainWindow", "Enable"), self.on_action_Enable) + self.actionDisableYourIdentities = self.ui.addressContextMenuToolbarYourIdentities.addAction( + _translate( + "MainWindow", "Disable"), self.on_action_Disable) + self.actionSetAvatarYourIdentities = self.ui.addressContextMenuToolbarYourIdentities.addAction( + _translate( + "MainWindow", "Set avatar..."), + self.on_action_TreeWidgetSetAvatar) + self.actionClipboardYourIdentities = self.ui.addressContextMenuToolbarYourIdentities.addAction( + _translate( + "MainWindow", "Copy address to clipboard"), + self.on_action_Clipboard) + self.actionSpecialAddressBehaviorYourIdentities = self.ui.addressContextMenuToolbarYourIdentities.addAction( + _translate( + "MainWindow", "Special address behavior..."), + self.on_action_SpecialAddressBehaviorDialog) + + self.ui.treeWidgetYourIdentities.setContextMenuPolicy( + QtCore.Qt.CustomContextMenu) + if connectSignal: + self.connect(self.ui.treeWidgetYourIdentities, QtCore.SIGNAL( + 'customContextMenuRequested(const QPoint&)'), + self.on_context_menuYourIdentities) + + self.popMenuYourIdentities = QtGui.QMenu(self) + self.popMenuYourIdentities.addAction(self.actionNewYourIdentities) + self.popMenuYourIdentities.addSeparator() + self.popMenuYourIdentities.addAction(self.actionClipboardYourIdentities) + self.popMenuYourIdentities.addSeparator() + self.popMenuYourIdentities.addAction(self.actionEnableYourIdentities) + self.popMenuYourIdentities.addAction(self.actionDisableYourIdentities) + self.popMenuYourIdentities.addAction(self.actionSetAvatarYourIdentities) + self.popMenuYourIdentities.addAction(self.actionSpecialAddressBehaviorYourIdentities) + + def init_chan_popup_menu(self, connectSignal=True): + # Popup menu for the Chanels tab self.ui.addressContextMenuToolbar = QtGui.QToolBar() # Actions self.actionNew = self.ui.addressContextMenuToolbar.addAction(_translate( "MainWindow", "New"), self.on_action_YourIdentitiesNew) self.actionEnable = self.ui.addressContextMenuToolbar.addAction( _translate( - "MainWindow", "Enable"), self.on_action_YourIdentitiesEnable) + "MainWindow", "Enable"), self.on_action_Enable) self.actionDisable = self.ui.addressContextMenuToolbar.addAction( _translate( - "MainWindow", "Disable"), self.on_action_YourIdentitiesDisable) + "MainWindow", "Disable"), self.on_action_Disable) self.actionSetAvatar = self.ui.addressContextMenuToolbar.addAction( _translate( "MainWindow", "Set avatar..."), - self.on_action_YourIdentitiesSetAvatar) + self.on_action_TreeWidgetSetAvatar) self.actionClipboard = self.ui.addressContextMenuToolbar.addAction( _translate( "MainWindow", "Copy address to clipboard"), - self.on_action_YourIdentitiesClipboard) + self.on_action_Clipboard) self.actionSpecialAddressBehavior = self.ui.addressContextMenuToolbar.addAction( _translate( "MainWindow", "Special address behavior..."), self.on_action_SpecialAddressBehaviorDialog) - self.ui.tableWidgetYourIdentities.setContextMenuPolicy( + self.ui.treeWidgetChanList.setContextMenuPolicy( QtCore.Qt.CustomContextMenu) if connectSignal: - self.connect(self.ui.tableWidgetYourIdentities, QtCore.SIGNAL( + self.connect(self.ui.treeWidgetChanList, QtCore.SIGNAL( 'customContextMenuRequested(const QPoint&)'), - self.on_context_menuYourIdentities) + self.on_context_menuChan) self.popMenu = QtGui.QMenu(self) self.popMenu.addAction(self.actionNew) @@ -372,11 +414,11 @@ class MyForm(QtGui.QMainWindow): self.on_action_SubscriptionsDisable) self.actionsubscriptionsSetAvatar = self.ui.subscriptionsContextMenuToolbar.addAction( _translate("MainWindow", "Set avatar..."), - self.on_action_SubscriptionsSetAvatar) - self.ui.tableWidgetSubscriptions.setContextMenuPolicy( + self.on_action_TreeWidgetSetAvatar) + self.ui.treeWidgetSubscriptions.setContextMenuPolicy( QtCore.Qt.CustomContextMenu) if connectSignal: - self.connect(self.ui.tableWidgetSubscriptions, QtCore.SIGNAL( + self.connect(self.ui.treeWidgetSubscriptions, QtCore.SIGNAL( 'customContextMenuRequested(const QPoint&)'), self.on_context_menuSubscriptions) self.popMenuSubscriptions = QtGui.QMenu(self) @@ -447,6 +489,58 @@ class MyForm(QtGui.QMainWindow): self.popMenuBlacklist.addAction(self.actionBlacklistDisable) self.popMenuBlacklist.addAction(self.actionBlacklistSetAvatar) + def rerenderTabTree(self, tab): + if tab == 'messages': + treeWidget = self.ui.treeWidgetYourIdentities + folders = ['inbox', 'sent', 'trash'] + elif tab == 'subscriptions': + treeWidget = self.ui.treeWidgetSubscriptions + folders = ['inbox', 'trash'] + elif tab == 'chan': + treeWidget = self.ui.treeWidgetChanList + folders = ['inbox', 'sent', 'trash'] + + treeWidget.clear() + + configSections = shared.config.sections() + for addressInKeysFile in configSections: + if addressInKeysFile != 'bitmessagesettings': + isEnabled = shared.config.getboolean( + addressInKeysFile, 'enabled') + isChan = shared.safeConfigGetBoolean( + addressInKeysFile, 'chan') + isMaillinglist = shared.safeConfigGetBoolean( + addressInKeysFile, 'mailinglist') + + if tab == 'messages': + if isChan or isMaillinglist: + continue + elif tab == 'subscriptions': + if not isMaillinglist: + continue + elif tab == 'chan': + if not isChan: + continue + + newItem = QtGui.QTreeWidgetItem(treeWidget) + newItem.setIcon(0, avatarize(addressInKeysFile)) + newItem.setText(0, unicode( + shared.config.get(addressInKeysFile, 'label'), 'utf-8)') + + ' (' + addressInKeysFile + ')') + newItem.setData(0, Qt.UserRole, [str(addressInKeysFile), "inbox"]) + #set text color + if isEnabled: + brush = QtGui.QBrush(QApplication.palette().text().color()) + else: + brush = QtGui.QBrush(QtGui.QColor(128, 128, 128)) + brush.setStyle(QtCore.Qt.NoBrush) + newItem.setForeground(0, brush) + + for folder in folders: + newSubItem = QtGui.QTreeWidgetItem(newItem) + newSubItem.setText(0, _translate("MainWindow", folder)) + newSubItem.setData(0, Qt.UserRole, [str(addressInKeysFile), folder]) + def __init__(self, parent=None): QtGui.QWidget.__init__(self, parent) self.ui = Ui_MainWindow() @@ -499,66 +593,61 @@ class MyForm(QtGui.QMainWindow): self.init_identities_popup_menu() self.init_addressbook_popup_menu() self.init_subscriptions_popup_menu() + self.init_chan_popup_menu() self.init_sent_popup_menu() self.init_blacklist_popup_menu() - # Initialize the user's list of addresses on the 'Your Identities' table. + # Initialize the user's list of addresses on the 'Chan' tab. + self.rerenderTabTree('chan') + """ + TODO remove configSections = shared.config.sections() for addressInKeysFile in configSections: if addressInKeysFile != 'bitmessagesettings': isEnabled = shared.config.getboolean( addressInKeysFile, 'enabled') - newItem = QtGui.QTableWidgetItem(unicode( - shared.config.get(addressInKeysFile, 'label'), 'utf-8)')) - if not isEnabled: - newItem.setTextColor(QtGui.QColor(128, 128, 128)) - self.ui.tableWidgetYourIdentities.insertRow(0) - newItem.setIcon(avatarize(addressInKeysFile)) - self.ui.tableWidgetYourIdentities.setItem(0, 0, newItem) - newItem = QtGui.QTableWidgetItem(addressInKeysFile) - newItem.setFlags( - QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) if shared.safeConfigGetBoolean(addressInKeysFile, 'chan'): - newItem.setTextColor(QtGui.QColor(216, 119, 0)) # orange - if not isEnabled: - newItem.setTextColor(QtGui.QColor(128, 128, 128)) - if shared.safeConfigGetBoolean(addressInKeysFile, 'mailinglist'): - newItem.setTextColor(QtGui.QColor(137, 04, 177)) # magenta - self.ui.tableWidgetYourIdentities.setItem(0, 1, newItem) - newItem = QtGui.QTableWidgetItem(str( - decodeAddress(addressInKeysFile)[2])) - newItem.setFlags( - QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) - if not isEnabled: - newItem.setTextColor(QtGui.QColor(128, 128, 128)) - self.ui.tableWidgetYourIdentities.setItem(0, 2, newItem) - if isEnabled: - status, addressVersionNumber, streamNumber, hash = decodeAddress( - addressInKeysFile) + newItem = QtGui.QTableWidgetItem(unicode( + shared.config.get(addressInKeysFile, 'label'), 'utf-8)')) + if not isEnabled: + newItem.setTextColor(QtGui.QColor(128, 128, 128)) + self.ui.tableWidgetChanList.insertRow(0) + newItem.setIcon(avatarize(addressInKeysFile)) + self.ui.tableWidgetChanList.setItem(0, 0, newItem) + newItem = QtGui.QTableWidgetItem(addressInKeysFile) + newItem.setFlags( + QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) + if shared.safeConfigGetBoolean(addressInKeysFile, 'chan'): + newItem.setTextColor(QtGui.QColor(216, 119, 0)) # orange + if not isEnabled: + newItem.setTextColor(QtGui.QColor(128, 128, 128)) + if shared.safeConfigGetBoolean(addressInKeysFile, 'mailinglist'): + newItem.setTextColor(QtGui.QColor(137, 04, 177)) # magenta + self.ui.tableWidgetChanList.setItem(0, 1, newItem) + newItem = QtGui.QTableWidgetItem(str( + decodeAddress(addressInKeysFile)[2])) + newItem.setFlags( + QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) + if not isEnabled: + newItem.setTextColor(QtGui.QColor(128, 128, 128)) + self.ui.tableWidgetChanList.setItem(0, 2, newItem) + if isEnabled: + status, addressVersionNumber, streamNumber, hash = decodeAddress( + addressInKeysFile) + """ - # Initialize the user's list of addresses on the 'Received' tab. - configSections = shared.config.sections() - for addressInKeysFile in configSections: - if addressInKeysFile != 'bitmessagesettings': - isEnabled = shared.config.getboolean( - addressInKeysFile, 'enabled') + # Initialize the user's list of addresses on the 'Messages' tab. + self.rerenderTabTree('messages') - if isEnabled and not shared.safeConfigGetBoolean(addressInKeysFile, 'chan') and not shared.safeConfigGetBoolean(addressInKeysFile, 'mailinglist'): - newItem = QtGui.QTreeWidgetItem(self.ui.treeWidgetYourIdentities) - newItem.setIcon(0, avatarize(addressInKeysFile)) - newItem.setText(0, unicode( - shared.config.get(addressInKeysFile, 'label'), 'utf-8)') - + ' (' + addressInKeysFile + ')') - newSubItem = QtGui.QTreeWidgetItem(newItem) - newSubItem.setText(0, _translate("MainWindow", "inbox")) - newSubItem = QtGui.QTreeWidgetItem(newItem) - newSubItem.setText(0, _translate("MainWindow", "sent")) - newSubItem = QtGui.QTreeWidgetItem(newItem) - newSubItem.setText(0, _translate("MainWindow", "trash")) - - # Load inbox from messages database file - #self.loadInbox() - self.loadMessagelist("BM-2cTxBr9RxjorkjvkgTkD1AgjhUJBwDnVKY"); + # Set welcome message + self.ui.textEditInboxMessage.setText( + """ + Welcome to easy and secure Bitmessage + * send messages like e-mails + * send broadcast messages like twitter or + * discuss in chan(el)s with other people + """ + ) # Initialize the address book self.rerenderAddressBook() @@ -576,14 +665,24 @@ class MyForm(QtGui.QMainWindow): self.ui.radioButtonWhitelist.click() self.rerenderBlackWhiteList() - QtCore.QObject.connect(self.ui.tableWidgetYourIdentities, QtCore.SIGNAL( - "itemChanged(QTableWidgetItem *)"), self.tableWidgetYourIdentitiesItemChanged) + # Initialize addresslists QtCore.QObject.connect(self.ui.tableWidgetAddressBook, QtCore.SIGNAL( "itemChanged(QTableWidgetItem *)"), self.tableWidgetAddressBookItemChanged) - QtCore.QObject.connect(self.ui.tableWidgetSubscriptions, QtCore.SIGNAL( - "itemChanged(QTableWidgetItem *)"), self.tableWidgetSubscriptionsItemChanged) + """ + TODO implement + QtCore.QObject.connect(self.ui.treeWidgetSubscriptions, QtCore.SIGNAL( + "itemChanged(QTableWidgetItem *)"), self.treeWidgetSubscriptionsItemChanged) + QtCore.QObject.connect(self.ui.treeWidgetChanList, QtCore.SIGNAL( + "itemChanged(QTableWidgetItem *)"), self.treeWidgetChanListItemChanged) + """ QtCore.QObject.connect(self.ui.tableWidgetInbox, QtCore.SIGNAL( "itemSelectionChanged ()"), self.tableWidgetInboxItemClicked) + QtCore.QObject.connect(self.ui.treeWidgetYourIdentities, QtCore.SIGNAL( + "itemSelectionChanged ()"), self.treeWidgetYourIdentitiesItemClicked) + QtCore.QObject.connect(self.ui.treeWidgetSubscriptions, QtCore.SIGNAL( + "itemSelectionChanged ()"), self.treeWidgetSubscribtionsItemClicked) + QtCore.QObject.connect(self.ui.treeWidgetChanList, QtCore.SIGNAL( + "itemSelectionChanged ()"), self.treeWidgetChanListItemClicked) # Put the colored icon on the status bar # self.ui.pushButtonStatusIcon.setIcon(QIcon(":/newPrefix/images/yellowicon.png")) @@ -598,9 +697,9 @@ class MyForm(QtGui.QMainWindow): # Set the icon sizes for the identicons identicon_size = 3*7 self.ui.tableWidgetInbox.setIconSize(QtCore.QSize(identicon_size, identicon_size)) - self.ui.tableWidgetYourIdentities.setIconSize(QtCore.QSize(identicon_size, identicon_size)) + self.ui.treeWidgetChanList.setIconSize(QtCore.QSize(identicon_size, identicon_size)) self.ui.treeWidgetYourIdentities.setIconSize(QtCore.QSize(identicon_size, identicon_size)) - self.ui.tableWidgetSubscriptions.setIconSize(QtCore.QSize(identicon_size, identicon_size)) + self.ui.treeWidgetSubscriptions.setIconSize(QtCore.QSize(identicon_size, identicon_size)) self.ui.tableWidgetAddressBook.setIconSize(QtCore.QSize(identicon_size, identicon_size)) self.ui.tableWidgetBlacklist.setIconSize(QtCore.QSize(identicon_size, identicon_size)) @@ -649,6 +748,7 @@ class MyForm(QtGui.QMainWindow): # structures were initialized. self.rerenderComboBoxSendFrom() + self.rerenderComboBoxSendFromBroadcast() # Put the TTL slider in the correct spot TTL = shared.config.getint('bitmessagesettings', 'ttl') @@ -671,11 +771,11 @@ class MyForm(QtGui.QMainWindow): options["user"] = shared.config.get('bitmessagesettings', 'namecoinrpcuser') options["password"] = shared.config.get('bitmessagesettings', 'namecoinrpcpassword') nc = namecoinConnection(options) - #if nc.test()[0] == 'failed': - #self.ui.pushButtonFetchNamecoinID.hide() + if nc.test()[0] == 'failed': + self.ui.pushButtonFetchNamecoinID.hide() except: print 'There was a problem testing for a Namecoin daemon. Hiding the Fetch Namecoin ID button' - #self.ui.pushButtonFetchNamecoinID.hide() + self.ui.pushButtonFetchNamecoinID.hide() def updateTTL(self, sliderPosition): TTL = int(sliderPosition ** 3.199 + 3600) @@ -918,7 +1018,7 @@ class MyForm(QtGui.QMainWindow): self.ui.tableWidgetInbox.keyPressEvent = self.tableWidgetInboxKeyPressEvent # Load inbox from messages database file - def loadMessagelist(self, account, folder="inbox", where="", what=""): + def loadMessagelist(self, tableWidget, account, folder="inbox", where="", what=""): what = "%" + what + "%" if where == "To": where = "toaddress" @@ -932,9 +1032,9 @@ class MyForm(QtGui.QMainWindow): where = "toaddress || fromaddress || subject || message" if folder == "sent": - accountaddress = "fromaddress" + accounttype = "fromaddress" else: - accountaddress = "toaddress" + accounttype = "toaddress" sqlStatement = ''' SELECT msgid, toaddress, fromaddress, subject, received, read @@ -942,8 +1042,8 @@ class MyForm(QtGui.QMainWindow): ORDER BY received ''' % (accounttype, where) - while self.ui.tableWidgetInbox.rowCount() > 0: - self.ui.tableWidgetInbox.removeRow(0) + while tableWidget.rowCount() > 0: + tableWidget.removeRow(0) font = QFont() font.setBold(True) @@ -982,7 +1082,7 @@ class MyForm(QtGui.QMainWindow): fromLabel = fromAddress # message row - self.ui.tableWidgetInbox.insertRow(0) + tableWidget.insertRow(0) # to to_item = QtGui.QTableWidgetItem(unicode(toLabel, 'utf-8')) to_item.setToolTip(unicode(toLabel, 'utf-8')) @@ -996,7 +1096,7 @@ class MyForm(QtGui.QMainWindow): if shared.safeConfigGetBoolean(str(toAddress), 'chan'): to_item.setTextColor(QtGui.QColor(216, 119, 0)) # orange to_item.setIcon(avatarize(toAddress)) - self.ui.tableWidgetInbox.setItem(0, 0, to_item) + tableWidget.setItem(0, 0, to_item) # from from_item = QtGui.QTableWidgetItem(unicode(fromLabel, 'utf-8')) from_item.setToolTip(unicode(fromLabel, 'utf-8')) @@ -1008,7 +1108,7 @@ class MyForm(QtGui.QMainWindow): if shared.safeConfigGetBoolean(str(fromAddress), 'chan'): from_item.setTextColor(QtGui.QColor(216, 119, 0)) # orange from_item.setIcon(avatarize(fromAddress)) - self.ui.tableWidgetInbox.setItem(0, 1, from_item) + tableWidget.setItem(0, 1, from_item) # subject subject_item = QtGui.QTableWidgetItem(unicode(subject, 'utf-8')) subject_item.setToolTip(unicode(subject, 'utf-8')) @@ -1016,7 +1116,7 @@ class MyForm(QtGui.QMainWindow): QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) if not read: subject_item.setFont(font) - self.ui.tableWidgetInbox.setItem(0, 2, subject_item) + tableWidget.setItem(0, 2, subject_item) # time received time_item = myTableWidgetItem(l10n.formatTimestamp(received)) time_item.setToolTip(l10n.formatTimestamp(received)) @@ -1026,10 +1126,10 @@ class MyForm(QtGui.QMainWindow): QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) if not read: time_item.setFont(font) - self.ui.tableWidgetInbox.setItem(0, 3, time_item) + tableWidget.setItem(0, 3, time_item) - self.ui.tableWidgetInbox.sortItems(3, Qt.DescendingOrder) - self.ui.tableWidgetInbox.keyPressEvent = self.tableWidgetInboxKeyPressEvent + tableWidget.sortItems(3, Qt.DescendingOrder) + tableWidget.keyPressEvent = self.tableWidgetInboxKeyPressEvent # Load inbox from messages database file def loadInbox(self, where="", what=""): @@ -1586,7 +1686,7 @@ class MyForm(QtGui.QMainWindow): if event.type() == QtCore.QEvent.LanguageChange: self.ui.retranslateUi(self) self.init_inbox_popup_menu(False) - self.init_identities_popup_menu(False) + self.init_chan_popup_menu(False) self.init_addressbook_popup_menu(False) self.init_subscriptions_popup_menu(False) self.init_sent_popup_menu(False) @@ -1970,6 +2070,9 @@ class MyForm(QtGui.QMainWindow): self.ui.tableWidgetAddressBook.setItem(0, 1, newItem) def rerenderSubscriptions(self): + self.rerenderTabTree('subscriptions') + """ + TODO remove self.ui.tableWidgetSubscriptions.setRowCount(0) queryreturn = sqlQuery('SELECT label, address, enabled FROM subscriptions') for row in queryreturn: @@ -1986,6 +2089,7 @@ class MyForm(QtGui.QMainWindow): if not enabled: newItem.setTextColor(QtGui.QColor(128, 128, 128)) self.ui.tableWidgetSubscriptions.setItem(0, 1, newItem) + """ def rerenderBlackWhiteList(self): self.ui.tableWidgetBlacklist.setRowCount(0) @@ -2018,10 +2122,18 @@ more work your computer must do to send the message. A Time-To-Live of four or f def click_pushButtonSend(self): self.statusBar().showMessage('') - toAddresses = str(self.ui.lineEditTo.text()) - subject = str(self.ui.lineEditSubject.text().toUtf8()) - message = str( - self.ui.textEditMessage.document().toPlainText().toUtf8()) + + if self.ui.tabWidgetSend.currentIndex() == 0: + sendMessageToPeople = True + toAddresses = str(self.ui.lineEditTo.text()) + subject = str(self.ui.lineEditSubject.text().toUtf8()) + message = str( + self.ui.textEditMessage.document().toPlainText().toUtf8()) + else: + sendMessageToPeople = False + subject = str(self.ui.lineEditSubjectBroadcast.text().toUtf8()) + message = str( + self.ui.textEditMessageBroadcast.document().toPlainText().toUtf8()) """ The whole network message must fit in 2^18 bytes. Let's assume 500 bytes of overhead. If someone wants to get that too an exact @@ -2033,7 +2145,13 @@ more work your computer must do to send the message. A Time-To-Live of four or f QMessageBox.about(self, _translate("MainWindow", "Message too long"), _translate( "MainWindow", "The message that you are trying to send is too long by %1 bytes. (The maximum is 261644 bytes). Please cut it down before sending.").arg(len(message) - (2 ** 18 - 500))) return - if self.ui.radioButtonSpecific.isChecked(): # To send a message to specific people (rather than broadcast) + + if toAddresses: + print toAddresses + print subject + print message + return + if sendMessageToPeople: # To send a message to specific people (rather than broadcast) toAddressesList = [s.strip() for s in toAddresses.replace(',', ';').split(';')] toAddressesList = list(set( @@ -2166,11 +2284,10 @@ more work your computer must do to send the message. A Time-To-Live of four or f shared.workerQueue.put(('sendbroadcast', '')) - self.ui.comboBoxSendFrom.setCurrentIndex(0) - self.ui.lineEditTo.setText('') - self.ui.lineEditSubject.setText('') - self.ui.textEditMessage.setText('') - self.ui.tabWidget.setCurrentIndex(2) + self.ui.comboBoxSendFromBroadcast.setCurrentIndex(0) + self.ui.lineEditSubjectBroadcast.setText('') + self.ui.textEditMessageBroadcast.setText('') + self.ui.tabWidget.setCurrentIndex(1) self.ui.tableWidgetInbox.setCurrentCell(0, 0) def click_pushButtonLoadFromAddressBook(self): @@ -2197,10 +2314,9 @@ more work your computer must do to send the message. A Time-To-Live of four or f # If this is a chan then don't let people broadcast because no one # should subscribe to chan addresses. if shared.safeConfigGetBoolean(str(address), 'chan'): - self.ui.radioButtonSpecific.click() - self.ui.radioButtonBroadcast.setEnabled(False) + self.ui.tabWidgetSend.setCurrentIndex(0) else: - self.ui.radioButtonBroadcast.setEnabled(True) + self.ui.tabWidgetSend.setCurrentIndex(1) def rerenderComboBoxSendFrom(self): self.ui.comboBoxSendFrom.clear() @@ -2209,7 +2325,8 @@ more work your computer must do to send the message. A Time-To-Live of four or f if addressInKeysFile != 'bitmessagesettings': isEnabled = shared.config.getboolean( addressInKeysFile, 'enabled') # I realize that this is poor programming practice but I don't care. It's easier for others to read. - if isEnabled: + isMaillinglist = shared.safeConfigGetBoolean(addressInKeysFile, 'mailinglist') + if isEnabled and not isMaillinglist: self.ui.comboBoxSendFrom.insertItem(0, avatarize(addressInKeysFile), unicode(shared.config.get( addressInKeysFile, 'label'), 'utf-8'), addressInKeysFile) self.ui.comboBoxSendFrom.insertItem(0, '', '') @@ -2218,6 +2335,23 @@ more work your computer must do to send the message. A Time-To-Live of four or f else: self.ui.comboBoxSendFrom.setCurrentIndex(0) + def rerenderComboBoxSendFromBroadcast(self): + self.ui.comboBoxSendFromBroadcast.clear() + configSections = shared.config.sections() + for addressInKeysFile in configSections: + if addressInKeysFile != 'bitmessagesettings': + isEnabled = shared.config.getboolean( + addressInKeysFile, 'enabled') # I realize that this is poor programming practice but I don't care. It's easier for others to read. + isMaillinglist = shared.safeConfigGetBoolean(addressInKeysFile, 'mailinglist') + if isEnabled and isMaillinglist: + self.ui.comboBoxSendFromBroadcast.insertItem(0, avatarize(addressInKeysFile), unicode(shared.config.get( + addressInKeysFile, 'label'), 'utf-8'), addressInKeysFile) + self.ui.comboBoxSendFromBroadcast.insertItem(0, '', '') + if(self.ui.comboBoxSendFromBroadcast.count() == 2): + self.ui.comboBoxSendFromBroadcast.setCurrentIndex(1) + else: + self.ui.comboBoxSendFromBroadcast.setCurrentIndex(0) + # This function is called by the processmsg function when that function # receives a message to an address that is acting as a # pseudo-mailing-list. The message will be broadcast out. This function @@ -2376,7 +2510,9 @@ more work your computer must do to send the message. A Time-To-Live of four or f #This should be handled outside of this function, for error displaying and such, but it must also be checked here. if shared.isAddressInMySubscriptionsList(address): return + """ #Add to UI list + TODO remove self.ui.tableWidgetSubscriptions.setSortingEnabled(False) self.ui.tableWidgetSubscriptions.insertRow(0) newItem = QtGui.QTableWidgetItem(unicode(label, 'utf-8')) @@ -2386,10 +2522,13 @@ more work your computer must do to send the message. A Time-To-Live of four or f newItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled ) self.ui.tableWidgetSubscriptions.setItem(0,1,newItem) self.ui.tableWidgetSubscriptions.setSortingEnabled(True) + """ #Add to database (perhaps this should be separated from the MyForm class) sqlExecute('''INSERT INTO subscriptions VALUES (?,?,?)''',str(label),address,True) self.rerenderInboxFromLabels() shared.reloadBroadcastSendersForWhichImWatching() + + self.rerenderTabTree('subscriptions') def click_pushButtonAddSubscription(self): self.NewSubscriptionDialogInstance = NewSubscriptionDialog(self) @@ -2400,7 +2539,7 @@ more work your computer must do to send the message. A Time-To-Live of four or f address = addBMIfNotPresent(str(self.NewSubscriptionDialogInstance.ui.lineEditSubscriptionAddress.text())) # We must check to see if the address is already in the subscriptions list. The user cannot add it again or else it will cause problems when updating and deleting the entry. if shared.isAddressInMySubscriptionsList(address): - self.statusBar().showMessage(_translate("MainWindow", "Error: You cannot add the same address to your subsciptions twice. Perhaps rename the existing one if you want.")) + self.statusBar().showMessage(_translate("MainWindow", "Error: You cannot add the same address to your subscriptions twice. Perhaps rename the existing one if you want.")) return label = self.NewSubscriptionDialogInstance.ui.newsubscriptionlabel.text().toUtf8() self.addSubscription(address, label) @@ -2700,9 +2839,7 @@ more work your computer must do to send the message. A Time-To-Live of four or f self.dialog = SpecialAddressBehaviorDialog(self) # For Modal dialogs if self.dialog.exec_(): - currentRow = self.ui.tableWidgetYourIdentities.currentRow() - addressAtCurrentRow = str( - self.ui.tableWidgetYourIdentities.item(currentRow, 1).text()) + addressAtCurrentRow = self.getCurrentAccount() if shared.safeConfigGetBoolean(addressAtCurrentRow, 'chan'): return if self.dialog.ui.radioButtonBehaveNormalAddress.isChecked(): @@ -2710,20 +2847,50 @@ more work your computer must do to send the message. A Time-To-Live of four or f addressAtCurrentRow), 'mailinglist', 'false') # Set the color to either black or grey if shared.config.getboolean(addressAtCurrentRow, 'enabled'): - self.ui.tableWidgetYourIdentities.item( + self.setCurrentItemColor(QApplication.palette() + .text().color()) + else: + self.setCurrentItemColor(QtGui.QColor(128, 128, 128)) + else: + shared.config.set(str( + addressAtCurrentRow), 'mailinglist', 'true') + shared.config.set(str(addressAtCurrentRow), 'mailinglistname', str( + self.dialog.ui.lineEditMailingListName.text().toUtf8())) + self.setCurrentItemColor(QtGui.QColor(137, 04, 177)) #magenta + shared.writeKeysFile() + self.rerenderInboxToLabels() + + """ + TODO remove + def on_action_ChanSpecialAddressBehaviorDialog(self): + self.dialog = SpecialAddressBehaviorDialog(self) + # For Modal dialogs + if self.dialog.exec_(): + currentRow = self.ui.tableWidgetChanList.currentRow() + addressAtCurrentRow = str( + self.ui.tableWidgetChanList.item(currentRow, 1).text()) + if shared.safeConfigGetBoolean(addressAtCurrentRow, 'chan'): + return + if self.dialog.ui.radioButtonBehaveNormalAddress.isChecked(): + shared.config.set(str( + addressAtCurrentRow), 'mailinglist', 'false') + # Set the color to either black or grey + if shared.config.getboolean(addressAtCurrentRow, 'enabled'): + self.ui.tableWidgetChanList.item( currentRow, 1).setTextColor(QApplication.palette() .text().color()) else: - self.ui.tableWidgetYourIdentities.item( + self.ui.tableWidgetChanList.item( currentRow, 1).setTextColor(QtGui.QColor(128, 128, 128)) else: shared.config.set(str( addressAtCurrentRow), 'mailinglist', 'true') shared.config.set(str(addressAtCurrentRow), 'mailinglistname', str( self.dialog.ui.lineEditMailingListName.text().toUtf8())) - self.ui.tableWidgetYourIdentities.item(currentRow, 1).setTextColor(QtGui.QColor(137, 04, 177)) # magenta + self.ui.tableWidgetChanList.item(currentRow, 1).setTextColor(QtGui.QColor(137, 04, 177)) # magenta shared.writeKeysFile() self.rerenderInboxToLabels() + """ def click_NewAddressDialog(self): self.dialog = NewAddressDialog(self) @@ -2910,7 +3077,7 @@ more work your computer must do to send the message. A Time-To-Live of four or f else: self.ui.lineEditSubject.setText( 'Re: ' + self.ui.tableWidgetInbox.item(currentInboxRow, 2).text()) - self.ui.radioButtonSpecific.setChecked(True) + self.ui.tabWidgetSend.setCurrentIndex(0) self.ui.tabWidget.setCurrentIndex(1) def on_action_InboxAddSenderToAddressBook(self): @@ -3086,7 +3253,7 @@ more work your computer must do to send the message. A Time-To-Live of four or f addressAtCurrentRow = str(self.ui.tableWidgetAddressBook.item(currentRow,1).text()) # Then subscribe to it... provided it's not already in the address book if shared.isAddressInMySubscriptionsList(addressAtCurrentRow): - self.statusBar().showMessage(QtGui.QApplication.translate("MainWindow", "Error: You cannot add the same address to your subsciptions twice. Perhaps rename the existing one if you want.")) + self.statusBar().showMessage(QtGui.QApplication.translate("MainWindow", "Error: You cannot add the same address to your subscriptions twice. Perhaps rename the existing one if you want.")) continue labelAtCurrentRow = self.ui.tableWidgetAddressBook.item(currentRow,0).text().toUtf8() self.addSubscription(addressAtCurrentRow, labelAtCurrentRow) @@ -3102,6 +3269,8 @@ more work your computer must do to send the message. A Time-To-Live of four or f def on_action_SubscriptionsDelete(self): print 'clicked Delete' + """ + TODO implement currentRow = self.ui.tableWidgetSubscriptions.currentRow() labelAtCurrentRow = self.ui.tableWidgetSubscriptions.item( currentRow, 0).text().toUtf8() @@ -3112,15 +3281,21 @@ more work your computer must do to send the message. A Time-To-Live of four or f self.ui.tableWidgetSubscriptions.removeRow(currentRow) self.rerenderInboxFromLabels() shared.reloadBroadcastSendersForWhichImWatching() + """ def on_action_SubscriptionsClipboard(self): + """ + TODO implement currentRow = self.ui.tableWidgetSubscriptions.currentRow() addressAtCurrentRow = self.ui.tableWidgetSubscriptions.item( currentRow, 1).text() clipboard = QtGui.QApplication.clipboard() clipboard.setText(str(addressAtCurrentRow)) + """ def on_action_SubscriptionsEnable(self): + """ + TODO implement currentRow = self.ui.tableWidgetSubscriptions.currentRow() labelAtCurrentRow = self.ui.tableWidgetSubscriptions.item( currentRow, 0).text().toUtf8() @@ -3134,8 +3309,11 @@ more work your computer must do to send the message. A Time-To-Live of four or f self.ui.tableWidgetSubscriptions.item( currentRow, 1).setTextColor(QApplication.palette().text().color()) shared.reloadBroadcastSendersForWhichImWatching() + """ def on_action_SubscriptionsDisable(self): + """ + TODO implement currentRow = self.ui.tableWidgetSubscriptions.currentRow() labelAtCurrentRow = self.ui.tableWidgetSubscriptions.item( currentRow, 0).text().toUtf8() @@ -3149,10 +3327,14 @@ more work your computer must do to send the message. A Time-To-Live of four or f self.ui.tableWidgetSubscriptions.item( currentRow, 1).setTextColor(QtGui.QColor(128, 128, 128)) shared.reloadBroadcastSendersForWhichImWatching() + """ def on_context_menuSubscriptions(self, point): + """ + TODO implement self.popMenuSubscriptions.exec_( self.ui.tableWidgetSubscriptions.mapToGlobal(point)) + """ # Group of functions for the Blacklist dialog box def on_action_BlacklistNew(self): @@ -3217,70 +3399,151 @@ more work your computer must do to send the message. A Time-To-Live of four or f sqlExecute( '''UPDATE whitelist SET enabled=0 WHERE address=?''', str(addressAtCurrentRow)) + def getCurrentTreeWidget(self): + currentIndex = self.ui.tabWidget.currentIndex(); + treeWidgetList = [ + self.ui.treeWidgetYourIdentities, + False, + self.ui.treeWidgetSubscriptions, + self.ui.treeWidgetChans + ] + if currentIndex >= 0 and currentIndex < len(treeWidgetList): + return treeWidgetList[currentIndex] + else: + return False + # Group of functions for the Your Identities dialog box + def getCurrentAccount(self): + treeWidget = self.getCurrentTreeWidget() + if treeWidget: + currentItem = treeWidget.currentItem() + if currentItem: + accountFolder = currentItem.data(0, Qt.UserRole).toPyObject() + account = accountFolder[0] + return str(account) + else: + # TODO need debug msg? + return False + + def getCurrentFolder(self): + treeWidget = self.getCurrentTreeWidget() + if treeWidget: + currentItem = treeWidget.currentItem() + if currentItem: + accountFolder = currentItem.data(0, Qt.UserRole).toPyObject() + folder = accountFolder[1] + return str(folder) + else: + # TODO need debug msg? + return False + + def setCurrentItemColor(self, color): + treeWidget = self.getCurrentTreeWidget() + if treeWidget: + brush = QtGui.QBrush() + brush.setStyle(QtCore.Qt.NoBrush) + currentItem = treeWidget.currentItem() + currentItem.setForeground(0, brush) + def on_action_YourIdentitiesNew(self): self.click_NewAddressDialog() - def on_action_YourIdentitiesEnable(self): - currentRow = self.ui.tableWidgetYourIdentities.currentRow() + def on_action_Enable(self): + addressAtCurrentRow = self.getCurrentAccount() + self.enableIdentity(addressAtCurrentRow) + self.setCurrentItemColor(QApplication.palette().text().color()) + + """ + TODO remove + def on_action_ChanEnable(self): + currentRow = self.ui.tableWidgetChanList.currentRow() addressAtCurrentRow = str( - self.ui.tableWidgetYourIdentities.item(currentRow, 1).text()) - shared.config.set(addressAtCurrentRow, 'enabled', 'true') - shared.writeKeysFile() - self.ui.tableWidgetYourIdentities.item( + self.ui.tableWidgetChanList.item(currentRow, 1).text()) + self.ui.tableWidgetChanList.item( currentRow, 0).setTextColor(QApplication.palette().text().color()) - self.ui.tableWidgetYourIdentities.item( + self.ui.tableWidgetChanList.item( currentRow, 1).setTextColor(QApplication.palette().text().color()) - self.ui.tableWidgetYourIdentities.item( + self.ui.tableWidgetChanList.item( currentRow, 2).setTextColor(QApplication.palette().text().color()) if shared.safeConfigGetBoolean(addressAtCurrentRow, 'mailinglist'): - self.ui.tableWidgetYourIdentities.item(currentRow, 1).setTextColor(QtGui.QColor(137, 04, 177)) # magenta + self.ui.tableWidgetChanList.item(currentRow, 1).setTextColor(QtGui.QColor(137, 04, 177)) # magenta if shared.safeConfigGetBoolean(addressAtCurrentRow, 'chan'): - self.ui.tableWidgetYourIdentities.item(currentRow, 1).setTextColor(QtGui.QColor(216, 119, 0)) # orange - shared.reloadMyAddressHashes() + self.ui.tableWidgetChanList.item(currentRow, 1).setTextColor(QtGui.QColor(216, 119, 0)) # orange + self.enableIdentity(addressAtCurrentRow) + """ - def on_action_YourIdentitiesDisable(self): - currentRow = self.ui.tableWidgetYourIdentities.currentRow() - addressAtCurrentRow = str( - self.ui.tableWidgetYourIdentities.item(currentRow, 1).text()) - shared.config.set(str(addressAtCurrentRow), 'enabled', 'false') - self.ui.tableWidgetYourIdentities.item( - currentRow, 0).setTextColor(QtGui.QColor(128, 128, 128)) - self.ui.tableWidgetYourIdentities.item( - currentRow, 1).setTextColor(QtGui.QColor(128, 128, 128)) - self.ui.tableWidgetYourIdentities.item( - currentRow, 2).setTextColor(QtGui.QColor(128, 128, 128)) - if shared.safeConfigGetBoolean(addressAtCurrentRow, 'mailinglist'): - self.ui.tableWidgetYourIdentities.item(currentRow, 1).setTextColor(QtGui.QColor(137, 04, 177)) # magenta + def enableIdentity(self, address): + shared.config.set(address, 'enabled', 'true') shared.writeKeysFile() shared.reloadMyAddressHashes() - def on_action_YourIdentitiesClipboard(self): - currentRow = self.ui.tableWidgetYourIdentities.currentRow() - addressAtCurrentRow = self.ui.tableWidgetYourIdentities.item( - currentRow, 1).text() + def on_action_Disable(self): + address = self.getCurrentAccount() + self.disableIdentity(address) + self.setCurrentItemColor(QtGui.QColor(128, 128, 128)) + + """ + TODO remove + def on_action_ChanDisable(self): + currentRow = self.ui.tableWidgetChanList.currentRow() + addressAtCurrentRow = str( + self.ui.tableWidgetChanList.item(currentRow, 1).text()) + self.ui.tableWidgetChanList.item( + currentRow, 0).setTextColor(QtGui.QColor(128, 128, 128)) + self.ui.tableWidgetChanList.item( + currentRow, 1).setTextColor(QtGui.QColor(128, 128, 128)) + self.ui.tableWidgetChanList.item( + currentRow, 2).setTextColor(QtGui.QColor(128, 128, 128)) + self.disableIdentity(address) + """ + + def disableIdentity(self, address): + shared.config.set(str(addressAtCurrentRow), 'enabled', 'false') + shared.writeKeysFile() + shared.reloadMyAddressHashes() + + def on_action_Clipboard(self): + addressAtCurrentRow = self.getCurrentAccount() clipboard = QtGui.QApplication.clipboard() clipboard.setText(str(addressAtCurrentRow)) - def on_action_YourIdentitiesSetAvatar(self): - self.on_action_SetAvatar(self.ui.tableWidgetYourIdentities) - + """ + TODO remove + def on_action_ChanClipboard(self): + currentRow = self.ui.tableWidgetChanList.currentRow() + addressAtCurrentRow = self.ui.tableWidgetChanList.item( + currentRow, 1).text() + clipboard = QtGui.QApplication.clipboard() + clipboard.setText(str(addressAtCurrentRow)) + """ + + #set avatar functions + def on_action_TreeWidgetSetAvatar(self): + addressAtCurrentRow = self.getCurrentAccount() + treeWidget = self.getCurrentTreeWidget() + setToIdenticon = not self.setAvatar(addressAtCurrentRow) + if treeWidget and setToIdenticon: + currentItem = treeWidget.currentItem() + currentItem.setIcon(0, avatarize(addressAtCurrentRow)) + def on_action_AddressBookSetAvatar(self): self.on_action_SetAvatar(self.ui.tableWidgetAddressBook) - def on_action_SubscriptionsSetAvatar(self): - self.on_action_SetAvatar(self.ui.tableWidgetSubscriptions) - def on_action_BlacklistSetAvatar(self): self.on_action_SetAvatar(self.ui.tableWidgetBlacklist) - + def on_action_SetAvatar(self, thisTableWidget): - # thisTableWidget = self.ui.tableWidgetYourIdentities - if not os.path.exists(shared.appdata + 'avatars/'): - os.makedirs(shared.appdata + 'avatars/') currentRow = thisTableWidget.currentRow() addressAtCurrentRow = thisTableWidget.item( currentRow, 1).text() + setToIdenticon = not self.setAvatar(addressAtCurrentRow) + if setToIdenticon: + thisTableWidget.item( + currentRow, 0).setIcon(avatarize(addressAtCurrentRow)) + + def setAvatar(self, addressAtCurrentRow): + if not os.path.exists(shared.appdata + 'avatars/'): + os.makedirs(shared.appdata + 'avatars/') hash = hashlib.md5(addBMIfNotPresent(addressAtCurrentRow)).hexdigest() extensions = ['PNG', 'GIF', 'JPG', 'JPEG', 'SVG', 'BMP', 'MNG', 'PBM', 'PGM', 'PPM', 'TIFF', 'XBM', 'XPM', 'TGA'] # http://pyqt.sourceforge.net/Docs/PyQt4/qimagereader.html#supportedImageFormats @@ -3331,21 +3594,28 @@ more work your computer must do to send the message. A Time-To-Live of four or f copied = QtCore.QFile.copy(sourcefile, destination) if not copied: print 'couldn\'t copy :(' - return False # set the icon - thisTableWidget.item( - currentRow, 0).setIcon(avatarize(addressAtCurrentRow)) self.rerenderSubscriptions() self.rerenderComboBoxSendFrom() + self.rerenderComboBoxSendFromBroadcast() self.rerenderInboxFromLabels() self.rerenderInboxToLabels() self.rerenderSentFromLabels() self.rerenderSentToLabels() self.rerenderBlackWhiteList() + # generate identicon + return False + + return True + # TODO make one popMenu def on_context_menuYourIdentities(self, point): + self.popMenuYourIdentities.exec_( + self.ui.treeWidgetYourIdentities.mapToGlobal(point)) + + def on_context_menuChan(self, point): self.popMenu.exec_( - self.ui.tableWidgetYourIdentities.mapToGlobal(point)) + self.ui.treeWidgetChanList.mapToGlobal(point)) def on_context_menuInbox(self, point): self.popMenuInbox.exec_(self.ui.tableWidgetInbox.mapToGlobal(point)) @@ -3374,60 +3644,28 @@ more work your computer must do to send the message. A Time-To-Live of four or f self.ui.textEditInboxMessage.setPlainText(QString("")) self.loadInbox(searchOption, searchKeyword) + def treeWidgetYourIdentitiesItemClicked(self): + currentItem = self.ui.treeWidgetYourIdentities.currentItem() + if currentItem: + accountFolder = currentItem.data(0, Qt.UserRole).toPyObject() + account = accountFolder[0] + folder = accountFolder[1] + self.loadMessagelist(self.ui.tableWidgetInbox, str(account), str(folder)) + + # TODO trees + def treeWidgetSubscribtionsItemClicked(self): + pass + + def treeWidgetChanListItemClicked(self): + pass + def tableWidgetInboxItemClicked(self): currentRow = self.ui.tableWidgetInbox.currentRow() if currentRow >= 0: - font = QFont() - font.setBold(False) - self.ui.textEditInboxMessage.setCurrentFont(font) - - fromAddress = str(self.ui.tableWidgetInbox.item( - currentRow, 1).data(Qt.UserRole).toPyObject()) msgid = str(self.ui.tableWidgetInbox.item( currentRow, 3).data(Qt.UserRole).toPyObject()) queryreturn = sqlQuery( '''select message from inbox where msgid=?''', msgid) - if queryreturn != []: - for row in queryreturn: - messageText, = row - messageText = shared.fixPotentiallyInvalidUTF8Data(messageText) - messageText = unicode(messageText, 'utf-8)') - if len(messageText) > 30000: - messageText = ( - messageText[:30000] + '\n' + - '--- Display of the remainder of the message ' + - 'truncated because it is too long.\n' + - '--- To see the full message, right-click in the ' + - 'Inbox view and select "View HTML code as formatted ' + - 'text",\n' + - '--- or select "Save message as..." to save it to a ' + - 'file, or select "Reply" and ' + - 'view the full message in the quote.') - # If we have received this message from either a broadcast address - # or from someone in our address book, display as HTML - if decodeAddress(fromAddress)[3] in shared.broadcastSendersForWhichImWatching or shared.isAddressInMyAddressBook(fromAddress): - self.ui.textEditInboxMessage.setText(messageText) - else: - self.ui.textEditInboxMessage.setPlainText(messageText) - - self.ui.tableWidgetInbox.item(currentRow, 0).setFont(font) - self.ui.tableWidgetInbox.item(currentRow, 1).setFont(font) - self.ui.tableWidgetInbox.item(currentRow, 2).setFont(font) - self.ui.tableWidgetInbox.item(currentRow, 3).setFont(font) - - inventoryHash = str(self.ui.tableWidgetInbox.item( - currentRow, 3).data(Qt.UserRole).toPyObject()) - self.ubuntuMessagingMenuClear(inventoryHash) - sqlExecute('''update inbox set read=1 WHERE msgid=?''', inventoryHash) - self.changedInboxUnread() - - def tableWidgetInboxItemClicked(self): - currentRow = self.ui.tableWidgetInbox.currentRow() - if currentRow >= 0: - ackdata = str(self.ui.tableWidgetInbox.item( - currentRow, 3).data(Qt.UserRole).toPyObject()) - queryreturn = sqlQuery( - '''select message from sent where ackdata=?''', ackdata) if queryreturn != []: for row in queryreturn: message, = row @@ -3436,20 +3674,6 @@ more work your computer must do to send the message. A Time-To-Live of four or f message = unicode(message, 'utf-8)') self.ui.textEditInboxMessage.setPlainText(message) - def tableWidgetYourIdentitiesItemChanged(self): - currentRow = self.ui.tableWidgetYourIdentities.currentRow() - if currentRow >= 0: - addressAtCurrentRow = self.ui.tableWidgetYourIdentities.item( - currentRow, 1).text() - shared.config.set(str(addressAtCurrentRow), 'label', str( - self.ui.tableWidgetYourIdentities.item(currentRow, 0).text().toUtf8())) - shared.writeKeysFile() - self.rerenderComboBoxSendFrom() - # self.rerenderInboxFromLabels() - self.rerenderInboxToLabels() - self.rerenderSentFromLabels() - # self.rerenderSentToLabels() - def tableWidgetAddressBookItemChanged(self): currentRow = self.ui.tableWidgetAddressBook.currentRow() if currentRow >= 0: @@ -3461,7 +3685,9 @@ more work your computer must do to send the message. A Time-To-Live of four or f self.rerenderInboxFromLabels() self.rerenderSentToLabels() - def tableWidgetSubscriptionsItemChanged(self): + """ + TODO implement + def treeWidgetSubscriptionsItemChanged(self): currentRow = self.ui.tableWidgetSubscriptions.currentRow() if currentRow >= 0: addressAtCurrentRow = self.ui.tableWidgetSubscriptions.item( @@ -3472,25 +3698,46 @@ more work your computer must do to send the message. A Time-To-Live of four or f self.rerenderInboxFromLabels() self.rerenderSentToLabels() + def treeWidgetChanListItemChanged(self): + currentRow = self.ui.tableWidgetChanList.currentRow() + if currentRow >= 0: + addressAtCurrentRow = self.ui.tableWidgetChanList.item( + currentRow, 1).text() + shared.config.set(str(addressAtCurrentRow), 'label', str( + self.ui.tableWidgetChanList.item(currentRow, 0).text().toUtf8())) + shared.writeKeysFile() + self.rerenderComboBoxSendFrom() + self.rerenderComboBoxSendFromBroadcast() + # self.rerenderInboxFromLabels() + self.rerenderInboxToLabels() + self.rerenderSentFromLabels() + # self.rerenderSentToLabels() + """ + def writeNewAddressToTable(self, label, address, streamNumber): - self.ui.tableWidgetYourIdentities.setSortingEnabled(False) - self.ui.tableWidgetYourIdentities.insertRow(0) + pass + """ + TODO implement + self.ui.tableWidgetChanList.setSortingEnabled(False) + self.ui.tableWidgetChanList.insertRow(0) newItem = QtGui.QTableWidgetItem(unicode(label, 'utf-8')) newItem.setIcon(avatarize(address)) - self.ui.tableWidgetYourIdentities.setItem( + self.ui.tableWidgetChanList.setItem( 0, 0, newItem) newItem = QtGui.QTableWidgetItem(address) newItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) if shared.safeConfigGetBoolean(address, 'chan'): newItem.setTextColor(QtGui.QColor(216, 119, 0)) # orange - self.ui.tableWidgetYourIdentities.setItem(0, 1, newItem) + self.ui.tableWidgetChanList.setItem(0, 1, newItem) newItem = QtGui.QTableWidgetItem(streamNumber) newItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) - self.ui.tableWidgetYourIdentities.setItem(0, 2, newItem) - # self.ui.tableWidgetYourIdentities.setSortingEnabled(True) + self.ui.tableWidgetChanList.setItem(0, 2, newItem) + # self.ui.tableWidgetChanList.setSortingEnabled(True) self.rerenderComboBoxSendFrom() + self.rerenderComboBoxSendFromBroadcast() + """ def updateStatusBar(self, data): if data != "": @@ -3749,8 +3996,8 @@ class settingsDialog(QtGui.QDialog): responseStatus = response[0] responseText = response[1] self.ui.labelNamecoinTestResult.setText(responseText) - #if responseStatus== 'success': - #self.parent.ui.pushButtonFetchNamecoinID.show() + if responseStatus== 'success': + self.parent.ui.pushButtonFetchNamecoinID.show() class SpecialAddressBehaviorDialog(QtGui.QDialog): @@ -3760,9 +4007,7 @@ class SpecialAddressBehaviorDialog(QtGui.QDialog): self.ui = Ui_SpecialAddressBehaviorDialog() self.ui.setupUi(self) self.parent = parent - currentRow = parent.ui.tableWidgetYourIdentities.currentRow() - addressAtCurrentRow = str( - parent.ui.tableWidgetYourIdentities.item(currentRow, 1).text()) + addressAtCurrentRow = parent.getCurrentAccount() if not shared.safeConfigGetBoolean(addressAtCurrentRow, 'chan'): if shared.safeConfigGetBoolean(addressAtCurrentRow, 'mailinglist'): self.ui.radioButtonBehaviorMailingList.click() @@ -3782,6 +4027,38 @@ class SpecialAddressBehaviorDialog(QtGui.QDialog): QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) +""" +TODO remove +class SpecialAddressBehaviorDialog(QtGui.QDialog): + + def __init__(self, parent): + QtGui.QWidget.__init__(self, parent) + self.ui = Ui_SpecialAddressBehaviorDialog() + self.ui.setupUi(self) + self.parent = parent + currentRow = parent.ui.tableWidgetChanList.currentRow() + addressAtCurrentRow = str( + parent.ui.tableWidgetChanList.item(currentRow, 1).text()) + if not shared.safeConfigGetBoolean(addressAtCurrentRow, 'chan'): + if shared.safeConfigGetBoolean(addressAtCurrentRow, 'mailinglist'): + self.ui.radioButtonBehaviorMailingList.click() + else: + self.ui.radioButtonBehaveNormalAddress.click() + try: + mailingListName = shared.config.get( + addressAtCurrentRow, 'mailinglistname') + except: + mailingListName = '' + self.ui.lineEditMailingListName.setText( + unicode(mailingListName, 'utf-8')) + else: # if addressAtCurrentRow is a chan address + self.ui.radioButtonBehaviorMailingList.setDisabled(True) + self.ui.lineEditMailingListName.setText(_translate( + "MainWindow", "This is a chan address. You cannot use it as a pseudo-mailing list.")) + + QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) +""" + class AddAddressDialog(QtGui.QDialog): @@ -3886,6 +4163,8 @@ class NewSubscriptionDialog(QtGui.QDialog): class NewAddressDialog(QtGui.QDialog): def __init__(self, parent): + """ + TODO implement QtGui.QWidget.__init__(self, parent) self.ui = Ui_NewAddressDialog() self.ui.setupUi(self) @@ -3893,13 +4172,14 @@ class NewAddressDialog(QtGui.QDialog): row = 1 # Let's fill out the 'existing address' combo box with addresses from # the 'Your Identities' tab. - while self.parent.ui.tableWidgetYourIdentities.item(row - 1, 1): + while self.parent.ui.tableWidgetChanList.item(row - 1, 1): self.ui.radioButtonExisting.click() self.ui.comboBoxExisting.addItem( - self.parent.ui.tableWidgetYourIdentities.item(row - 1, 1).text()) + self.parent.ui.tableWidgetChanList.item(row - 1, 1).text()) row += 1 self.ui.groupBoxDeterministic.setHidden(True) QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) + """ class newChanDialog(QtGui.QDialog): diff --git a/src/bitmessageqt/bitmessageui.py b/src/bitmessageqt/bitmessageui.py index 073dd457..17ab57ee 100644 --- a/src/bitmessageqt/bitmessageui.py +++ b/src/bitmessageqt/bitmessageui.py @@ -2,13 +2,8 @@ # Form implementation generated from reading ui file 'bitmessageui.ui' # -<<<<<<< HEAD -# Created: Wed Mar 4 00:11:02 2015 +# Created: Fri Mar 20 19:19:21 2015 # by: PyQt4 UI code generator 4.10.4 -======= -# Created: Sun Mar 08 22:07:43 2015 -# by: PyQt4 UI code generator 4.10.3 ->>>>>>> bbb8c645afd8900b15ec35a1f1b632e095f41f6e # # WARNING! All changes made in this file will be lost! @@ -121,8 +116,8 @@ class Ui_MainWindow(object): self.tabWidget.addTab(self.inbox, icon2, _fromUtf8("")) self.send = QtGui.QWidget() self.send.setObjectName(_fromUtf8("send")) - self.gridLayout_11 = QtGui.QGridLayout(self.send) - self.gridLayout_11.setObjectName(_fromUtf8("gridLayout_11")) + self.gridLayout_7 = QtGui.QGridLayout(self.send) + self.gridLayout_7.setObjectName(_fromUtf8("gridLayout_7")) self.horizontalLayout = QtGui.QHBoxLayout() self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout")) self.verticalLayout_2 = QtGui.QVBoxLayout() @@ -152,28 +147,26 @@ class Ui_MainWindow(object): self.pushButtonAddAddressBook.setMaximumSize(QtCore.QSize(200, 16777215)) self.pushButtonAddAddressBook.setObjectName(_fromUtf8("pushButtonAddAddressBook")) self.verticalLayout_2.addWidget(self.pushButtonAddAddressBook) + self.pushButtonFetchNamecoinID = QtGui.QPushButton(self.send) + self.pushButtonFetchNamecoinID.setMaximumSize(QtCore.QSize(200, 16777215)) + font = QtGui.QFont() + font.setPointSize(9) + self.pushButtonFetchNamecoinID.setFont(font) + self.pushButtonFetchNamecoinID.setObjectName(_fromUtf8("pushButtonFetchNamecoinID")) + self.verticalLayout_2.addWidget(self.pushButtonFetchNamecoinID) self.horizontalLayout.addLayout(self.verticalLayout_2) - self.tabWidget_2 = QtGui.QTabWidget(self.send) - self.tabWidget_2.setObjectName(_fromUtf8("tabWidget_2")) + self.verticalLayout = QtGui.QVBoxLayout() + self.verticalLayout.setObjectName(_fromUtf8("verticalLayout")) + self.tabWidgetSend = QtGui.QTabWidget(self.send) + self.tabWidgetSend.setObjectName(_fromUtf8("tabWidgetSend")) self.tab = QtGui.QWidget() self.tab.setObjectName(_fromUtf8("tab")) self.gridLayout_8 = QtGui.QGridLayout(self.tab) self.gridLayout_8.setObjectName(_fromUtf8("gridLayout_8")) - self.verticalLayout = QtGui.QVBoxLayout() - self.verticalLayout.setObjectName(_fromUtf8("verticalLayout")) + self.verticalLayout_5 = QtGui.QVBoxLayout() + self.verticalLayout_5.setObjectName(_fromUtf8("verticalLayout_5")) self.gridLayout_2 = QtGui.QGridLayout() self.gridLayout_2.setObjectName(_fromUtf8("gridLayout_2")) -<<<<<<< HEAD - self.comboBoxSendFrom = QtGui.QComboBox(self.tab) - self.comboBoxSendFrom.setMinimumSize(QtCore.QSize(300, 0)) - self.comboBoxSendFrom.setObjectName(_fromUtf8("comboBoxSendFrom")) - self.gridLayout_2.addWidget(self.comboBoxSendFrom, 0, 1, 1, 1) - self.label = QtGui.QLabel(self.tab) - self.label.setObjectName(_fromUtf8("label")) - self.gridLayout_2.addWidget(self.label, 1, 0, 1, 1) - self.lineEditTo = QtGui.QLineEdit(self.tab) - self.lineEditTo.setObjectName(_fromUtf8("lineEditTo")) - self.gridLayout_2.addWidget(self.lineEditTo, 1, 1, 1, 1) self.label_3 = QtGui.QLabel(self.tab) self.label_3.setObjectName(_fromUtf8("label_3")) self.gridLayout_2.addWidget(self.label_3, 2, 0, 1, 1) @@ -184,160 +177,53 @@ class Ui_MainWindow(object): self.lineEditSubject.setText(_fromUtf8("")) self.lineEditSubject.setObjectName(_fromUtf8("lineEditSubject")) self.gridLayout_2.addWidget(self.lineEditSubject, 2, 1, 1, 1) - self.verticalLayout.addLayout(self.gridLayout_2) + self.label = QtGui.QLabel(self.tab) + self.label.setObjectName(_fromUtf8("label")) + self.gridLayout_2.addWidget(self.label, 1, 0, 1, 1) + self.comboBoxSendFrom = QtGui.QComboBox(self.tab) + self.comboBoxSendFrom.setMinimumSize(QtCore.QSize(300, 0)) + self.comboBoxSendFrom.setObjectName(_fromUtf8("comboBoxSendFrom")) + self.gridLayout_2.addWidget(self.comboBoxSendFrom, 0, 1, 1, 1) + self.lineEditTo = QtGui.QLineEdit(self.tab) + self.lineEditTo.setObjectName(_fromUtf8("lineEditTo")) + self.gridLayout_2.addWidget(self.lineEditTo, 1, 1, 1, 1) + self.verticalLayout_5.addLayout(self.gridLayout_2) self.textEditMessage = QtGui.QTextEdit(self.tab) self.textEditMessage.setObjectName(_fromUtf8("textEditMessage")) - self.verticalLayout.addWidget(self.textEditMessage) - self.pushButtonSend = QtGui.QPushButton(self.tab) - self.pushButtonSend.setObjectName(_fromUtf8("pushButtonSend")) - self.verticalLayout.addWidget(self.pushButtonSend) - self.gridLayout_8.addLayout(self.verticalLayout, 0, 0, 1, 1) - self.tabWidget_2.addTab(self.tab, _fromUtf8("")) + self.verticalLayout_5.addWidget(self.textEditMessage) + self.gridLayout_8.addLayout(self.verticalLayout_5, 0, 0, 1, 1) + self.tabWidgetSend.addTab(self.tab, _fromUtf8("")) self.tab_2 = QtGui.QWidget() self.tab_2.setObjectName(_fromUtf8("tab_2")) self.gridLayout_9 = QtGui.QGridLayout(self.tab_2) self.gridLayout_9.setObjectName(_fromUtf8("gridLayout_9")) - self.verticalLayout_5 = QtGui.QVBoxLayout() - self.verticalLayout_5.setObjectName(_fromUtf8("verticalLayout_5")) + self.verticalLayout_6 = QtGui.QVBoxLayout() + self.verticalLayout_6.setObjectName(_fromUtf8("verticalLayout_6")) self.gridLayout_5 = QtGui.QGridLayout() self.gridLayout_5.setObjectName(_fromUtf8("gridLayout_5")) self.label_8 = QtGui.QLabel(self.tab_2) self.label_8.setObjectName(_fromUtf8("label_8")) self.gridLayout_5.addWidget(self.label_8, 0, 0, 1, 1) - self.lineEditSubject_2 = QtGui.QLineEdit(self.tab_2) - self.lineEditSubject_2.setText(_fromUtf8("")) - self.lineEditSubject_2.setObjectName(_fromUtf8("lineEditSubject_2")) - self.gridLayout_5.addWidget(self.lineEditSubject_2, 1, 1, 1, 1) + self.lineEditSubjectBroadcast = QtGui.QLineEdit(self.tab_2) + self.lineEditSubjectBroadcast.setText(_fromUtf8("")) + self.lineEditSubjectBroadcast.setObjectName(_fromUtf8("lineEditSubjectBroadcast")) + self.gridLayout_5.addWidget(self.lineEditSubjectBroadcast, 1, 1, 1, 1) self.label_7 = QtGui.QLabel(self.tab_2) self.label_7.setObjectName(_fromUtf8("label_7")) self.gridLayout_5.addWidget(self.label_7, 1, 0, 1, 1) - self.comboBoxSendFrom_2 = QtGui.QComboBox(self.tab_2) - self.comboBoxSendFrom_2.setMinimumSize(QtCore.QSize(300, 0)) - self.comboBoxSendFrom_2.setObjectName(_fromUtf8("comboBoxSendFrom_2")) - self.gridLayout_5.addWidget(self.comboBoxSendFrom_2, 0, 1, 1, 1) - self.verticalLayout_5.addLayout(self.gridLayout_5) - self.textEditMessage_2 = QtGui.QTextEdit(self.tab_2) - self.textEditMessage_2.setObjectName(_fromUtf8("textEditMessage_2")) - self.verticalLayout_5.addWidget(self.textEditMessage_2) - self.pushButtonSend_3 = QtGui.QPushButton(self.tab_2) - self.pushButtonSend_3.setObjectName(_fromUtf8("pushButtonSend_3")) - self.verticalLayout_5.addWidget(self.pushButtonSend_3) - self.gridLayout_9.addLayout(self.verticalLayout_5, 0, 0, 1, 1) - self.tabWidget_2.addTab(self.tab_2, _fromUtf8("")) - self.horizontalLayout.addWidget(self.tabWidget_2) - self.gridLayout_11.addLayout(self.horizontalLayout, 0, 0, 1, 1) - icon4 = QtGui.QIcon() - icon4.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/send.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) - self.tabWidget.addTab(self.send, icon4, _fromUtf8("")) - self.subscriptions = QtGui.QWidget() - self.subscriptions.setObjectName(_fromUtf8("subscriptions")) - self.gridLayout_4 = QtGui.QGridLayout(self.subscriptions) - self.gridLayout_4.setObjectName(_fromUtf8("gridLayout_4")) - self.horizontalLayout_2 = QtGui.QHBoxLayout() - self.horizontalLayout_2.setObjectName(_fromUtf8("horizontalLayout_2")) - self.verticalLayout_3 = QtGui.QVBoxLayout() - self.verticalLayout_3.setObjectName(_fromUtf8("verticalLayout_3")) - self.tableWidgetSubscriptions = QtGui.QTableWidget(self.subscriptions) - self.tableWidgetSubscriptions.setMaximumSize(QtCore.QSize(200, 16777215)) - self.tableWidgetSubscriptions.setAlternatingRowColors(True) - self.tableWidgetSubscriptions.setSelectionMode(QtGui.QAbstractItemView.SingleSelection) - self.tableWidgetSubscriptions.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) - self.tableWidgetSubscriptions.setObjectName(_fromUtf8("tableWidgetSubscriptions")) - self.tableWidgetSubscriptions.setColumnCount(2) - self.tableWidgetSubscriptions.setRowCount(0) - item = QtGui.QTableWidgetItem() - icon5 = QtGui.QIcon() - icon5.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/subscriptions.png")), QtGui.QIcon.Selected, QtGui.QIcon.Off) - item.setIcon(icon5) - self.tableWidgetSubscriptions.setHorizontalHeaderItem(0, item) - item = QtGui.QTableWidgetItem() - self.tableWidgetSubscriptions.setHorizontalHeaderItem(1, item) - self.tableWidgetSubscriptions.horizontalHeader().setCascadingSectionResizes(True) - self.tableWidgetSubscriptions.horizontalHeader().setDefaultSectionSize(200) - self.tableWidgetSubscriptions.horizontalHeader().setHighlightSections(False) - self.tableWidgetSubscriptions.horizontalHeader().setSortIndicatorShown(False) - self.tableWidgetSubscriptions.horizontalHeader().setStretchLastSection(True) - self.tableWidgetSubscriptions.verticalHeader().setVisible(False) - self.verticalLayout_3.addWidget(self.tableWidgetSubscriptions) - self.pushButtonAddSubscription = QtGui.QPushButton(self.subscriptions) - self.pushButtonAddSubscription.setMaximumSize(QtCore.QSize(200, 16777215)) - self.pushButtonAddSubscription.setObjectName(_fromUtf8("pushButtonAddSubscription")) - self.verticalLayout_3.addWidget(self.pushButtonAddSubscription) - self.horizontalLayout_2.addLayout(self.verticalLayout_3) - self.verticalLayout_4 = QtGui.QVBoxLayout() - self.verticalLayout_4.setObjectName(_fromUtf8("verticalLayout_4")) - self.inboxSearchLineSubscriptions = QtGui.QLineEdit(self.subscriptions) - self.inboxSearchLineSubscriptions.setObjectName(_fromUtf8("inboxSearchLineSubscriptions")) - self.verticalLayout_4.addWidget(self.inboxSearchLineSubscriptions) - self.tableWidgetInboxSubscriptions = QtGui.QTableWidget(self.subscriptions) - self.tableWidgetInboxSubscriptions.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers) - self.tableWidgetInboxSubscriptions.setAlternatingRowColors(True) - self.tableWidgetInboxSubscriptions.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection) - self.tableWidgetInboxSubscriptions.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) - self.tableWidgetInboxSubscriptions.setWordWrap(False) - self.tableWidgetInboxSubscriptions.setObjectName(_fromUtf8("tableWidgetInboxSubscriptions")) - self.tableWidgetInboxSubscriptions.setColumnCount(4) - self.tableWidgetInboxSubscriptions.setRowCount(0) -======= - self.pushButtonLoadFromAddressBook = QtGui.QPushButton(self.send) - font = QtGui.QFont() - font.setPointSize(7) - self.pushButtonLoadFromAddressBook.setFont(font) - self.pushButtonLoadFromAddressBook.setObjectName(_fromUtf8("pushButtonLoadFromAddressBook")) - self.gridLayout_2.addWidget(self.pushButtonLoadFromAddressBook, 3, 2, 1, 1) - self.label_3 = QtGui.QLabel(self.send) - self.label_3.setObjectName(_fromUtf8("label_3")) - self.gridLayout_2.addWidget(self.label_3, 4, 0, 1, 1) - self.pushButtonSend = QtGui.QPushButton(self.send) - self.pushButtonSend.setObjectName(_fromUtf8("pushButtonSend")) - self.gridLayout_2.addWidget(self.pushButtonSend, 7, 8, 1, 1) - self.horizontalSliderTTL = QtGui.QSlider(self.send) - self.horizontalSliderTTL.setMinimumSize(QtCore.QSize(35, 0)) - self.horizontalSliderTTL.setMaximumSize(QtCore.QSize(70, 16777215)) - self.horizontalSliderTTL.setOrientation(QtCore.Qt.Horizontal) - self.horizontalSliderTTL.setInvertedAppearance(False) - self.horizontalSliderTTL.setInvertedControls(False) - self.horizontalSliderTTL.setObjectName(_fromUtf8("horizontalSliderTTL")) - self.gridLayout_2.addWidget(self.horizontalSliderTTL, 7, 6, 1, 1) - spacerItem = QtGui.QSpacerItem(20, 297, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) - self.gridLayout_2.addItem(spacerItem, 6, 0, 1, 1) - self.comboBoxSendFrom = QtGui.QComboBox(self.send) - self.comboBoxSendFrom.setMinimumSize(QtCore.QSize(300, 0)) - self.comboBoxSendFrom.setObjectName(_fromUtf8("comboBoxSendFrom")) - self.gridLayout_2.addWidget(self.comboBoxSendFrom, 2, 1, 1, 1) - self.labelHumanFriendlyTTLDescription = QtGui.QLabel(self.send) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.labelHumanFriendlyTTLDescription.sizePolicy().hasHeightForWidth()) - self.labelHumanFriendlyTTLDescription.setSizePolicy(sizePolicy) - self.labelHumanFriendlyTTLDescription.setMinimumSize(QtCore.QSize(45, 0)) - self.labelHumanFriendlyTTLDescription.setMaximumSize(QtCore.QSize(45, 16777215)) - self.labelHumanFriendlyTTLDescription.setObjectName(_fromUtf8("labelHumanFriendlyTTLDescription")) - self.gridLayout_2.addWidget(self.labelHumanFriendlyTTLDescription, 7, 7, 1, 1) - self.label_4 = QtGui.QLabel(self.send) - self.label_4.setObjectName(_fromUtf8("label_4")) - self.gridLayout_2.addWidget(self.label_4, 5, 0, 1, 1) - self.label = QtGui.QLabel(self.send) - self.label.setObjectName(_fromUtf8("label")) - self.gridLayout_2.addWidget(self.label, 3, 0, 1, 1) - self.radioButtonSpecific = QtGui.QRadioButton(self.send) - self.radioButtonSpecific.setChecked(True) - self.radioButtonSpecific.setObjectName(_fromUtf8("radioButtonSpecific")) - self.gridLayout_2.addWidget(self.radioButtonSpecific, 0, 1, 1, 1) - self.labelSendBroadcastWarning = QtGui.QLabel(self.send) - self.labelSendBroadcastWarning.setEnabled(True) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Ignored, QtGui.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.labelSendBroadcastWarning.sizePolicy().hasHeightForWidth()) - self.labelSendBroadcastWarning.setSizePolicy(sizePolicy) - self.labelSendBroadcastWarning.setIndent(-1) - self.labelSendBroadcastWarning.setObjectName(_fromUtf8("labelSendBroadcastWarning")) - self.gridLayout_2.addWidget(self.labelSendBroadcastWarning, 7, 1, 1, 4) - self.radioButtonBroadcast = QtGui.QRadioButton(self.send) - self.radioButtonBroadcast.setObjectName(_fromUtf8("radioButtonBroadcast")) - self.gridLayout_2.addWidget(self.radioButtonBroadcast, 1, 1, 1, 2) + self.comboBoxSendFromBroadcast = QtGui.QComboBox(self.tab_2) + self.comboBoxSendFromBroadcast.setMinimumSize(QtCore.QSize(300, 0)) + self.comboBoxSendFromBroadcast.setObjectName(_fromUtf8("comboBoxSendFromBroadcast")) + self.gridLayout_5.addWidget(self.comboBoxSendFromBroadcast, 0, 1, 1, 1) + self.verticalLayout_6.addLayout(self.gridLayout_5) + self.textEditMessageBroadcast = QtGui.QTextEdit(self.tab_2) + self.textEditMessageBroadcast.setObjectName(_fromUtf8("textEditMessageBroadcast")) + self.verticalLayout_6.addWidget(self.textEditMessageBroadcast) + self.gridLayout_9.addLayout(self.verticalLayout_6, 0, 0, 1, 1) + self.tabWidgetSend.addTab(self.tab_2, _fromUtf8("")) + self.verticalLayout.addWidget(self.tabWidgetSend) + self.horizontalLayout_5 = QtGui.QHBoxLayout() + self.horizontalLayout_5.setObjectName(_fromUtf8("horizontalLayout_5")) self.pushButtonTTL = QtGui.QPushButton(self.send) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) @@ -361,66 +247,72 @@ class Ui_MainWindow(object): self.pushButtonTTL.setFont(font) self.pushButtonTTL.setFlat(True) self.pushButtonTTL.setObjectName(_fromUtf8("pushButtonTTL")) - self.gridLayout_2.addWidget(self.pushButtonTTL, 7, 5, 1, 1) - self.label_2 = QtGui.QLabel(self.send) - self.label_2.setObjectName(_fromUtf8("label_2")) - self.gridLayout_2.addWidget(self.label_2, 2, 0, 1, 1) - self.lineEditTo = QtGui.QLineEdit(self.send) - self.lineEditTo.setObjectName(_fromUtf8("lineEditTo")) - self.gridLayout_2.addWidget(self.lineEditTo, 3, 1, 1, 1) - self.textEditMessage = QtGui.QTextEdit(self.send) - self.textEditMessage.setObjectName(_fromUtf8("textEditMessage")) - self.gridLayout_2.addWidget(self.textEditMessage, 5, 1, 2, 8) - self.pushButtonFetchNamecoinID = QtGui.QPushButton(self.send) - font = QtGui.QFont() - font.setPointSize(7) - self.pushButtonFetchNamecoinID.setFont(font) - self.pushButtonFetchNamecoinID.setObjectName(_fromUtf8("pushButtonFetchNamecoinID")) - self.gridLayout_2.addWidget(self.pushButtonFetchNamecoinID, 3, 3, 1, 1) - self.labelFrom = QtGui.QLabel(self.send) - self.labelFrom.setText(_fromUtf8("")) - self.labelFrom.setObjectName(_fromUtf8("labelFrom")) - self.gridLayout_2.addWidget(self.labelFrom, 2, 2, 1, 7) - self.lineEditSubject = QtGui.QLineEdit(self.send) - self.lineEditSubject.setText(_fromUtf8("")) - self.lineEditSubject.setObjectName(_fromUtf8("lineEditSubject")) - self.gridLayout_2.addWidget(self.lineEditSubject, 4, 1, 1, 8) - icon2 = QtGui.QIcon() - icon2.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/send.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) - self.tabWidget.addTab(self.send, icon2, _fromUtf8("")) - self.sent = QtGui.QWidget() - self.sent.setObjectName(_fromUtf8("sent")) - self.verticalLayout = QtGui.QVBoxLayout(self.sent) - self.verticalLayout.setObjectName(_fromUtf8("verticalLayout")) - self.horizontalLayout = QtGui.QHBoxLayout() - self.horizontalLayout.setContentsMargins(-1, 0, -1, -1) - self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout")) - self.sentSearchLineEdit = QtGui.QLineEdit(self.sent) - self.sentSearchLineEdit.setObjectName(_fromUtf8("sentSearchLineEdit")) - self.horizontalLayout.addWidget(self.sentSearchLineEdit) - self.sentSearchOptionCB = QtGui.QComboBox(self.sent) - self.sentSearchOptionCB.setObjectName(_fromUtf8("sentSearchOptionCB")) - self.sentSearchOptionCB.addItem(_fromUtf8("")) - self.sentSearchOptionCB.addItem(_fromUtf8("")) - self.sentSearchOptionCB.addItem(_fromUtf8("")) - self.sentSearchOptionCB.addItem(_fromUtf8("")) - self.sentSearchOptionCB.addItem(_fromUtf8("")) - self.horizontalLayout.addWidget(self.sentSearchOptionCB) - self.verticalLayout.addLayout(self.horizontalLayout) - self.splitter_2 = QtGui.QSplitter(self.sent) - self.splitter_2.setOrientation(QtCore.Qt.Vertical) - self.splitter_2.setObjectName(_fromUtf8("splitter_2")) - self.tableWidgetSent = QtGui.QTableWidget(self.splitter_2) - self.tableWidgetSent.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers) - self.tableWidgetSent.setDragDropMode(QtGui.QAbstractItemView.DragDrop) - self.tableWidgetSent.setAlternatingRowColors(True) - self.tableWidgetSent.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection) - self.tableWidgetSent.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) - self.tableWidgetSent.setWordWrap(False) - self.tableWidgetSent.setObjectName(_fromUtf8("tableWidgetSent")) - self.tableWidgetSent.setColumnCount(4) - self.tableWidgetSent.setRowCount(0) ->>>>>>> bbb8c645afd8900b15ec35a1f1b632e095f41f6e + self.horizontalLayout_5.addWidget(self.pushButtonTTL) + self.horizontalSliderTTL = QtGui.QSlider(self.send) + self.horizontalSliderTTL.setMinimumSize(QtCore.QSize(35, 0)) + self.horizontalSliderTTL.setMaximumSize(QtCore.QSize(70, 16777215)) + self.horizontalSliderTTL.setOrientation(QtCore.Qt.Horizontal) + self.horizontalSliderTTL.setInvertedAppearance(False) + self.horizontalSliderTTL.setInvertedControls(False) + self.horizontalSliderTTL.setObjectName(_fromUtf8("horizontalSliderTTL")) + self.horizontalLayout_5.addWidget(self.horizontalSliderTTL) + self.labelHumanFriendlyTTLDescription = QtGui.QLabel(self.send) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.labelHumanFriendlyTTLDescription.sizePolicy().hasHeightForWidth()) + self.labelHumanFriendlyTTLDescription.setSizePolicy(sizePolicy) + self.labelHumanFriendlyTTLDescription.setMinimumSize(QtCore.QSize(45, 0)) + self.labelHumanFriendlyTTLDescription.setMaximumSize(QtCore.QSize(45, 16777215)) + self.labelHumanFriendlyTTLDescription.setObjectName(_fromUtf8("labelHumanFriendlyTTLDescription")) + self.horizontalLayout_5.addWidget(self.labelHumanFriendlyTTLDescription) + self.pushButtonSend = QtGui.QPushButton(self.send) + self.pushButtonSend.setMaximumSize(QtCore.QSize(16777215, 16777215)) + self.pushButtonSend.setObjectName(_fromUtf8("pushButtonSend")) + self.horizontalLayout_5.addWidget(self.pushButtonSend) + self.verticalLayout.addLayout(self.horizontalLayout_5) + self.horizontalLayout.addLayout(self.verticalLayout) + self.gridLayout_7.addLayout(self.horizontalLayout, 0, 0, 1, 1) + icon4 = QtGui.QIcon() + icon4.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/send.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.tabWidget.addTab(self.send, icon4, _fromUtf8("")) + self.subscriptions = QtGui.QWidget() + self.subscriptions.setObjectName(_fromUtf8("subscriptions")) + self.gridLayout_4 = QtGui.QGridLayout(self.subscriptions) + self.gridLayout_4.setObjectName(_fromUtf8("gridLayout_4")) + self.horizontalLayout_2 = QtGui.QHBoxLayout() + self.horizontalLayout_2.setObjectName(_fromUtf8("horizontalLayout_2")) + self.verticalLayout_3 = QtGui.QVBoxLayout() + self.verticalLayout_3.setObjectName(_fromUtf8("verticalLayout_3")) + self.treeWidgetSubscriptions = QtGui.QTreeWidget(self.subscriptions) + self.treeWidgetSubscriptions.setMaximumSize(QtCore.QSize(200, 16777215)) + self.treeWidgetSubscriptions.setAlternatingRowColors(True) + self.treeWidgetSubscriptions.setSelectionMode(QtGui.QAbstractItemView.SingleSelection) + self.treeWidgetSubscriptions.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) + self.treeWidgetSubscriptions.setObjectName(_fromUtf8("treeWidgetSubscriptions")) + icon5 = QtGui.QIcon() + icon5.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/subscriptions.png")), QtGui.QIcon.Selected, QtGui.QIcon.Off) + self.treeWidgetSubscriptions.headerItem().setIcon(0, icon5) + self.verticalLayout_3.addWidget(self.treeWidgetSubscriptions) + self.pushButtonAddSubscription = QtGui.QPushButton(self.subscriptions) + self.pushButtonAddSubscription.setMaximumSize(QtCore.QSize(200, 16777215)) + self.pushButtonAddSubscription.setObjectName(_fromUtf8("pushButtonAddSubscription")) + self.verticalLayout_3.addWidget(self.pushButtonAddSubscription) + self.horizontalLayout_2.addLayout(self.verticalLayout_3) + self.verticalLayout_4 = QtGui.QVBoxLayout() + self.verticalLayout_4.setObjectName(_fromUtf8("verticalLayout_4")) + self.inboxSearchLineSubscriptions = QtGui.QLineEdit(self.subscriptions) + self.inboxSearchLineSubscriptions.setObjectName(_fromUtf8("inboxSearchLineSubscriptions")) + self.verticalLayout_4.addWidget(self.inboxSearchLineSubscriptions) + self.tableWidgetInboxSubscriptions = QtGui.QTableWidget(self.subscriptions) + self.tableWidgetInboxSubscriptions.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers) + self.tableWidgetInboxSubscriptions.setAlternatingRowColors(True) + self.tableWidgetInboxSubscriptions.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection) + self.tableWidgetInboxSubscriptions.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) + self.tableWidgetInboxSubscriptions.setWordWrap(False) + self.tableWidgetInboxSubscriptions.setObjectName(_fromUtf8("tableWidgetInboxSubscriptions")) + self.tableWidgetInboxSubscriptions.setColumnCount(4) + self.tableWidgetInboxSubscriptions.setRowCount(0) item = QtGui.QTableWidgetItem() self.tableWidgetInboxSubscriptions.setHorizontalHeaderItem(0, item) item = QtGui.QTableWidgetItem() @@ -452,50 +344,22 @@ class Ui_MainWindow(object): self.tab_3.setObjectName(_fromUtf8("tab_3")) self.gridLayout_3 = QtGui.QGridLayout(self.tab_3) self.gridLayout_3.setObjectName(_fromUtf8("gridLayout_3")) -<<<<<<< HEAD self.horizontalLayout_4 = QtGui.QHBoxLayout() self.horizontalLayout_4.setObjectName(_fromUtf8("horizontalLayout_4")) self.verticalLayout_17 = QtGui.QVBoxLayout() self.verticalLayout_17.setObjectName(_fromUtf8("verticalLayout_17")) - self.tableWidgetYourIdentities = QtGui.QTableWidget(self.tab_3) - self.tableWidgetYourIdentities.setMaximumSize(QtCore.QSize(200, 16777215)) -======= - self.pushButtonNewAddress = QtGui.QPushButton(self.youridentities) - self.pushButtonNewAddress.setObjectName(_fromUtf8("pushButtonNewAddress")) - self.gridLayout_3.addWidget(self.pushButtonNewAddress, 0, 0, 1, 1) - spacerItem1 = QtGui.QSpacerItem(689, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) - self.gridLayout_3.addItem(spacerItem1, 0, 1, 1, 1) - self.tableWidgetYourIdentities = QtGui.QTableWidget(self.youridentities) ->>>>>>> bbb8c645afd8900b15ec35a1f1b632e095f41f6e - self.tableWidgetYourIdentities.setFrameShadow(QtGui.QFrame.Sunken) - self.tableWidgetYourIdentities.setLineWidth(1) - self.tableWidgetYourIdentities.setAlternatingRowColors(True) - self.tableWidgetYourIdentities.setSelectionMode(QtGui.QAbstractItemView.SingleSelection) - self.tableWidgetYourIdentities.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) - self.tableWidgetYourIdentities.setObjectName(_fromUtf8("tableWidgetYourIdentities")) - self.tableWidgetYourIdentities.setColumnCount(3) - self.tableWidgetYourIdentities.setRowCount(0) - item = QtGui.QTableWidgetItem() - font = QtGui.QFont() - font.setKerning(True) - item.setFont(font) - item.setIcon(icon1) - self.tableWidgetYourIdentities.setHorizontalHeaderItem(0, item) - item = QtGui.QTableWidgetItem() - self.tableWidgetYourIdentities.setHorizontalHeaderItem(1, item) - item = QtGui.QTableWidgetItem() - self.tableWidgetYourIdentities.setHorizontalHeaderItem(2, item) - self.tableWidgetYourIdentities.horizontalHeader().setCascadingSectionResizes(True) - self.tableWidgetYourIdentities.horizontalHeader().setDefaultSectionSize(200) - self.tableWidgetYourIdentities.horizontalHeader().setMinimumSectionSize(52) - self.tableWidgetYourIdentities.horizontalHeader().setSortIndicatorShown(True) - self.tableWidgetYourIdentities.horizontalHeader().setStretchLastSection(True) - self.tableWidgetYourIdentities.verticalHeader().setVisible(False) - self.tableWidgetYourIdentities.verticalHeader().setDefaultSectionSize(26) - self.tableWidgetYourIdentities.verticalHeader().setSortIndicatorShown(False) - self.tableWidgetYourIdentities.verticalHeader().setStretchLastSection(False) -<<<<<<< HEAD - self.verticalLayout_17.addWidget(self.tableWidgetYourIdentities) + self.treeWidgetChanList = QtGui.QTreeWidget(self.tab_3) + self.treeWidgetChanList.setMaximumSize(QtCore.QSize(200, 16777215)) + self.treeWidgetChanList.setFrameShadow(QtGui.QFrame.Sunken) + self.treeWidgetChanList.setLineWidth(1) + self.treeWidgetChanList.setAlternatingRowColors(True) + self.treeWidgetChanList.setSelectionMode(QtGui.QAbstractItemView.SingleSelection) + self.treeWidgetChanList.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) + self.treeWidgetChanList.setObjectName(_fromUtf8("treeWidgetChanList")) + icon7 = QtGui.QIcon() + icon7.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/can-icon-16px.png")), QtGui.QIcon.Selected, QtGui.QIcon.Off) + self.treeWidgetChanList.headerItem().setIcon(0, icon7) + self.verticalLayout_17.addWidget(self.treeWidgetChanList) self.pushButtonAddChanel = QtGui.QPushButton(self.tab_3) self.pushButtonAddChanel.setMaximumSize(QtCore.QSize(200, 16777215)) self.pushButtonAddChanel.setObjectName(_fromUtf8("pushButtonAddChanel")) @@ -519,70 +383,10 @@ class Ui_MainWindow(object): self.tableWidgetInbox_2.setObjectName(_fromUtf8("tableWidgetInbox_2")) self.tableWidgetInbox_2.setColumnCount(4) self.tableWidgetInbox_2.setRowCount(0) -======= - self.gridLayout_3.addWidget(self.tableWidgetYourIdentities, 1, 0, 1, 2) - icon4 = QtGui.QIcon() - icon4.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/identities.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) - self.tabWidget.addTab(self.youridentities, icon4, _fromUtf8("")) - self.subscriptions = QtGui.QWidget() - self.subscriptions.setObjectName(_fromUtf8("subscriptions")) - self.gridLayout_4 = QtGui.QGridLayout(self.subscriptions) - self.gridLayout_4.setObjectName(_fromUtf8("gridLayout_4")) - self.label_5 = QtGui.QLabel(self.subscriptions) - self.label_5.setWordWrap(True) - self.label_5.setObjectName(_fromUtf8("label_5")) - self.gridLayout_4.addWidget(self.label_5, 0, 0, 1, 2) - self.pushButtonAddSubscription = QtGui.QPushButton(self.subscriptions) - self.pushButtonAddSubscription.setObjectName(_fromUtf8("pushButtonAddSubscription")) - self.gridLayout_4.addWidget(self.pushButtonAddSubscription, 1, 0, 1, 1) - spacerItem2 = QtGui.QSpacerItem(689, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) - self.gridLayout_4.addItem(spacerItem2, 1, 1, 1, 1) - self.tableWidgetSubscriptions = QtGui.QTableWidget(self.subscriptions) - self.tableWidgetSubscriptions.setAlternatingRowColors(True) - self.tableWidgetSubscriptions.setSelectionMode(QtGui.QAbstractItemView.SingleSelection) - self.tableWidgetSubscriptions.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) - self.tableWidgetSubscriptions.setObjectName(_fromUtf8("tableWidgetSubscriptions")) - self.tableWidgetSubscriptions.setColumnCount(2) - self.tableWidgetSubscriptions.setRowCount(0) ->>>>>>> bbb8c645afd8900b15ec35a1f1b632e095f41f6e item = QtGui.QTableWidgetItem() self.tableWidgetInbox_2.setHorizontalHeaderItem(0, item) item = QtGui.QTableWidgetItem() -<<<<<<< HEAD self.tableWidgetInbox_2.setHorizontalHeaderItem(1, item) -======= - self.tableWidgetSubscriptions.setHorizontalHeaderItem(1, item) - self.tableWidgetSubscriptions.horizontalHeader().setCascadingSectionResizes(True) - self.tableWidgetSubscriptions.horizontalHeader().setDefaultSectionSize(400) - self.tableWidgetSubscriptions.horizontalHeader().setHighlightSections(False) - self.tableWidgetSubscriptions.horizontalHeader().setSortIndicatorShown(False) - self.tableWidgetSubscriptions.horizontalHeader().setStretchLastSection(True) - self.tableWidgetSubscriptions.verticalHeader().setVisible(False) - self.gridLayout_4.addWidget(self.tableWidgetSubscriptions, 2, 0, 1, 2) - icon5 = QtGui.QIcon() - icon5.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/subscriptions.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) - self.tabWidget.addTab(self.subscriptions, icon5, _fromUtf8("")) - self.addressbook = QtGui.QWidget() - self.addressbook.setObjectName(_fromUtf8("addressbook")) - self.gridLayout_5 = QtGui.QGridLayout(self.addressbook) - self.gridLayout_5.setObjectName(_fromUtf8("gridLayout_5")) - self.label_6 = QtGui.QLabel(self.addressbook) - self.label_6.setWordWrap(True) - self.label_6.setObjectName(_fromUtf8("label_6")) - self.gridLayout_5.addWidget(self.label_6, 0, 0, 1, 2) - self.pushButtonAddAddressBook = QtGui.QPushButton(self.addressbook) - self.pushButtonAddAddressBook.setObjectName(_fromUtf8("pushButtonAddAddressBook")) - self.gridLayout_5.addWidget(self.pushButtonAddAddressBook, 1, 0, 1, 1) - spacerItem3 = QtGui.QSpacerItem(689, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) - self.gridLayout_5.addItem(spacerItem3, 1, 1, 1, 1) - self.tableWidgetAddressBook = QtGui.QTableWidget(self.addressbook) - self.tableWidgetAddressBook.setAlternatingRowColors(True) - self.tableWidgetAddressBook.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection) - self.tableWidgetAddressBook.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) - self.tableWidgetAddressBook.setObjectName(_fromUtf8("tableWidgetAddressBook")) - self.tableWidgetAddressBook.setColumnCount(2) - self.tableWidgetAddressBook.setRowCount(0) ->>>>>>> bbb8c645afd8900b15ec35a1f1b632e095f41f6e item = QtGui.QTableWidgetItem() self.tableWidgetInbox_2.setHorizontalHeaderItem(2, item) item = QtGui.QTableWidgetItem() @@ -603,9 +407,9 @@ class Ui_MainWindow(object): self.verticalLayout_13.addWidget(self.textEditInboxMessage_2) self.horizontalLayout_4.addLayout(self.verticalLayout_13) self.gridLayout_3.addLayout(self.horizontalLayout_4, 0, 0, 1, 1) - icon7 = QtGui.QIcon() - icon7.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/can-icon-16px.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) - self.tabWidget.addTab(self.tab_3, icon7, _fromUtf8("")) + icon8 = QtGui.QIcon() + icon8.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/can-icon-16px.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.tabWidget.addTab(self.tab_3, icon8, _fromUtf8("")) self.blackwhitelist = QtGui.QWidget() self.blackwhitelist.setObjectName(_fromUtf8("blackwhitelist")) self.gridLayout_6 = QtGui.QGridLayout(self.blackwhitelist) @@ -620,13 +424,8 @@ class Ui_MainWindow(object): self.pushButtonAddBlacklist = QtGui.QPushButton(self.blackwhitelist) self.pushButtonAddBlacklist.setObjectName(_fromUtf8("pushButtonAddBlacklist")) self.gridLayout_6.addWidget(self.pushButtonAddBlacklist, 2, 0, 1, 1) -<<<<<<< HEAD spacerItem = QtGui.QSpacerItem(689, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) self.gridLayout_6.addItem(spacerItem, 2, 1, 1, 1) -======= - spacerItem4 = QtGui.QSpacerItem(689, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) - self.gridLayout_6.addItem(spacerItem4, 2, 1, 1, 1) ->>>>>>> bbb8c645afd8900b15ec35a1f1b632e095f41f6e self.tableWidgetBlacklist = QtGui.QTableWidget(self.blackwhitelist) self.tableWidgetBlacklist.setAlternatingRowColors(True) self.tableWidgetBlacklist.setSelectionMode(QtGui.QAbstractItemView.SingleSelection) @@ -645,17 +444,17 @@ class Ui_MainWindow(object): self.tableWidgetBlacklist.horizontalHeader().setStretchLastSection(True) self.tableWidgetBlacklist.verticalHeader().setVisible(False) self.gridLayout_6.addWidget(self.tableWidgetBlacklist, 3, 0, 1, 2) - icon8 = QtGui.QIcon() - icon8.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/blacklist.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) - self.tabWidget.addTab(self.blackwhitelist, icon8, _fromUtf8("")) + icon9 = QtGui.QIcon() + icon9.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/blacklist.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.tabWidget.addTab(self.blackwhitelist, icon9, _fromUtf8("")) self.networkstatus = QtGui.QWidget() self.networkstatus.setObjectName(_fromUtf8("networkstatus")) self.pushButtonStatusIcon = QtGui.QPushButton(self.networkstatus) self.pushButtonStatusIcon.setGeometry(QtCore.QRect(680, 440, 21, 23)) self.pushButtonStatusIcon.setText(_fromUtf8("")) - icon9 = QtGui.QIcon() - icon9.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/redicon.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) - self.pushButtonStatusIcon.setIcon(icon9) + icon10 = QtGui.QIcon() + icon10.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/redicon.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.pushButtonStatusIcon.setIcon(icon10) self.pushButtonStatusIcon.setFlat(True) self.pushButtonStatusIcon.setObjectName(_fromUtf8("pushButtonStatusIcon")) self.tableWidgetConnectionCount = QtGui.QTableWidget(self.networkstatus) @@ -711,9 +510,9 @@ class Ui_MainWindow(object): self.labelBytesSentCount = QtGui.QLabel(self.networkstatus) self.labelBytesSentCount.setGeometry(QtCore.QRect(350, 230, 251, 16)) self.labelBytesSentCount.setObjectName(_fromUtf8("labelBytesSentCount")) - icon10 = QtGui.QIcon() - icon10.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/networkstatus.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) - self.tabWidget.addTab(self.networkstatus, icon10, _fromUtf8("")) + icon11 = QtGui.QIcon() + icon11.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/networkstatus.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.tabWidget.addTab(self.networkstatus, icon11, _fromUtf8("")) self.gridLayout_10.addWidget(self.tabWidget, 0, 0, 1, 1) MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtGui.QMenuBar(MainWindow) @@ -779,15 +578,14 @@ class Ui_MainWindow(object): self.retranslateUi(MainWindow) self.tabWidget.setCurrentIndex(0) - self.tabWidget_2.setCurrentIndex(0) + self.tabWidgetSend.setCurrentIndex(0) QtCore.QMetaObject.connectSlotsByName(MainWindow) MainWindow.setTabOrder(self.tableWidgetInbox, self.textEditInboxMessage) MainWindow.setTabOrder(self.textEditInboxMessage, self.comboBoxSendFrom) MainWindow.setTabOrder(self.comboBoxSendFrom, self.lineEditTo) MainWindow.setTabOrder(self.lineEditTo, self.lineEditSubject) MainWindow.setTabOrder(self.lineEditSubject, self.textEditMessage) - MainWindow.setTabOrder(self.textEditMessage, self.pushButtonSend) - MainWindow.setTabOrder(self.pushButtonSend, self.pushButtonAddSubscription) + MainWindow.setTabOrder(self.textEditMessage, self.pushButtonAddSubscription) MainWindow.setTabOrder(self.pushButtonAddSubscription, self.radioButtonBlacklist) MainWindow.setTabOrder(self.radioButtonBlacklist, self.radioButtonWhitelist) MainWindow.setTabOrder(self.radioButtonWhitelist, self.pushButtonAddBlacklist) @@ -809,58 +607,36 @@ class Ui_MainWindow(object): item.setText(_translate("MainWindow", "Subject", None)) item = self.tableWidgetInbox.horizontalHeaderItem(3) item.setText(_translate("MainWindow", "Received", None)) -<<<<<<< HEAD - self.tabWidget.setTabText(self.tabWidget.indexOf(self.inbox), _translate("MainWindow", "Received", None)) + self.tabWidget.setTabText(self.tabWidget.indexOf(self.inbox), _translate("MainWindow", "Messages", None)) self.tableWidgetAddressBook.setSortingEnabled(True) item = self.tableWidgetAddressBook.horizontalHeaderItem(0) item.setText(_translate("MainWindow", "Name", None)) item = self.tableWidgetAddressBook.horizontalHeaderItem(1) item.setText(_translate("MainWindow", "Address", None)) self.pushButtonAddAddressBook.setText(_translate("MainWindow", "Add Contact", None)) - self.label.setText(_translate("MainWindow", "To:", None)) + self.pushButtonFetchNamecoinID.setText(_translate("MainWindow", "Fetch Namecoin ID", None)) self.label_3.setText(_translate("MainWindow", "Subject:", None)) -======= - self.tabWidget.setTabText(self.tabWidget.indexOf(self.inbox), _translate("MainWindow", "Inbox", None)) - self.pushButtonLoadFromAddressBook.setText(_translate("MainWindow", "Load from Address book", None)) - self.label_3.setText(_translate("MainWindow", "Subject:", None)) - self.pushButtonSend.setText(_translate("MainWindow", "Send", None)) - self.labelHumanFriendlyTTLDescription.setText(_translate("MainWindow", "X days", None)) - self.label_4.setText(_translate("MainWindow", "Message:", None)) - self.label.setText(_translate("MainWindow", "To:", None)) - self.radioButtonSpecific.setText(_translate("MainWindow", "Send to one or more specific people", None)) - self.labelSendBroadcastWarning.setText(_translate("MainWindow", "Be aware that broadcasts are only encrypted with your address. Anyone who knows your address can read them.", None)) - self.radioButtonBroadcast.setText(_translate("MainWindow", "Broadcast to everyone who is subscribed to your address", None)) - self.pushButtonTTL.setText(_translate("MainWindow", "TTL:", None)) ->>>>>>> bbb8c645afd8900b15ec35a1f1b632e095f41f6e self.label_2.setText(_translate("MainWindow", "From:", None)) + self.label.setText(_translate("MainWindow", "To:", None)) self.textEditMessage.setHtml(_translate("MainWindow", "\n" "\n" "


", None)) - self.pushButtonSend.setText(_translate("MainWindow", "Send", None)) - self.tabWidget_2.setTabText(self.tabWidget_2.indexOf(self.tab), _translate("MainWindow", "Send ordinary Message", None)) + self.tabWidgetSend.setTabText(self.tabWidgetSend.indexOf(self.tab), _translate("MainWindow", "Send ordinary Message", None)) self.label_8.setText(_translate("MainWindow", "From:", None)) self.label_7.setText(_translate("MainWindow", "Subject:", None)) - self.textEditMessage_2.setHtml(_translate("MainWindow", "\n" + self.textEditMessageBroadcast.setHtml(_translate("MainWindow", "\n" "\n" "


", None)) - self.pushButtonSend_3.setText(_translate("MainWindow", "Send", None)) - self.tabWidget_2.setTabText(self.tabWidget_2.indexOf(self.tab_2), _translate("MainWindow", "Send Message to your Subscribers", None)) -======= -"\n" -"


", None)) - self.pushButtonFetchNamecoinID.setText(_translate("MainWindow", "Fetch Namecoin ID", None)) ->>>>>>> bbb8c645afd8900b15ec35a1f1b632e095f41f6e + self.tabWidgetSend.setTabText(self.tabWidgetSend.indexOf(self.tab_2), _translate("MainWindow", "Send Message to your Subscribers", None)) + self.pushButtonTTL.setText(_translate("MainWindow", "TTL:", None)) + self.labelHumanFriendlyTTLDescription.setText(_translate("MainWindow", "X days", None)) + self.pushButtonSend.setText(_translate("MainWindow", "Send", None)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.send), _translate("MainWindow", "Send", None)) - self.tableWidgetSubscriptions.setSortingEnabled(True) - item = self.tableWidgetSubscriptions.horizontalHeaderItem(0) - item.setText(_translate("MainWindow", "Name", None)) - item = self.tableWidgetSubscriptions.horizontalHeaderItem(1) - item.setText(_translate("MainWindow", "Address", None)) + self.treeWidgetSubscriptions.headerItem().setText(0, _translate("MainWindow", "Subscriptions", None)) self.pushButtonAddSubscription.setText(_translate("MainWindow", "Add new Subscription", None)) self.inboxSearchLineSubscriptions.setPlaceholderText(_translate("MainWindow", "Search", None)) self.tableWidgetInboxSubscriptions.setSortingEnabled(True) @@ -873,13 +649,7 @@ class Ui_MainWindow(object): item = self.tableWidgetInboxSubscriptions.horizontalHeaderItem(3) item.setText(_translate("MainWindow", "Received", None)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.subscriptions), _translate("MainWindow", "Subscriptions", None)) - self.tableWidgetYourIdentities.setSortingEnabled(True) - item = self.tableWidgetYourIdentities.horizontalHeaderItem(0) - item.setText(_translate("MainWindow", "Name", None)) - item = self.tableWidgetYourIdentities.horizontalHeaderItem(1) - item.setText(_translate("MainWindow", "Address", None)) - item = self.tableWidgetYourIdentities.horizontalHeaderItem(2) - item.setText(_translate("MainWindow", "Stream", None)) + self.treeWidgetChanList.headerItem().setText(0, _translate("MainWindow", "Chans", None)) self.pushButtonAddChanel.setText(_translate("MainWindow", "Add Chanel", None)) self.inboxSearchLineEdit_2.setPlaceholderText(_translate("MainWindow", "Search", None)) self.tableWidgetInbox_2.setSortingEnabled(True) @@ -891,7 +661,7 @@ class Ui_MainWindow(object): item.setText(_translate("MainWindow", "Subject", None)) item = self.tableWidgetInbox_2.horizontalHeaderItem(3) item.setText(_translate("MainWindow", "Received", None)) - self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_3), _translate("MainWindow", "Chanels", None)) + self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_3), _translate("MainWindow", "Chans", None)) self.radioButtonBlacklist.setText(_translate("MainWindow", "Use a Blacklist (Allow all incoming messages except those on the Blacklist)", None)) self.radioButtonWhitelist.setText(_translate("MainWindow", "Use a Whitelist (Block all incoming messages except those on the Whitelist)", None)) self.pushButtonAddBlacklist.setText(_translate("MainWindow", "Add new entry", None)) diff --git a/src/bitmessageqt/bitmessageui.ui b/src/bitmessageqt/bitmessageui.ui index 8d006a89..09498ac4 100644 --- a/src/bitmessageqt/bitmessageui.ui +++ b/src/bitmessageqt/bitmessageui.ui @@ -54,7 +54,7 @@ QTabWidget::Rounded - 1 + 0 @@ -62,7 +62,7 @@ :/newPrefix/images/inbox.png:/newPrefix/images/inbox.png - Received + Messages @@ -213,7 +213,7 @@ Send - + @@ -283,280 +283,297 @@
+ + + + + 200 + 16777215 + + + + + 9 + + + + Fetch Namecoin ID + + +
- - - 0 - - - - Send ordinary Message - - - - - - - - - - Subject: - - + + + + + 0 + + + + Send ordinary Message + + + + + + + + + + Subject: + + + + + + + From: + + + + + + + + + + + + + + To: + + + + + + + + 300 + 0 + + + + + + + + - - - - From: - - - - - - - - - - - - - - To: - - - - - - - - 300 - 0 - - - - - - - - - - - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Droid Sans'; font-size:9pt; font-weight:400; font-style:normal;"> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2';"><br /></p></body></html> - - - - - - - - - - 0 - 0 - - - - - 32 - 16777215 - - - - - - - - - 0 - 0 - 255 - - - - - - - - - 0 - 0 - 255 - - - - - - - - - 120 - 120 - 120 - - - - - - - - - true - - - - TTL: - - - true - - - - - - - - 35 - 0 - - - - - 70 - 16777215 - - - - Qt::Horizontal - - - false - - - false - - - - - - - - 0 - 0 - - - - - 45 - 0 - - - - - 45 - 16777215 - - - - X days - - - - - - - - 16777215 - 16777215 - - - - Send - - - - - - Send Message to your Subscribers - - - - - - - - - - From: - - + + + + Send Message to your Subscribers + + + + + + + + + + From: + + + + + + + + + + + + + + Subject: + + + + + + + + 300 + 0 + + + + + - - - - - - - - - - - Subject: - - - - - - - - 300 - 0 - + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Droid Sans'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2';"><br /></p></body></html> - - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Droid Sans'; font-size:9pt; font-weight:400; font-style:normal;"> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2';"><br /></p></body></html> - - - - - - - Send - - - + + + + + + + + + + 0 + 0 + + + + + 32 + 16777215 + + + + + + + + + 0 + 0 + 255 + + + + + + + + + 0 + 0 + 255 + + + + + + + + + 120 + 120 + 120 + + + + + + + + + true + + + + TTL: + + + true + + + + + + + + 35 + 0 + + + + + 70 + 16777215 + + + + Qt::Horizontal + + + false + + + false + + + + + + + + 0 + 0 + + + + + 45 + 0 + + + + + 45 + 16777215 + + + + X days + + + + + + + + 16777215 + 16777215 + + + + Send + + -
- + + + + pushButtonFetchNamecoinID @@ -572,7 +589,7 @@ p, li { white-space: pre-wrap; } - + 200 @@ -588,30 +605,9 @@ p, li { white-space: pre-wrap; } QAbstractItemView::SelectRows - - true - - - true - - - 200 - - - false - - - false - - - true - - - false - - Name + Subscriptions @@ -619,11 +615,6 @@ p, li { white-space: pre-wrap; } - - - Address - - @@ -741,7 +732,7 @@ p, li { white-space: pre-wrap; } :/newPrefix/images/can-icon-16px.png:/newPrefix/images/can-icon-16px.png - Chanels + Chans @@ -749,7 +740,7 @@ p, li { white-space: pre-wrap; } - + 200 @@ -771,61 +762,16 @@ p, li { white-space: pre-wrap; } QAbstractItemView::SelectRows - - true - - - true - - - 200 - - - 52 - - - true - - - true - - - false - - - 26 - - - false - - - false - - Name - - - - true - + Chans - :/newPrefix/images/identities.png + :/newPrefix/images/can-icon-16px.png - - - Address - - - - - Stream - - From 484166882eed42b9b5137fc3535755e920942679 Mon Sep 17 00:00:00 2001 From: sbkaf Date: Mon, 23 Mar 2015 22:35:56 +0100 Subject: [PATCH 007/210] finished implementing more email client like interface --- src/bitmessagemain.py | 2 +- src/bitmessageqt/__init__.py | 894 ++++++++++++------------------- src/bitmessageqt/bitmessageui.py | 219 +++++--- src/bitmessageqt/bitmessageui.ui | 135 ++++- 4 files changed, 587 insertions(+), 663 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 4454b031..732c16c5 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -202,7 +202,7 @@ class Main: singleAPIThread.daemon = True # close the main program even if there are threads left singleAPIThread.start() - #connectToStream(1) + connectToStream(1) singleListenerThread = singleListener() singleListenerThread.setup(selfInitiatedConnections) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index acee89e4..6382d73f 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -199,7 +199,7 @@ class MyForm(QtGui.QMainWindow): QtCore.SIGNAL( "triggered()"), self.click_actionRegenerateDeterministicAddresses) - QtCore.QObject.connect(self.ui.pushButtonAddChanel, QtCore.SIGNAL( + QtCore.QObject.connect(self.ui.pushButtonAddChan, QtCore.SIGNAL( "clicked()"), self.click_actionJoinChan) # also used for creating chans. QtCore.QObject.connect(self.ui.pushButtonNewAddress, QtCore.SIGNAL( @@ -253,12 +253,27 @@ class MyForm(QtGui.QMainWindow): self.actionMarkUnread = self.ui.inboxContextMenuToolbar.addAction( _translate( "MainWindow", "Mark Unread"), self.on_action_InboxMarkUnread) + + # contextmenu messagelists self.ui.tableWidgetInbox.setContextMenuPolicy( QtCore.Qt.CustomContextMenu) if connectSignal: self.connect(self.ui.tableWidgetInbox, QtCore.SIGNAL( 'customContextMenuRequested(const QPoint&)'), self.on_context_menuInbox) + self.ui.tableWidgetInboxSubscriptions.setContextMenuPolicy( + QtCore.Qt.CustomContextMenu) + if connectSignal: + self.connect(self.ui.tableWidgetInboxSubscriptions, QtCore.SIGNAL( + 'customContextMenuRequested(const QPoint&)'), + self.on_context_menuInbox) + self.ui.tableWidgetInboxChans.setContextMenuPolicy( + QtCore.Qt.CustomContextMenu) + if connectSignal: + self.connect(self.ui.tableWidgetInboxChans, QtCore.SIGNAL( + 'customContextMenuRequested(const QPoint&)'), + self.on_context_menuInbox) + self.popMenuInbox = QtGui.QMenu(self) self.popMenuInbox.addAction(self.actionForceHtml) self.popMenuInbox.addAction(self.actionMarkUnread) @@ -312,7 +327,7 @@ class MyForm(QtGui.QMainWindow): self.popMenuYourIdentities.addAction(self.actionSpecialAddressBehaviorYourIdentities) def init_chan_popup_menu(self, connectSignal=True): - # Popup menu for the Chanels tab + # Popup menu for the Channels tab self.ui.addressContextMenuToolbar = QtGui.QToolBar() # Actions self.actionNew = self.ui.addressContextMenuToolbar.addAction(_translate( @@ -336,10 +351,10 @@ class MyForm(QtGui.QMainWindow): "MainWindow", "Special address behavior..."), self.on_action_SpecialAddressBehaviorDialog) - self.ui.treeWidgetChanList.setContextMenuPolicy( + self.ui.treeWidgetChans.setContextMenuPolicy( QtCore.Qt.CustomContextMenu) if connectSignal: - self.connect(self.ui.treeWidgetChanList, QtCore.SIGNAL( + self.connect(self.ui.treeWidgetChans, QtCore.SIGNAL( 'customContextMenuRequested(const QPoint&)'), self.on_context_menuChan) @@ -489,19 +504,53 @@ class MyForm(QtGui.QMainWindow): self.popMenuBlacklist.addAction(self.actionBlacklistDisable) self.popMenuBlacklist.addAction(self.actionBlacklistSetAvatar) + def rerenderTabTreeSubscriptions(self): + treeWidget = self.ui.treeWidgetSubscriptions + folders = ['inbox', 'trash'] + treeWidget.clear() + queryreturn = sqlQuery('SELECT label, address, enabled FROM subscriptions') + for row in queryreturn: + label, address, enabled = row + newItem = QtGui.QTreeWidgetItem(treeWidget) + newItem.setExpanded(True) + newItem.setIcon(0, avatarize(address)) + newItem.setText(0, label + ' (' + address + ')') + newItem.setData(0, Qt.UserRole, [str(address), "inbox"]) + #set text color + if enabled: + brush = QtGui.QBrush(QApplication.palette().text().color()) + else: + brush = QtGui.QBrush(QtGui.QColor(128, 128, 128)) + brush.setStyle(QtCore.Qt.NoBrush) + newItem.setForeground(0, brush) + + for folder in folders: + newSubItem = QtGui.QTreeWidgetItem(newItem) + newSubItem.setText(0, _translate("MainWindow", folder)) + newSubItem.setData(0, Qt.UserRole, [str(address), folder]) + + def rerenderTabTreeMessages(self): + self.rerenderTabTree('messages') + + def rerenderTabTreeChans(self): + self.rerenderTabTree('chan') + def rerenderTabTree(self, tab): + folders = ['inbox', 'sent', 'trash'] if tab == 'messages': treeWidget = self.ui.treeWidgetYourIdentities - folders = ['inbox', 'sent', 'trash'] - elif tab == 'subscriptions': - treeWidget = self.ui.treeWidgetSubscriptions - folders = ['inbox', 'trash'] elif tab == 'chan': - treeWidget = self.ui.treeWidgetChanList - folders = ['inbox', 'sent', 'trash'] + treeWidget = self.ui.treeWidgetChans treeWidget.clear() + # get number of (unread) messages + cntUnreadMsg = {} + queryreturn = sqlQuery('SELECT toaddress, folder, count(msgid) as cnt FROM inbox WHERE read = 0 GROUP BY toaddress, folder') + for row in queryreturn: + toaddress, folder, cnt = row + cntUnreadMsg[toaddress + folder] = cnt + configSections = shared.config.sections() for addressInKeysFile in configSections: if addressInKeysFile != 'bitmessagesettings': @@ -513,16 +562,14 @@ class MyForm(QtGui.QMainWindow): addressInKeysFile, 'mailinglist') if tab == 'messages': - if isChan or isMaillinglist: - continue - elif tab == 'subscriptions': - if not isMaillinglist: + if isChan: continue elif tab == 'chan': if not isChan: continue newItem = QtGui.QTreeWidgetItem(treeWidget) + newItem.setExpanded(True) newItem.setIcon(0, avatarize(addressInKeysFile)) newItem.setText(0, unicode( shared.config.get(addressInKeysFile, 'label'), 'utf-8)') @@ -530,7 +577,10 @@ class MyForm(QtGui.QMainWindow): newItem.setData(0, Qt.UserRole, [str(addressInKeysFile), "inbox"]) #set text color if isEnabled: - brush = QtGui.QBrush(QApplication.palette().text().color()) + if isMaillinglist: + brush = QtGui.QBrush(QtGui.QColor(137, 04, 177)) + else: + brush = QtGui.QBrush(QApplication.palette().text().color()) else: brush = QtGui.QBrush(QtGui.QColor(128, 128, 128)) brush.setStyle(QtCore.Qt.NoBrush) @@ -538,7 +588,17 @@ class MyForm(QtGui.QMainWindow): for folder in folders: newSubItem = QtGui.QTreeWidgetItem(newItem) - newSubItem.setText(0, _translate("MainWindow", folder)) + + cnt = cntUnreadMsg.get(addressInKeysFile + folder, False) + if cnt: + unreadText = " (" + str(cnt) + ")" + font = QtGui.QFont() + font.setBold(True) + newSubItem.setFont(0, font) + else: + unreadText = "" + + newSubItem.setText(0, _translate("MainWindow", folder) + unreadText) newSubItem.setData(0, Qt.UserRole, [str(addressInKeysFile), folder]) def __init__(self, parent=None): @@ -598,54 +658,18 @@ class MyForm(QtGui.QMainWindow): self.init_blacklist_popup_menu() # Initialize the user's list of addresses on the 'Chan' tab. - self.rerenderTabTree('chan') - """ - TODO remove - configSections = shared.config.sections() - for addressInKeysFile in configSections: - if addressInKeysFile != 'bitmessagesettings': - isEnabled = shared.config.getboolean( - addressInKeysFile, 'enabled') - if shared.safeConfigGetBoolean(addressInKeysFile, 'chan'): - newItem = QtGui.QTableWidgetItem(unicode( - shared.config.get(addressInKeysFile, 'label'), 'utf-8)')) - if not isEnabled: - newItem.setTextColor(QtGui.QColor(128, 128, 128)) - self.ui.tableWidgetChanList.insertRow(0) - newItem.setIcon(avatarize(addressInKeysFile)) - self.ui.tableWidgetChanList.setItem(0, 0, newItem) - newItem = QtGui.QTableWidgetItem(addressInKeysFile) - newItem.setFlags( - QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) - if shared.safeConfigGetBoolean(addressInKeysFile, 'chan'): - newItem.setTextColor(QtGui.QColor(216, 119, 0)) # orange - if not isEnabled: - newItem.setTextColor(QtGui.QColor(128, 128, 128)) - if shared.safeConfigGetBoolean(addressInKeysFile, 'mailinglist'): - newItem.setTextColor(QtGui.QColor(137, 04, 177)) # magenta - self.ui.tableWidgetChanList.setItem(0, 1, newItem) - newItem = QtGui.QTableWidgetItem(str( - decodeAddress(addressInKeysFile)[2])) - newItem.setFlags( - QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) - if not isEnabled: - newItem.setTextColor(QtGui.QColor(128, 128, 128)) - self.ui.tableWidgetChanList.setItem(0, 2, newItem) - if isEnabled: - status, addressVersionNumber, streamNumber, hash = decodeAddress( - addressInKeysFile) - """ + self.rerenderTabTreeChans() # Initialize the user's list of addresses on the 'Messages' tab. - self.rerenderTabTree('messages') + self.rerenderTabTreeMessages() # Set welcome message self.ui.textEditInboxMessage.setText( """ Welcome to easy and secure Bitmessage - * send messages like e-mails + * send messages to other people * send broadcast messages like twitter or - * discuss in chan(el)s with other people + * discuss in chan(nel)s with other people """ ) @@ -658,6 +682,10 @@ class MyForm(QtGui.QMainWindow): # Initialize the inbox search QtCore.QObject.connect(self.ui.inboxSearchLineEdit, QtCore.SIGNAL( "returnPressed()"), self.inboxSearchLineEditPressed) + QtCore.QObject.connect(self.ui.inboxSearchLineEditSubscriptions, QtCore.SIGNAL( + "returnPressed()"), self.inboxSearchLineEditPressed) + QtCore.QObject.connect(self.ui.inboxSearchLineEditChans, QtCore.SIGNAL( + "returnPressed()"), self.inboxSearchLineEditPressed) # Initialize the Blacklist or Whitelist if shared.config.get('bitmessagesettings', 'blackwhitelist') == 'white': @@ -665,24 +693,25 @@ class MyForm(QtGui.QMainWindow): self.ui.radioButtonWhitelist.click() self.rerenderBlackWhiteList() - # Initialize addresslists + # Initialize addressbook QtCore.QObject.connect(self.ui.tableWidgetAddressBook, QtCore.SIGNAL( "itemChanged(QTableWidgetItem *)"), self.tableWidgetAddressBookItemChanged) - """ - TODO implement - QtCore.QObject.connect(self.ui.treeWidgetSubscriptions, QtCore.SIGNAL( - "itemChanged(QTableWidgetItem *)"), self.treeWidgetSubscriptionsItemChanged) - QtCore.QObject.connect(self.ui.treeWidgetChanList, QtCore.SIGNAL( - "itemChanged(QTableWidgetItem *)"), self.treeWidgetChanListItemChanged) - """ + + # show messages from message list QtCore.QObject.connect(self.ui.tableWidgetInbox, QtCore.SIGNAL( "itemSelectionChanged ()"), self.tableWidgetInboxItemClicked) + QtCore.QObject.connect(self.ui.tableWidgetInboxSubscriptions, QtCore.SIGNAL( + "itemSelectionChanged ()"), self.tableWidgetInboxItemClicked) + QtCore.QObject.connect(self.ui.tableWidgetInboxChans, QtCore.SIGNAL( + "itemSelectionChanged ()"), self.tableWidgetInboxItemClicked) + + # tree address lists QtCore.QObject.connect(self.ui.treeWidgetYourIdentities, QtCore.SIGNAL( - "itemSelectionChanged ()"), self.treeWidgetYourIdentitiesItemClicked) + "itemSelectionChanged ()"), self.treeWidgetItemClicked) QtCore.QObject.connect(self.ui.treeWidgetSubscriptions, QtCore.SIGNAL( - "itemSelectionChanged ()"), self.treeWidgetSubscribtionsItemClicked) - QtCore.QObject.connect(self.ui.treeWidgetChanList, QtCore.SIGNAL( - "itemSelectionChanged ()"), self.treeWidgetChanListItemClicked) + "itemSelectionChanged ()"), self.treeWidgetItemClicked) + QtCore.QObject.connect(self.ui.treeWidgetChans, QtCore.SIGNAL( + "itemSelectionChanged ()"), self.treeWidgetItemClicked) # Put the colored icon on the status bar # self.ui.pushButtonStatusIcon.setIcon(QIcon(":/newPrefix/images/yellowicon.png")) @@ -697,7 +726,7 @@ class MyForm(QtGui.QMainWindow): # Set the icon sizes for the identicons identicon_size = 3*7 self.ui.tableWidgetInbox.setIconSize(QtCore.QSize(identicon_size, identicon_size)) - self.ui.treeWidgetChanList.setIconSize(QtCore.QSize(identicon_size, identicon_size)) + self.ui.treeWidgetChans.setIconSize(QtCore.QSize(identicon_size, identicon_size)) self.ui.treeWidgetYourIdentities.setIconSize(QtCore.QSize(identicon_size, identicon_size)) self.ui.treeWidgetSubscriptions.setIconSize(QtCore.QSize(identicon_size, identicon_size)) self.ui.tableWidgetAddressBook.setIconSize(QtCore.QSize(identicon_size, identicon_size)) @@ -883,35 +912,35 @@ class MyForm(QtGui.QMainWindow): self.appIndicatorShow() self.ui.tabWidget.setCurrentIndex(2) - # Show the program window and select chanels tab - def appIndicatorChanel(self): + # Show the program window and select channels tab + def appIndicatorChannel(self): self.appIndicatorShow() self.ui.tabWidget.setCurrentIndex(3) # Load Sent items from database - def loadSent(self, where="", what=""): + def loadSent(self, tableWidget, account, where="", what=""): what = "%" + what + "%" - if where == "To": + if where == _translate("MainWindow", "To"): where = "toaddress" - elif where == "From": + elif where == _translate("MainWindow", "From"): where = "fromaddress" - elif where == "Subject": + elif where == _translate("MainWindow", "Subject"): where = "subject" - elif where == "Message": + elif where == _translate("MainWindow", "Message"): where = "message" else: where = "toaddress || fromaddress || subject || message" sqlStatement = ''' SELECT toaddress, fromaddress, subject, status, ackdata, lastactiontime - FROM sent WHERE folder="sent" AND %s LIKE ? + FROM sent WHERE fromaddress=? AND folder="sent" AND %s LIKE ? ORDER BY lastactiontime ''' % (where,) - while self.ui.tableWidgetInbox.rowCount() > 0: - self.ui.tableWidgetInbox.removeRow(0) + while tableWidget.rowCount() > 0: + tableWidget.removeRow(0) - queryreturn = sqlQuery(sqlStatement, what) + queryreturn = sqlQuery(sqlStatement, account, what) for row in queryreturn: toAddress, fromAddress, subject, status, ackdata, lastactiontime = row subject = shared.fixPotentiallyInvalidUTF8Data(subject) @@ -943,14 +972,14 @@ class MyForm(QtGui.QMainWindow): if toLabel == '': toLabel = toAddress - self.ui.tableWidgetInbox.insertRow(0) + tableWidget.insertRow(0) toAddressItem = QtGui.QTableWidgetItem(unicode(toLabel, 'utf-8')) toAddressItem.setToolTip(unicode(toLabel, 'utf-8')) toAddressItem.setIcon(avatarize(toAddress)) toAddressItem.setData(Qt.UserRole, str(toAddress)) toAddressItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) - self.ui.tableWidgetInbox.setItem(0, 0, toAddressItem) + tableWidget.setItem(0, 0, toAddressItem) if fromLabel == '': fromLabel = fromAddress @@ -960,13 +989,13 @@ class MyForm(QtGui.QMainWindow): fromAddressItem.setData(Qt.UserRole, str(fromAddress)) fromAddressItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) - self.ui.tableWidgetInbox.setItem(0, 1, fromAddressItem) + tableWidget.setItem(0, 1, fromAddressItem) subjectItem = QtGui.QTableWidgetItem(unicode(subject, 'utf-8')) subjectItem.setToolTip(unicode(subject, 'utf-8')) subjectItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) - self.ui.tableWidgetInbox.setItem(0, 2, subjectItem) + tableWidget.setItem(0, 2, subjectItem) if status == 'awaitingpubkey': statusText = _translate( @@ -1013,34 +1042,33 @@ class MyForm(QtGui.QMainWindow): newItem.setData(33, int(lastactiontime)) newItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) - self.ui.tableWidgetInbox.setItem(0, 3, newItem) - self.ui.tableWidgetInbox.sortItems(3, Qt.DescendingOrder) - self.ui.tableWidgetInbox.keyPressEvent = self.tableWidgetInboxKeyPressEvent + tableWidget.setItem(0, 3, newItem) + tableWidget.sortItems(3, Qt.DescendingOrder) + tableWidget.keyPressEvent = self.tableWidgetInboxKeyPressEvent - # Load inbox from messages database file + # Load messages from database file def loadMessagelist(self, tableWidget, account, folder="inbox", where="", what=""): + if folder == 'sent': + self.loadSent(tableWidget, account, where, what) + return + what = "%" + what + "%" - if where == "To": + if where == _translate("MainWindow", "To"): where = "toaddress" - elif where == "From": + elif where == _translate("MainWindow", "From"): where = "fromaddress" - elif where == "Subject": + elif where == _translate("MainWindow", "Subject"): where = "subject" - elif where == "Message": + elif where == _translate("MainWindow", "Message"): where = "message" else: where = "toaddress || fromaddress || subject || message" - if folder == "sent": - accounttype = "fromaddress" - else: - accounttype = "toaddress" - sqlStatement = ''' SELECT msgid, toaddress, fromaddress, subject, received, read - FROM inbox WHERE %s=? AND folder=? AND %s LIKE ? + FROM inbox WHERE toaddress=? AND folder=? AND %s LIKE ? ORDER BY received - ''' % (accounttype, where) + ''' % (where) while tableWidget.rowCount() > 0: tableWidget.removeRow(0) @@ -1131,115 +1159,6 @@ class MyForm(QtGui.QMainWindow): tableWidget.sortItems(3, Qt.DescendingOrder) tableWidget.keyPressEvent = self.tableWidgetInboxKeyPressEvent - # Load inbox from messages database file - def loadInbox(self, where="", what=""): - what = "%" + what + "%" - if where == "To": - where = "toaddress" - elif where == "From": - where = "fromaddress" - elif where == "Subject": - where = "subject" - elif where == "Message": - where = "message" - else: - where = "toaddress || fromaddress || subject || message" - - sqlStatement = ''' - SELECT msgid, toaddress, fromaddress, subject, received, read - FROM inbox WHERE folder="inbox" AND %s LIKE ? - ORDER BY received - ''' % (where,) - - while self.ui.tableWidgetInbox.rowCount() > 0: - self.ui.tableWidgetInbox.removeRow(0) - - font = QFont() - font.setBold(True) - queryreturn = sqlQuery(sqlStatement, what) - for row in queryreturn: - msgid, toAddress, fromAddress, subject, received, read = row - subject = shared.fixPotentiallyInvalidUTF8Data(subject) - try: - if toAddress == self.str_broadcast_subscribers: - toLabel = self.str_broadcast_subscribers - else: - toLabel = shared.config.get(toAddress, 'label') - except: - toLabel = '' - if toLabel == '': - toLabel = toAddress - - fromLabel = '' - if shared.config.has_section(fromAddress): - fromLabel = shared.config.get(fromAddress, 'label') - - if fromLabel == '': # If the fromAddress isn't one of our addresses and isn't a chan - queryreturn = sqlQuery( - '''select label from addressbook where address=?''', fromAddress) - if queryreturn != []: - for row in queryreturn: - fromLabel, = row - - if fromLabel == '': # If this address wasn't in our address book... - queryreturn = sqlQuery( - '''select label from subscriptions where address=?''', fromAddress) - if queryreturn != []: - for row in queryreturn: - fromLabel, = row - if fromLabel == '': - fromLabel = fromAddress - - # message row - self.ui.tableWidgetInbox.insertRow(0) - # to - to_item = QtGui.QTableWidgetItem(unicode(toLabel, 'utf-8')) - to_item.setToolTip(unicode(toLabel, 'utf-8')) - to_item.setFlags( - QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) - if not read: - to_item.setFont(font) - to_item.setData(Qt.UserRole, str(toAddress)) - if shared.safeConfigGetBoolean(toAddress, 'mailinglist'): - to_item.setTextColor(QtGui.QColor(137, 04, 177)) # magenta - if shared.safeConfigGetBoolean(str(toAddress), 'chan'): - to_item.setTextColor(QtGui.QColor(216, 119, 0)) # orange - to_item.setIcon(avatarize(toAddress)) - self.ui.tableWidgetInbox.setItem(0, 0, to_item) - # from - from_item = QtGui.QTableWidgetItem(unicode(fromLabel, 'utf-8')) - from_item.setToolTip(unicode(fromLabel, 'utf-8')) - from_item.setFlags( - QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) - if not read: - from_item.setFont(font) - from_item.setData(Qt.UserRole, str(fromAddress)) - if shared.safeConfigGetBoolean(str(fromAddress), 'chan'): - from_item.setTextColor(QtGui.QColor(216, 119, 0)) # orange - from_item.setIcon(avatarize(fromAddress)) - self.ui.tableWidgetInbox.setItem(0, 1, from_item) - # subject - subject_item = QtGui.QTableWidgetItem(unicode(subject, 'utf-8')) - subject_item.setToolTip(unicode(subject, 'utf-8')) - subject_item.setFlags( - QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) - if not read: - subject_item.setFont(font) - self.ui.tableWidgetInbox.setItem(0, 2, subject_item) - # time received - time_item = myTableWidgetItem(l10n.formatTimestamp(received)) - time_item.setToolTip(l10n.formatTimestamp(received)) - time_item.setData(Qt.UserRole, QByteArray(msgid)) - time_item.setData(33, int(received)) - time_item.setFlags( - QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) - if not read: - time_item.setFont(font) - self.ui.tableWidgetInbox.setItem(0, 3, time_item) - - self.ui.tableWidgetInbox.sortItems(3, Qt.DescendingOrder) - self.ui.tableWidgetInbox.keyPressEvent = self.tableWidgetInboxKeyPressEvent - # create application indicator def appIndicatorInit(self, app): self.initTrayIcon("can-icon-24px-red.png", app) @@ -1279,10 +1198,10 @@ class MyForm(QtGui.QMainWindow): actionSubscribe.triggered.connect(self.appIndicatorSubscribe) m.addAction(actionSubscribe) - # Chanels + # Channels actionSubscribe = QtGui.QAction(_translate( - "MainWindow", "Chanel"), m, checkable=False) - actionSubscribe.triggered.connect(self.appIndicatorChanel) + "MainWindow", "Channel"), m, checkable=False) + actionSubscribe.triggered.connect(self.appIndicatorChannel) m.addAction(actionSubscribe) # separator @@ -2070,26 +1989,7 @@ class MyForm(QtGui.QMainWindow): self.ui.tableWidgetAddressBook.setItem(0, 1, newItem) def rerenderSubscriptions(self): - self.rerenderTabTree('subscriptions') - """ - TODO remove - self.ui.tableWidgetSubscriptions.setRowCount(0) - queryreturn = sqlQuery('SELECT label, address, enabled FROM subscriptions') - for row in queryreturn: - label, address, enabled = row - self.ui.tableWidgetSubscriptions.insertRow(0) - newItem = QtGui.QTableWidgetItem(unicode(label, 'utf-8')) - if not enabled: - newItem.setTextColor(QtGui.QColor(128, 128, 128)) - newItem.setIcon(avatarize(address)) - self.ui.tableWidgetSubscriptions.setItem(0, 0, newItem) - newItem = QtGui.QTableWidgetItem(address) - newItem.setFlags( - QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) - if not enabled: - newItem.setTextColor(QtGui.QColor(128, 128, 128)) - self.ui.tableWidgetSubscriptions.setItem(0, 1, newItem) - """ + self.rerenderTabTreeSubscriptions() def rerenderBlackWhiteList(self): self.ui.tableWidgetBlacklist.setRowCount(0) @@ -2124,13 +2024,21 @@ more work your computer must do to send the message. A Time-To-Live of four or f self.statusBar().showMessage('') if self.ui.tabWidgetSend.currentIndex() == 0: + # message to specific people sendMessageToPeople = True + fromAddress = self.ui.comboBoxSendFrom.itemData( + self.ui.comboBoxSendFrom.currentIndex(), + Qt.UserRole).toString() toAddresses = str(self.ui.lineEditTo.text()) subject = str(self.ui.lineEditSubject.text().toUtf8()) message = str( self.ui.textEditMessage.document().toPlainText().toUtf8()) else: + # broadcast message sendMessageToPeople = False + fromAddress = self.ui.comboBoxSendFromBroadcast.itemData( + self.ui.comboBoxSendFromBroadcast.currentIndex(), + Qt.UserRole).toString() subject = str(self.ui.lineEditSubjectBroadcast.text().toUtf8()) message = str( self.ui.textEditMessageBroadcast.document().toPlainText().toUtf8()) @@ -2146,11 +2054,6 @@ more work your computer must do to send the message. A Time-To-Live of four or f "MainWindow", "The message that you are trying to send is too long by %1 bytes. (The maximum is 261644 bytes). Please cut it down before sending.").arg(len(message) - (2 ** 18 - 500))) return - if toAddresses: - print toAddresses - print subject - print message - return if sendMessageToPeople: # To send a message to specific people (rather than broadcast) toAddressesList = [s.strip() for s in toAddresses.replace(',', ';').split(';')] @@ -2510,25 +2413,12 @@ more work your computer must do to send the message. A Time-To-Live of four or f #This should be handled outside of this function, for error displaying and such, but it must also be checked here. if shared.isAddressInMySubscriptionsList(address): return - """ - #Add to UI list - TODO remove - self.ui.tableWidgetSubscriptions.setSortingEnabled(False) - self.ui.tableWidgetSubscriptions.insertRow(0) - newItem = QtGui.QTableWidgetItem(unicode(label, 'utf-8')) - newItem.setIcon(avatarize(address)) - self.ui.tableWidgetSubscriptions.setItem(0,0,newItem) - newItem = QtGui.QTableWidgetItem(address) - newItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled ) - self.ui.tableWidgetSubscriptions.setItem(0,1,newItem) - self.ui.tableWidgetSubscriptions.setSortingEnabled(True) - """ #Add to database (perhaps this should be separated from the MyForm class) sqlExecute('''INSERT INTO subscriptions VALUES (?,?,?)''',str(label),address,True) self.rerenderInboxFromLabels() shared.reloadBroadcastSendersForWhichImWatching() - self.rerenderTabTree('subscriptions') + self.rerenderTabTreeSubscriptions() def click_pushButtonAddSubscription(self): self.NewSubscriptionDialogInstance = NewSubscriptionDialog(self) @@ -2860,38 +2750,6 @@ more work your computer must do to send the message. A Time-To-Live of four or f shared.writeKeysFile() self.rerenderInboxToLabels() - """ - TODO remove - def on_action_ChanSpecialAddressBehaviorDialog(self): - self.dialog = SpecialAddressBehaviorDialog(self) - # For Modal dialogs - if self.dialog.exec_(): - currentRow = self.ui.tableWidgetChanList.currentRow() - addressAtCurrentRow = str( - self.ui.tableWidgetChanList.item(currentRow, 1).text()) - if shared.safeConfigGetBoolean(addressAtCurrentRow, 'chan'): - return - if self.dialog.ui.radioButtonBehaveNormalAddress.isChecked(): - shared.config.set(str( - addressAtCurrentRow), 'mailinglist', 'false') - # Set the color to either black or grey - if shared.config.getboolean(addressAtCurrentRow, 'enabled'): - self.ui.tableWidgetChanList.item( - currentRow, 1).setTextColor(QApplication.palette() - .text().color()) - else: - self.ui.tableWidgetChanList.item( - currentRow, 1).setTextColor(QtGui.QColor(128, 128, 128)) - else: - shared.config.set(str( - addressAtCurrentRow), 'mailinglist', 'true') - shared.config.set(str(addressAtCurrentRow), 'mailinglistname', str( - self.dialog.ui.lineEditMailingListName.text().toUtf8())) - self.ui.tableWidgetChanList.item(currentRow, 1).setTextColor(QtGui.QColor(137, 04, 177)) # magenta - shared.writeKeysFile() - self.rerenderInboxToLabels() - """ - def click_NewAddressDialog(self): self.dialog = NewAddressDialog(self) # For Modal dialogs @@ -2959,10 +2817,10 @@ more work your computer must do to send the message. A Time-To-Live of four or f self.quit() def on_action_InboxMessageForceHtml(self): - currentInboxRow = self.ui.tableWidgetInbox.currentRow() - - msgid = str(self.ui.tableWidgetInbox.item( - currentInboxRow, 3).data(Qt.UserRole).toPyObject()) + msgid = self.getCurrentMessageId() + textEdit = self.getCurrentMessageTextedit() + if not msgid: + return queryreturn = sqlQuery( '''select message from inbox where msgid=?''', msgid) if queryreturn != []: @@ -2983,29 +2841,32 @@ more work your computer must do to send the message. A Time-To-Live of four or f content = ' '.join(lines) # To keep the whitespace between lines content = shared.fixPotentiallyInvalidUTF8Data(content) content = unicode(content, 'utf-8)') - self.ui.textEditInboxMessage.setHtml(QtCore.QString(content)) + textEdit.setHtml(QtCore.QString(content)) def on_action_InboxMarkUnread(self): + tableWidget = self.getCurrentMessagelist() + if not tableWidget: + return font = QFont() font.setBold(True) inventoryHashesToMarkUnread = [] - for row in self.ui.tableWidgetInbox.selectedIndexes(): + for row in tableWidget.selectedIndexes(): currentRow = row.row() - inventoryHashToMarkUnread = str(self.ui.tableWidgetInbox.item( + inventoryHashToMarkUnread = str(tableWidget.item( currentRow, 3).data(Qt.UserRole).toPyObject()) inventoryHashesToMarkUnread.append(inventoryHashToMarkUnread) - self.ui.tableWidgetInbox.item(currentRow, 0).setFont(font) - self.ui.tableWidgetInbox.item(currentRow, 1).setFont(font) - self.ui.tableWidgetInbox.item(currentRow, 2).setFont(font) - self.ui.tableWidgetInbox.item(currentRow, 3).setFont(font) + tableWidget.item(currentRow, 0).setFont(font) + tableWidget.item(currentRow, 1).setFont(font) + tableWidget.item(currentRow, 2).setFont(font) + tableWidget.item(currentRow, 3).setFont(font) #sqlite requires the exact number of ?s to prevent injection sqlExecute('''UPDATE inbox SET read=0 WHERE msgid IN (%s)''' % ( "?," * len(inventoryHashesToMarkUnread))[:-1], *inventoryHashesToMarkUnread) self.changedInboxUnread() - # self.ui.tableWidgetInbox.selectRow(currentRow + 1) + # tableWidget.selectRow(currentRow + 1) # This doesn't de-select the last message if you try to mark it unread, but that doesn't interfere. Might not be necessary. # We could also select upwards, but then our problem would be with the topmost message. - # self.ui.tableWidgetInbox.clearSelection() manages to mark the message as read again. + # tableWidget.clearSelection() manages to mark the message as read again. # Format predefined text on message reply. def quoted_text(self, message): @@ -3030,12 +2891,15 @@ more work your computer must do to send the message. A Time-To-Live of four or f return '\n'.join([quote_line(l) for l in message.splitlines()]) + '\n\n' def on_action_InboxReply(self): - currentInboxRow = self.ui.tableWidgetInbox.currentRow() - toAddressAtCurrentInboxRow = str(self.ui.tableWidgetInbox.item( + tableWidget = self.getCurrentMessagelist() + if not tableWidget: + return + currentInboxRow = tableWidget.currentRow() + toAddressAtCurrentInboxRow = str(tableWidget.item( currentInboxRow, 0).data(Qt.UserRole).toPyObject()) - fromAddressAtCurrentInboxRow = str(self.ui.tableWidgetInbox.item( + fromAddressAtCurrentInboxRow = str(tableWidget.item( currentInboxRow, 1).data(Qt.UserRole).toPyObject()) - msgid = str(self.ui.tableWidgetInbox.item( + msgid = str(tableWidget.item( currentInboxRow, 3).data(Qt.UserRole).toPyObject()) queryreturn = sqlQuery( '''select message from inbox where msgid=?''', msgid) @@ -3071,19 +2935,22 @@ more work your computer must do to send the message. A Time-To-Live of four or f quotedText = self.quoted_text(unicode(messageAtCurrentInboxRow, 'utf-8')) self.ui.textEditMessage.setText(quotedText) - if self.ui.tableWidgetInbox.item(currentInboxRow, 2).text()[0:3] in ['Re:', 'RE:']: + if tableWidget.item(currentInboxRow, 2).text()[0:3] in ['Re:', 'RE:']: self.ui.lineEditSubject.setText( - self.ui.tableWidgetInbox.item(currentInboxRow, 2).text()) + tableWidget.item(currentInboxRow, 2).text()) else: self.ui.lineEditSubject.setText( - 'Re: ' + self.ui.tableWidgetInbox.item(currentInboxRow, 2).text()) + 'Re: ' + tableWidget.item(currentInboxRow, 2).text()) self.ui.tabWidgetSend.setCurrentIndex(0) self.ui.tabWidget.setCurrentIndex(1) def on_action_InboxAddSenderToAddressBook(self): - currentInboxRow = self.ui.tableWidgetInbox.currentRow() - # self.ui.tableWidgetInbox.item(currentRow,1).data(Qt.UserRole).toPyObject() - addressAtCurrentInboxRow = str(self.ui.tableWidgetInbox.item( + tableWidget = self.getCurrentMessagelist() + if not tableWidget: + return + currentInboxRow = tableWidget.currentRow() + # tableWidget.item(currentRow,1).data(Qt.UserRole).toPyObject() + addressAtCurrentInboxRow = str(tableWidget.item( currentInboxRow, 1).data(Qt.UserRole).toPyObject()) # Let's make sure that it isn't already in the address book queryreturn = sqlQuery('''select * from addressbook where address=?''', @@ -3101,7 +2968,7 @@ more work your computer must do to send the message. A Time-To-Live of four or f sqlExecute('''INSERT INTO addressbook VALUES (?,?)''', '--New entry. Change label in Address Book.--', addressAtCurrentInboxRow) - self.ui.tabWidget.setCurrentIndex(5) + self.ui.tabWidget.setCurrentIndex(1) self.ui.tableWidgetAddressBook.setCurrentCell(0, 0) self.statusBar().showMessage(_translate( "MainWindow", "Entry added to the Address Book. Edit the label to your liking.")) @@ -3111,29 +2978,35 @@ more work your computer must do to send the message. A Time-To-Live of four or f # Send item on the Inbox tab to trash def on_action_InboxTrash(self): - while self.ui.tableWidgetInbox.selectedIndexes() != []: - currentRow = self.ui.tableWidgetInbox.selectedIndexes()[0].row() - inventoryHashToTrash = str(self.ui.tableWidgetInbox.item( + tableWidget = self.getCurrentMessagelist() + if not tableWidget: + return + while tableWidget.selectedIndexes() != []: + currentRow = tableWidget.selectedIndexes()[0].row() + inventoryHashToTrash = str(tableWidget.item( currentRow, 3).data(Qt.UserRole).toPyObject()) sqlExecute('''UPDATE inbox SET folder='trash' WHERE msgid=?''', inventoryHashToTrash) self.ui.textEditInboxMessage.setText("") - self.ui.tableWidgetInbox.removeRow(currentRow) + tableWidget.removeRow(currentRow) self.statusBar().showMessage(_translate( "MainWindow", "Moved items to trash. There is no user interface to view your trash, but it is still on disk if you are desperate to get it back.")) if currentRow == 0: - self.ui.tableWidgetInbox.selectRow(currentRow) + tableWidget.selectRow(currentRow) else: - self.ui.tableWidgetInbox.selectRow(currentRow - 1) + tableWidget.selectRow(currentRow - 1) def on_action_InboxSaveMessageAs(self): - currentInboxRow = self.ui.tableWidgetInbox.currentRow() + tableWidget = self.getCurrentMessagelist() + if not tableWidget: + return + currentInboxRow = tableWidget.currentRow() try: - subjectAtCurrentInboxRow = str(self.ui.tableWidgetInbox.item(currentInboxRow,2).text()) + subjectAtCurrentInboxRow = str(tableWidget.item(currentInboxRow,2).text()) except: subjectAtCurrentInboxRow = '' # Retrieve the message data out of the SQL database - msgid = str(self.ui.tableWidgetInbox.item( + msgid = str(tableWidget.item( currentInboxRow, 3).data(Qt.UserRole).toPyObject()) queryreturn = sqlQuery( '''select message from inbox where msgid=?''', msgid) @@ -3268,73 +3141,37 @@ more work your computer must do to send the message. A Time-To-Live of four or f self.click_pushButtonAddSubscription() def on_action_SubscriptionsDelete(self): - print 'clicked Delete' - """ - TODO implement - currentRow = self.ui.tableWidgetSubscriptions.currentRow() - labelAtCurrentRow = self.ui.tableWidgetSubscriptions.item( - currentRow, 0).text().toUtf8() - addressAtCurrentRow = self.ui.tableWidgetSubscriptions.item( - currentRow, 1).text() - sqlExecute('''DELETE FROM subscriptions WHERE label=? AND address=?''', - str(labelAtCurrentRow), str(addressAtCurrentRow)) - self.ui.tableWidgetSubscriptions.removeRow(currentRow) + address = self.getCurrentAccount() + sqlExecute('''DELETE FROM subscriptions WHERE address=?''', + address) + self.rerenderTabTreeSubscriptions() self.rerenderInboxFromLabels() shared.reloadBroadcastSendersForWhichImWatching() - """ def on_action_SubscriptionsClipboard(self): - """ - TODO implement - currentRow = self.ui.tableWidgetSubscriptions.currentRow() - addressAtCurrentRow = self.ui.tableWidgetSubscriptions.item( - currentRow, 1).text() + address = self.getCurrentAccount() clipboard = QtGui.QApplication.clipboard() - clipboard.setText(str(addressAtCurrentRow)) - """ + clipboard.setText(str(address)) def on_action_SubscriptionsEnable(self): - """ - TODO implement - currentRow = self.ui.tableWidgetSubscriptions.currentRow() - labelAtCurrentRow = self.ui.tableWidgetSubscriptions.item( - currentRow, 0).text().toUtf8() - addressAtCurrentRow = self.ui.tableWidgetSubscriptions.item( - currentRow, 1).text() + address = self.getCurrentAccount() sqlExecute( - '''update subscriptions set enabled=1 WHERE label=? AND address=?''', - str(labelAtCurrentRow), str(addressAtCurrentRow)) - self.ui.tableWidgetSubscriptions.item( - currentRow, 0).setTextColor(QApplication.palette().text().color()) - self.ui.tableWidgetSubscriptions.item( - currentRow, 1).setTextColor(QApplication.palette().text().color()) + '''update subscriptions set enabled=1 WHERE address=?''', + address) + self.setCurrentItemColor(QApplication.palette().text().color()) shared.reloadBroadcastSendersForWhichImWatching() - """ def on_action_SubscriptionsDisable(self): - """ - TODO implement - currentRow = self.ui.tableWidgetSubscriptions.currentRow() - labelAtCurrentRow = self.ui.tableWidgetSubscriptions.item( - currentRow, 0).text().toUtf8() - addressAtCurrentRow = self.ui.tableWidgetSubscriptions.item( - currentRow, 1).text() + address = self.getCurrentAccount() sqlExecute( - '''update subscriptions set enabled=0 WHERE label=? AND address=?''', - str(labelAtCurrentRow), str(addressAtCurrentRow)) - self.ui.tableWidgetSubscriptions.item( - currentRow, 0).setTextColor(QtGui.QColor(128, 128, 128)) - self.ui.tableWidgetSubscriptions.item( - currentRow, 1).setTextColor(QtGui.QColor(128, 128, 128)) + '''update subscriptions set enabled=0 WHERE address=?''', + address) + self.setCurrentItemColor(QtGui.QColor(128, 128, 128)) shared.reloadBroadcastSendersForWhichImWatching() - """ def on_context_menuSubscriptions(self, point): - """ - TODO implement self.popMenuSubscriptions.exec_( - self.ui.tableWidgetSubscriptions.mapToGlobal(point)) - """ + self.ui.treeWidgetSubscriptions.mapToGlobal(point)) # Group of functions for the Blacklist dialog box def on_action_BlacklistNew(self): @@ -3412,6 +3249,68 @@ more work your computer must do to send the message. A Time-To-Live of four or f else: return False + def getCurrentMessagelist(self): + currentIndex = self.ui.tabWidget.currentIndex(); + messagelistList = [ + self.ui.tableWidgetInbox, + False, + self.ui.tableWidgetInboxSubscriptions, + self.ui.tableWidgetInboxChans, + ] + if currentIndex >= 0 and currentIndex < len(messagelistList): + return messagelistList[currentIndex] + else: + return False + + def getCurrentMessageId(self): + messagelist = self.getCurrentMessagelist() + if messagelist: + currentRow = messagelist.currentRow() + if currentRow >= 0: + msgid = str(messagelist.item( + currentRow, 3).data(Qt.UserRole).toPyObject()) # data is saved at the 4. column of the table... + return msgid + return False + + def getCurrentMessageTextedit(self): + currentIndex = self.ui.tabWidget.currentIndex(); + messagelistList = [ + self.ui.textEditInboxMessage, + False, + self.ui.textEditInboxMessageSubscriptions, + self.ui.textEditInboxMessageChans, + ] + if currentIndex >= 0 and currentIndex < len(messagelistList): + return messagelistList[currentIndex] + else: + return False + + def getCurrentSearchLine(self): + currentIndex = self.ui.tabWidget.currentIndex(); + messagelistList = [ + self.ui.inboxSearchLineEdit, + False, + self.ui.inboxSearchLineEditSubscriptions, + self.ui.inboxSearchLineEditChans, + ] + if currentIndex >= 0 and currentIndex < len(messagelistList): + return messagelistList[currentIndex] + else: + return False + + def getCurrentSearchOption(self): + currentIndex = self.ui.tabWidget.currentIndex(); + messagelistList = [ + self.ui.inboxSearchOption, + False, + self.ui.inboxSearchOptionSubscriptions, + self.ui.inboxSearchOptionChans, + ] + if currentIndex >= 0 and currentIndex < len(messagelistList): + return messagelistList[currentIndex].currentText().toUtf8().data() + else: + return False + # Group of functions for the Your Identities dialog box def getCurrentAccount(self): treeWidget = self.getCurrentTreeWidget() @@ -3442,6 +3341,7 @@ more work your computer must do to send the message. A Time-To-Live of four or f if treeWidget: brush = QtGui.QBrush() brush.setStyle(QtCore.Qt.NoBrush) + brush.setColor(color) currentItem = treeWidget.currentItem() currentItem.setForeground(0, brush) @@ -3453,25 +3353,6 @@ more work your computer must do to send the message. A Time-To-Live of four or f self.enableIdentity(addressAtCurrentRow) self.setCurrentItemColor(QApplication.palette().text().color()) - """ - TODO remove - def on_action_ChanEnable(self): - currentRow = self.ui.tableWidgetChanList.currentRow() - addressAtCurrentRow = str( - self.ui.tableWidgetChanList.item(currentRow, 1).text()) - self.ui.tableWidgetChanList.item( - currentRow, 0).setTextColor(QApplication.palette().text().color()) - self.ui.tableWidgetChanList.item( - currentRow, 1).setTextColor(QApplication.palette().text().color()) - self.ui.tableWidgetChanList.item( - currentRow, 2).setTextColor(QApplication.palette().text().color()) - if shared.safeConfigGetBoolean(addressAtCurrentRow, 'mailinglist'): - self.ui.tableWidgetChanList.item(currentRow, 1).setTextColor(QtGui.QColor(137, 04, 177)) # magenta - if shared.safeConfigGetBoolean(addressAtCurrentRow, 'chan'): - self.ui.tableWidgetChanList.item(currentRow, 1).setTextColor(QtGui.QColor(216, 119, 0)) # orange - self.enableIdentity(addressAtCurrentRow) - """ - def enableIdentity(self, address): shared.config.set(address, 'enabled', 'true') shared.writeKeysFile() @@ -3482,49 +3363,20 @@ more work your computer must do to send the message. A Time-To-Live of four or f self.disableIdentity(address) self.setCurrentItemColor(QtGui.QColor(128, 128, 128)) - """ - TODO remove - def on_action_ChanDisable(self): - currentRow = self.ui.tableWidgetChanList.currentRow() - addressAtCurrentRow = str( - self.ui.tableWidgetChanList.item(currentRow, 1).text()) - self.ui.tableWidgetChanList.item( - currentRow, 0).setTextColor(QtGui.QColor(128, 128, 128)) - self.ui.tableWidgetChanList.item( - currentRow, 1).setTextColor(QtGui.QColor(128, 128, 128)) - self.ui.tableWidgetChanList.item( - currentRow, 2).setTextColor(QtGui.QColor(128, 128, 128)) - self.disableIdentity(address) - """ - def disableIdentity(self, address): - shared.config.set(str(addressAtCurrentRow), 'enabled', 'false') + shared.config.set(str(address), 'enabled', 'false') shared.writeKeysFile() shared.reloadMyAddressHashes() def on_action_Clipboard(self): - addressAtCurrentRow = self.getCurrentAccount() + address = self.getCurrentAccount() clipboard = QtGui.QApplication.clipboard() - clipboard.setText(str(addressAtCurrentRow)) - - """ - TODO remove - def on_action_ChanClipboard(self): - currentRow = self.ui.tableWidgetChanList.currentRow() - addressAtCurrentRow = self.ui.tableWidgetChanList.item( - currentRow, 1).text() - clipboard = QtGui.QApplication.clipboard() - clipboard.setText(str(addressAtCurrentRow)) - """ + clipboard.setText(str(address)) #set avatar functions def on_action_TreeWidgetSetAvatar(self): - addressAtCurrentRow = self.getCurrentAccount() - treeWidget = self.getCurrentTreeWidget() - setToIdenticon = not self.setAvatar(addressAtCurrentRow) - if treeWidget and setToIdenticon: - currentItem = treeWidget.currentItem() - currentItem.setIcon(0, avatarize(addressAtCurrentRow)) + address = self.getCurrentAccount() + self.setAvatar(address) def on_action_AddressBookSetAvatar(self): self.on_action_SetAvatar(self.ui.tableWidgetAddressBook) @@ -3595,7 +3447,9 @@ more work your computer must do to send the message. A Time-To-Live of four or f if not copied: print 'couldn\'t copy :(' # set the icon - self.rerenderSubscriptions() + self.rerenderTabTreeMessages() + self.rerenderTabTreeSubscriptions() + self.rerenderTabTreeChans() self.rerenderComboBoxSendFrom() self.rerenderComboBoxSendFromBroadcast() self.rerenderInboxFromLabels() @@ -3608,17 +3462,25 @@ more work your computer must do to send the message. A Time-To-Live of four or f return True - # TODO make one popMenu def on_context_menuYourIdentities(self, point): self.popMenuYourIdentities.exec_( self.ui.treeWidgetYourIdentities.mapToGlobal(point)) + # TODO make one popMenu def on_context_menuChan(self, point): self.popMenu.exec_( - self.ui.treeWidgetChanList.mapToGlobal(point)) + self.ui.treeWidgetChans.mapToGlobal(point)) def on_context_menuInbox(self, point): - self.popMenuInbox.exec_(self.ui.tableWidgetInbox.mapToGlobal(point)) + tableWidget = self.getCurrentMessagelist() + if tableWidget: + currentFolder = self.getCurrentFolder() + if currentFolder == False: + pass + if currentFolder == 'sent': + self.on_context_menuSent(point) + else: + self.popMenuInbox.exec_(tableWidget.mapToGlobal(point)) def on_context_menuSent(self, point): self.popMenuSent = QtGui.QMenu(self) @@ -3628,51 +3490,65 @@ more work your computer must do to send the message. A Time-To-Live of four or f # Check to see if this item is toodifficult and display an additional # menu option (Force Send) if it is. currentRow = self.ui.tableWidgetInbox.currentRow() - ackData = str(self.ui.tableWidgetInbox.item( - currentRow, 3).data(Qt.UserRole).toPyObject()) - queryreturn = sqlQuery('''SELECT status FROM sent where ackdata=?''', ackData) - for row in queryreturn: - status, = row - if status == 'toodifficult': - self.popMenuSent.addAction(self.actionForceSend) + if currentRow >= 0: + ackData = str(self.ui.tableWidgetInbox.item( + currentRow, 3).data(Qt.UserRole).toPyObject()) + queryreturn = sqlQuery('''SELECT status FROM sent where ackdata=?''', ackData) + for row in queryreturn: + status, = row + if status == 'toodifficult': + self.popMenuSent.addAction(self.actionForceSend) + self.popMenuSent.exec_(self.ui.tableWidgetInbox.mapToGlobal(point)) def inboxSearchLineEditPressed(self): - searchKeyword = self.ui.inboxSearchLineEdit.text().toUtf8().data() - searchOption = self.ui.inboxSearchOptionCB.currentText().toUtf8().data() - self.ui.inboxSearchLineEdit.setText(QString("")) - self.ui.textEditInboxMessage.setPlainText(QString("")) - self.loadInbox(searchOption, searchKeyword) + searchLine = self.getCurrentSearchLine() + searchOption = self.getCurrentSearchOption() + if searchLine: + searchKeyword = searchLine.text().toUtf8().data() + searchLine.setText(QString("")) + messageTextedit = self.getCurrentMessageTextedit() + if messageTextedit: + messageTextedit.setPlainText(QString("")) + messagelist = self.getCurrentMessagelist() + if messagelist: + account = self.getCurrentAccount() + folder = self.getCurrentFolder() + self.loadMessagelist(messagelist, account, folder, searchOption, searchKeyword) - def treeWidgetYourIdentitiesItemClicked(self): - currentItem = self.ui.treeWidgetYourIdentities.currentItem() - if currentItem: - accountFolder = currentItem.data(0, Qt.UserRole).toPyObject() - account = accountFolder[0] - folder = accountFolder[1] - self.loadMessagelist(self.ui.tableWidgetInbox, str(account), str(folder)) - - # TODO trees - def treeWidgetSubscribtionsItemClicked(self): - pass - - def treeWidgetChanListItemClicked(self): - pass + def treeWidgetItemClicked(self): + messagelist = self.getCurrentMessagelist() + if messagelist: + account = self.getCurrentAccount() + folder = self.getCurrentFolder() + self.loadMessagelist(messagelist, account, folder) def tableWidgetInboxItemClicked(self): - currentRow = self.ui.tableWidgetInbox.currentRow() - if currentRow >= 0: - msgid = str(self.ui.tableWidgetInbox.item( - currentRow, 3).data(Qt.UserRole).toPyObject()) - queryreturn = sqlQuery( - '''select message from inbox where msgid=?''', msgid) - if queryreturn != []: - for row in queryreturn: - message, = row - else: + folder = self.getCurrentFolder() + messageTextedit = self.getCurrentMessageTextedit() + queryreturn = [] + message = "" + + if folder == 'sent': + ackdata = self.getCurrentMessageId() + if ackdata and messageTextedit: + queryreturn = sqlQuery( + '''select message from sent where ackdata=?''', ackdata) + else: + msgid = self.getCurrentMessageId() + if msgid and messageTextedit: + queryreturn = sqlQuery( + '''select message from inbox where msgid=?''', msgid) + + if queryreturn != []: + for row in queryreturn: + message, = row + else: + data = self.getCurrentMessageId() + if data != False: message = "Error occurred: could not load message from disk." - message = unicode(message, 'utf-8)') - self.ui.textEditInboxMessage.setPlainText(message) + message = unicode(message, 'utf-8)') + messageTextedit.setPlainText(message) def tableWidgetAddressBookItemChanged(self): currentRow = self.ui.tableWidgetAddressBook.currentRow() @@ -3685,59 +3561,12 @@ more work your computer must do to send the message. A Time-To-Live of four or f self.rerenderInboxFromLabels() self.rerenderSentToLabels() - """ - TODO implement - def treeWidgetSubscriptionsItemChanged(self): - currentRow = self.ui.tableWidgetSubscriptions.currentRow() - if currentRow >= 0: - addressAtCurrentRow = self.ui.tableWidgetSubscriptions.item( - currentRow, 1).text() - sqlExecute('''UPDATE subscriptions set label=? WHERE address=?''', - str(self.ui.tableWidgetSubscriptions.item(currentRow, 0).text().toUtf8()), - str(addressAtCurrentRow)) - self.rerenderInboxFromLabels() - self.rerenderSentToLabels() - - def treeWidgetChanListItemChanged(self): - currentRow = self.ui.tableWidgetChanList.currentRow() - if currentRow >= 0: - addressAtCurrentRow = self.ui.tableWidgetChanList.item( - currentRow, 1).text() - shared.config.set(str(addressAtCurrentRow), 'label', str( - self.ui.tableWidgetChanList.item(currentRow, 0).text().toUtf8())) - shared.writeKeysFile() - self.rerenderComboBoxSendFrom() - self.rerenderComboBoxSendFromBroadcast() - # self.rerenderInboxFromLabels() - self.rerenderInboxToLabels() - self.rerenderSentFromLabels() - # self.rerenderSentToLabels() - """ - def writeNewAddressToTable(self, label, address, streamNumber): - pass - """ - TODO implement - self.ui.tableWidgetChanList.setSortingEnabled(False) - self.ui.tableWidgetChanList.insertRow(0) - newItem = QtGui.QTableWidgetItem(unicode(label, 'utf-8')) - newItem.setIcon(avatarize(address)) - self.ui.tableWidgetChanList.setItem( - 0, 0, newItem) - newItem = QtGui.QTableWidgetItem(address) - newItem.setFlags( - QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) - if shared.safeConfigGetBoolean(address, 'chan'): - newItem.setTextColor(QtGui.QColor(216, 119, 0)) # orange - self.ui.tableWidgetChanList.setItem(0, 1, newItem) - newItem = QtGui.QTableWidgetItem(streamNumber) - newItem.setFlags( - QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) - self.ui.tableWidgetChanList.setItem(0, 2, newItem) - # self.ui.tableWidgetChanList.setSortingEnabled(True) + self.rerenderTabTreeMessages() + self.rerenderTabTreeSubscriptions() + self.rerenderTabTreeChans() self.rerenderComboBoxSendFrom() self.rerenderComboBoxSendFromBroadcast() - """ def updateStatusBar(self, data): if data != "": @@ -4027,39 +3856,6 @@ class SpecialAddressBehaviorDialog(QtGui.QDialog): QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) -""" -TODO remove -class SpecialAddressBehaviorDialog(QtGui.QDialog): - - def __init__(self, parent): - QtGui.QWidget.__init__(self, parent) - self.ui = Ui_SpecialAddressBehaviorDialog() - self.ui.setupUi(self) - self.parent = parent - currentRow = parent.ui.tableWidgetChanList.currentRow() - addressAtCurrentRow = str( - parent.ui.tableWidgetChanList.item(currentRow, 1).text()) - if not shared.safeConfigGetBoolean(addressAtCurrentRow, 'chan'): - if shared.safeConfigGetBoolean(addressAtCurrentRow, 'mailinglist'): - self.ui.radioButtonBehaviorMailingList.click() - else: - self.ui.radioButtonBehaveNormalAddress.click() - try: - mailingListName = shared.config.get( - addressAtCurrentRow, 'mailinglistname') - except: - mailingListName = '' - self.ui.lineEditMailingListName.setText( - unicode(mailingListName, 'utf-8')) - else: # if addressAtCurrentRow is a chan address - self.ui.radioButtonBehaviorMailingList.setDisabled(True) - self.ui.lineEditMailingListName.setText(_translate( - "MainWindow", "This is a chan address. You cannot use it as a pseudo-mailing list.")) - - QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) -""" - - class AddAddressDialog(QtGui.QDialog): def __init__(self, parent): @@ -4163,8 +3959,6 @@ class NewSubscriptionDialog(QtGui.QDialog): class NewAddressDialog(QtGui.QDialog): def __init__(self, parent): - """ - TODO implement QtGui.QWidget.__init__(self, parent) self.ui = Ui_NewAddressDialog() self.ui.setupUi(self) @@ -4172,14 +3966,16 @@ class NewAddressDialog(QtGui.QDialog): row = 1 # Let's fill out the 'existing address' combo box with addresses from # the 'Your Identities' tab. - while self.parent.ui.tableWidgetChanList.item(row - 1, 1): + configSections = shared.config.sections() + for addressInKeysFile in configSections: + if addressInKeysFile == 'bitmessagesettings': + continue self.ui.radioButtonExisting.click() self.ui.comboBoxExisting.addItem( - self.parent.ui.tableWidgetChanList.item(row - 1, 1).text()) + addressInKeysFile) row += 1 self.ui.groupBoxDeterministic.setHidden(True) QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) - """ class newChanDialog(QtGui.QDialog): diff --git a/src/bitmessageqt/bitmessageui.py b/src/bitmessageqt/bitmessageui.py index 17ab57ee..d795f17c 100644 --- a/src/bitmessageqt/bitmessageui.py +++ b/src/bitmessageqt/bitmessageui.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'bitmessageui.ui' # -# Created: Fri Mar 20 19:19:21 2015 +# Created: Mon Mar 23 22:18:07 2015 # by: PyQt4 UI code generator 4.10.4 # # WARNING! All changes made in this file will be lost! @@ -69,15 +69,23 @@ class Ui_MainWindow(object): self.pushButtonNewAddress.setObjectName(_fromUtf8("pushButtonNewAddress")) self.verticalLayout_12.addWidget(self.pushButtonNewAddress) self.horizontalLayout_3.addLayout(self.verticalLayout_12) - self.verticalLayout_11 = QtGui.QVBoxLayout() - self.verticalLayout_11.setObjectName(_fromUtf8("verticalLayout_11")) + self.verticalLayout_7 = QtGui.QVBoxLayout() + self.verticalLayout_7.setObjectName(_fromUtf8("verticalLayout_7")) self.horizontalLayoutSearch = QtGui.QHBoxLayout() self.horizontalLayoutSearch.setContentsMargins(-1, 0, -1, -1) self.horizontalLayoutSearch.setObjectName(_fromUtf8("horizontalLayoutSearch")) self.inboxSearchLineEdit = QtGui.QLineEdit(self.inbox) self.inboxSearchLineEdit.setObjectName(_fromUtf8("inboxSearchLineEdit")) self.horizontalLayoutSearch.addWidget(self.inboxSearchLineEdit) - self.verticalLayout_11.addLayout(self.horizontalLayoutSearch) + self.inboxSearchOption = QtGui.QComboBox(self.inbox) + self.inboxSearchOption.setObjectName(_fromUtf8("inboxSearchOption")) + self.inboxSearchOption.addItem(_fromUtf8("")) + self.inboxSearchOption.addItem(_fromUtf8("")) + self.inboxSearchOption.addItem(_fromUtf8("")) + self.inboxSearchOption.addItem(_fromUtf8("")) + self.inboxSearchOption.addItem(_fromUtf8("")) + self.horizontalLayoutSearch.addWidget(self.inboxSearchOption) + self.verticalLayout_7.addLayout(self.horizontalLayoutSearch) self.tableWidgetInbox = QtGui.QTableWidget(self.inbox) self.tableWidgetInbox.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers) self.tableWidgetInbox.setAlternatingRowColors(True) @@ -103,13 +111,13 @@ class Ui_MainWindow(object): self.tableWidgetInbox.horizontalHeader().setStretchLastSection(True) self.tableWidgetInbox.verticalHeader().setVisible(False) self.tableWidgetInbox.verticalHeader().setDefaultSectionSize(26) - self.verticalLayout_11.addWidget(self.tableWidgetInbox) + self.verticalLayout_7.addWidget(self.tableWidgetInbox) self.textEditInboxMessage = QtGui.QTextEdit(self.inbox) self.textEditInboxMessage.setBaseSize(QtCore.QSize(0, 500)) self.textEditInboxMessage.setReadOnly(True) self.textEditInboxMessage.setObjectName(_fromUtf8("textEditInboxMessage")) - self.verticalLayout_11.addWidget(self.textEditInboxMessage) - self.horizontalLayout_3.addLayout(self.verticalLayout_11) + self.verticalLayout_7.addWidget(self.textEditInboxMessage) + self.horizontalLayout_3.addLayout(self.verticalLayout_7) self.gridLayout.addLayout(self.horizontalLayout_3, 0, 0, 1, 1) icon2 = QtGui.QIcon() icon2.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/inbox.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) @@ -278,10 +286,10 @@ class Ui_MainWindow(object): self.tabWidget.addTab(self.send, icon4, _fromUtf8("")) self.subscriptions = QtGui.QWidget() self.subscriptions.setObjectName(_fromUtf8("subscriptions")) - self.gridLayout_4 = QtGui.QGridLayout(self.subscriptions) - self.gridLayout_4.setObjectName(_fromUtf8("gridLayout_4")) - self.horizontalLayout_2 = QtGui.QHBoxLayout() - self.horizontalLayout_2.setObjectName(_fromUtf8("horizontalLayout_2")) + self.gridLayout_3 = QtGui.QGridLayout(self.subscriptions) + self.gridLayout_3.setObjectName(_fromUtf8("gridLayout_3")) + self.horizontalLayout_4 = QtGui.QHBoxLayout() + self.horizontalLayout_4.setObjectName(_fromUtf8("horizontalLayout_4")) self.verticalLayout_3 = QtGui.QVBoxLayout() self.verticalLayout_3.setObjectName(_fromUtf8("verticalLayout_3")) self.treeWidgetSubscriptions = QtGui.QTreeWidget(self.subscriptions) @@ -298,12 +306,23 @@ class Ui_MainWindow(object): self.pushButtonAddSubscription.setMaximumSize(QtCore.QSize(200, 16777215)) self.pushButtonAddSubscription.setObjectName(_fromUtf8("pushButtonAddSubscription")) self.verticalLayout_3.addWidget(self.pushButtonAddSubscription) - self.horizontalLayout_2.addLayout(self.verticalLayout_3) + self.horizontalLayout_4.addLayout(self.verticalLayout_3) self.verticalLayout_4 = QtGui.QVBoxLayout() self.verticalLayout_4.setObjectName(_fromUtf8("verticalLayout_4")) - self.inboxSearchLineSubscriptions = QtGui.QLineEdit(self.subscriptions) - self.inboxSearchLineSubscriptions.setObjectName(_fromUtf8("inboxSearchLineSubscriptions")) - self.verticalLayout_4.addWidget(self.inboxSearchLineSubscriptions) + self.horizontalLayout_2 = QtGui.QHBoxLayout() + self.horizontalLayout_2.setObjectName(_fromUtf8("horizontalLayout_2")) + self.inboxSearchLineEditSubscriptions = QtGui.QLineEdit(self.subscriptions) + self.inboxSearchLineEditSubscriptions.setObjectName(_fromUtf8("inboxSearchLineEditSubscriptions")) + self.horizontalLayout_2.addWidget(self.inboxSearchLineEditSubscriptions) + self.inboxSearchOptionSubscriptions = QtGui.QComboBox(self.subscriptions) + self.inboxSearchOptionSubscriptions.setObjectName(_fromUtf8("inboxSearchOptionSubscriptions")) + self.inboxSearchOptionSubscriptions.addItem(_fromUtf8("")) + self.inboxSearchOptionSubscriptions.addItem(_fromUtf8("")) + self.inboxSearchOptionSubscriptions.addItem(_fromUtf8("")) + self.inboxSearchOptionSubscriptions.addItem(_fromUtf8("")) + self.inboxSearchOptionSubscriptions.addItem(_fromUtf8("")) + self.horizontalLayout_2.addWidget(self.inboxSearchOptionSubscriptions) + self.verticalLayout_4.addLayout(self.horizontalLayout_2) self.tableWidgetInboxSubscriptions = QtGui.QTableWidget(self.subscriptions) self.tableWidgetInboxSubscriptions.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers) self.tableWidgetInboxSubscriptions.setAlternatingRowColors(True) @@ -330,83 +349,90 @@ class Ui_MainWindow(object): self.tableWidgetInboxSubscriptions.verticalHeader().setVisible(False) self.tableWidgetInboxSubscriptions.verticalHeader().setDefaultSectionSize(26) self.verticalLayout_4.addWidget(self.tableWidgetInboxSubscriptions) - self.textEditInboxSubscriptions = QtGui.QTextEdit(self.subscriptions) - self.textEditInboxSubscriptions.setBaseSize(QtCore.QSize(0, 500)) - self.textEditInboxSubscriptions.setReadOnly(True) - self.textEditInboxSubscriptions.setObjectName(_fromUtf8("textEditInboxSubscriptions")) - self.verticalLayout_4.addWidget(self.textEditInboxSubscriptions) - self.horizontalLayout_2.addLayout(self.verticalLayout_4) - self.gridLayout_4.addLayout(self.horizontalLayout_2, 0, 0, 1, 1) + self.textEditInboxMessageSubscriptions = QtGui.QTextEdit(self.subscriptions) + self.textEditInboxMessageSubscriptions.setBaseSize(QtCore.QSize(0, 500)) + self.textEditInboxMessageSubscriptions.setReadOnly(True) + self.textEditInboxMessageSubscriptions.setObjectName(_fromUtf8("textEditInboxMessageSubscriptions")) + self.verticalLayout_4.addWidget(self.textEditInboxMessageSubscriptions) + self.horizontalLayout_4.addLayout(self.verticalLayout_4) + self.gridLayout_3.addLayout(self.horizontalLayout_4, 0, 0, 1, 1) icon6 = QtGui.QIcon() icon6.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/subscriptions.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.tabWidget.addTab(self.subscriptions, icon6, _fromUtf8("")) self.tab_3 = QtGui.QWidget() self.tab_3.setObjectName(_fromUtf8("tab_3")) - self.gridLayout_3 = QtGui.QGridLayout(self.tab_3) - self.gridLayout_3.setObjectName(_fromUtf8("gridLayout_3")) - self.horizontalLayout_4 = QtGui.QHBoxLayout() - self.horizontalLayout_4.setObjectName(_fromUtf8("horizontalLayout_4")) + self.gridLayout_4 = QtGui.QGridLayout(self.tab_3) + self.gridLayout_4.setObjectName(_fromUtf8("gridLayout_4")) + self.horizontalLayout_7 = QtGui.QHBoxLayout() + self.horizontalLayout_7.setObjectName(_fromUtf8("horizontalLayout_7")) self.verticalLayout_17 = QtGui.QVBoxLayout() self.verticalLayout_17.setObjectName(_fromUtf8("verticalLayout_17")) - self.treeWidgetChanList = QtGui.QTreeWidget(self.tab_3) - self.treeWidgetChanList.setMaximumSize(QtCore.QSize(200, 16777215)) - self.treeWidgetChanList.setFrameShadow(QtGui.QFrame.Sunken) - self.treeWidgetChanList.setLineWidth(1) - self.treeWidgetChanList.setAlternatingRowColors(True) - self.treeWidgetChanList.setSelectionMode(QtGui.QAbstractItemView.SingleSelection) - self.treeWidgetChanList.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) - self.treeWidgetChanList.setObjectName(_fromUtf8("treeWidgetChanList")) + self.treeWidgetChans = QtGui.QTreeWidget(self.tab_3) + self.treeWidgetChans.setMaximumSize(QtCore.QSize(200, 16777215)) + self.treeWidgetChans.setFrameShadow(QtGui.QFrame.Sunken) + self.treeWidgetChans.setLineWidth(1) + self.treeWidgetChans.setAlternatingRowColors(True) + self.treeWidgetChans.setSelectionMode(QtGui.QAbstractItemView.SingleSelection) + self.treeWidgetChans.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) + self.treeWidgetChans.setObjectName(_fromUtf8("treeWidgetChans")) icon7 = QtGui.QIcon() icon7.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/can-icon-16px.png")), QtGui.QIcon.Selected, QtGui.QIcon.Off) - self.treeWidgetChanList.headerItem().setIcon(0, icon7) - self.verticalLayout_17.addWidget(self.treeWidgetChanList) - self.pushButtonAddChanel = QtGui.QPushButton(self.tab_3) - self.pushButtonAddChanel.setMaximumSize(QtCore.QSize(200, 16777215)) - self.pushButtonAddChanel.setObjectName(_fromUtf8("pushButtonAddChanel")) - self.verticalLayout_17.addWidget(self.pushButtonAddChanel) - self.horizontalLayout_4.addLayout(self.verticalLayout_17) - self.verticalLayout_13 = QtGui.QVBoxLayout() - self.verticalLayout_13.setObjectName(_fromUtf8("verticalLayout_13")) - self.horizontalLayoutSearch_2 = QtGui.QHBoxLayout() - self.horizontalLayoutSearch_2.setContentsMargins(-1, 0, -1, -1) - self.horizontalLayoutSearch_2.setObjectName(_fromUtf8("horizontalLayoutSearch_2")) - self.inboxSearchLineEdit_2 = QtGui.QLineEdit(self.tab_3) - self.inboxSearchLineEdit_2.setObjectName(_fromUtf8("inboxSearchLineEdit_2")) - self.horizontalLayoutSearch_2.addWidget(self.inboxSearchLineEdit_2) - self.verticalLayout_13.addLayout(self.horizontalLayoutSearch_2) - self.tableWidgetInbox_2 = QtGui.QTableWidget(self.tab_3) - self.tableWidgetInbox_2.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers) - self.tableWidgetInbox_2.setAlternatingRowColors(True) - self.tableWidgetInbox_2.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection) - self.tableWidgetInbox_2.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) - self.tableWidgetInbox_2.setWordWrap(False) - self.tableWidgetInbox_2.setObjectName(_fromUtf8("tableWidgetInbox_2")) - self.tableWidgetInbox_2.setColumnCount(4) - self.tableWidgetInbox_2.setRowCount(0) + self.treeWidgetChans.headerItem().setIcon(0, icon7) + self.verticalLayout_17.addWidget(self.treeWidgetChans) + self.pushButtonAddChan = QtGui.QPushButton(self.tab_3) + self.pushButtonAddChan.setMaximumSize(QtCore.QSize(200, 16777215)) + self.pushButtonAddChan.setObjectName(_fromUtf8("pushButtonAddChan")) + self.verticalLayout_17.addWidget(self.pushButtonAddChan) + self.horizontalLayout_7.addLayout(self.verticalLayout_17) + self.verticalLayout_8 = QtGui.QVBoxLayout() + self.verticalLayout_8.setObjectName(_fromUtf8("verticalLayout_8")) + self.horizontalLayout_6 = QtGui.QHBoxLayout() + self.horizontalLayout_6.setObjectName(_fromUtf8("horizontalLayout_6")) + self.inboxSearchLineEditChans = QtGui.QLineEdit(self.tab_3) + self.inboxSearchLineEditChans.setObjectName(_fromUtf8("inboxSearchLineEditChans")) + self.horizontalLayout_6.addWidget(self.inboxSearchLineEditChans) + self.inboxSearchOptionChans = QtGui.QComboBox(self.tab_3) + self.inboxSearchOptionChans.setObjectName(_fromUtf8("inboxSearchOptionChans")) + self.inboxSearchOptionChans.addItem(_fromUtf8("")) + self.inboxSearchOptionChans.addItem(_fromUtf8("")) + self.inboxSearchOptionChans.addItem(_fromUtf8("")) + self.inboxSearchOptionChans.addItem(_fromUtf8("")) + self.inboxSearchOptionChans.addItem(_fromUtf8("")) + self.horizontalLayout_6.addWidget(self.inboxSearchOptionChans) + self.verticalLayout_8.addLayout(self.horizontalLayout_6) + self.tableWidgetInboxChans = QtGui.QTableWidget(self.tab_3) + self.tableWidgetInboxChans.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers) + self.tableWidgetInboxChans.setAlternatingRowColors(True) + self.tableWidgetInboxChans.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection) + self.tableWidgetInboxChans.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) + self.tableWidgetInboxChans.setWordWrap(False) + self.tableWidgetInboxChans.setObjectName(_fromUtf8("tableWidgetInboxChans")) + self.tableWidgetInboxChans.setColumnCount(4) + self.tableWidgetInboxChans.setRowCount(0) item = QtGui.QTableWidgetItem() - self.tableWidgetInbox_2.setHorizontalHeaderItem(0, item) + self.tableWidgetInboxChans.setHorizontalHeaderItem(0, item) item = QtGui.QTableWidgetItem() - self.tableWidgetInbox_2.setHorizontalHeaderItem(1, item) + self.tableWidgetInboxChans.setHorizontalHeaderItem(1, item) item = QtGui.QTableWidgetItem() - self.tableWidgetInbox_2.setHorizontalHeaderItem(2, item) + self.tableWidgetInboxChans.setHorizontalHeaderItem(2, item) item = QtGui.QTableWidgetItem() - self.tableWidgetInbox_2.setHorizontalHeaderItem(3, item) - self.tableWidgetInbox_2.horizontalHeader().setCascadingSectionResizes(True) - self.tableWidgetInbox_2.horizontalHeader().setDefaultSectionSize(200) - self.tableWidgetInbox_2.horizontalHeader().setHighlightSections(False) - self.tableWidgetInbox_2.horizontalHeader().setMinimumSectionSize(27) - self.tableWidgetInbox_2.horizontalHeader().setSortIndicatorShown(False) - self.tableWidgetInbox_2.horizontalHeader().setStretchLastSection(True) - self.tableWidgetInbox_2.verticalHeader().setVisible(False) - self.tableWidgetInbox_2.verticalHeader().setDefaultSectionSize(26) - self.verticalLayout_13.addWidget(self.tableWidgetInbox_2) - self.textEditInboxMessage_2 = QtGui.QTextEdit(self.tab_3) - self.textEditInboxMessage_2.setBaseSize(QtCore.QSize(0, 500)) - self.textEditInboxMessage_2.setReadOnly(True) - self.textEditInboxMessage_2.setObjectName(_fromUtf8("textEditInboxMessage_2")) - self.verticalLayout_13.addWidget(self.textEditInboxMessage_2) - self.horizontalLayout_4.addLayout(self.verticalLayout_13) - self.gridLayout_3.addLayout(self.horizontalLayout_4, 0, 0, 1, 1) + self.tableWidgetInboxChans.setHorizontalHeaderItem(3, item) + self.tableWidgetInboxChans.horizontalHeader().setCascadingSectionResizes(True) + self.tableWidgetInboxChans.horizontalHeader().setDefaultSectionSize(200) + self.tableWidgetInboxChans.horizontalHeader().setHighlightSections(False) + self.tableWidgetInboxChans.horizontalHeader().setMinimumSectionSize(27) + self.tableWidgetInboxChans.horizontalHeader().setSortIndicatorShown(False) + self.tableWidgetInboxChans.horizontalHeader().setStretchLastSection(True) + self.tableWidgetInboxChans.verticalHeader().setVisible(False) + self.tableWidgetInboxChans.verticalHeader().setDefaultSectionSize(26) + self.verticalLayout_8.addWidget(self.tableWidgetInboxChans) + self.textEditInboxMessageChans = QtGui.QTextEdit(self.tab_3) + self.textEditInboxMessageChans.setBaseSize(QtCore.QSize(0, 500)) + self.textEditInboxMessageChans.setReadOnly(True) + self.textEditInboxMessageChans.setObjectName(_fromUtf8("textEditInboxMessageChans")) + self.verticalLayout_8.addWidget(self.textEditInboxMessageChans) + self.horizontalLayout_7.addLayout(self.verticalLayout_8) + self.gridLayout_4.addLayout(self.horizontalLayout_7, 0, 0, 1, 1) icon8 = QtGui.QIcon() icon8.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/can-icon-16px.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.tabWidget.addTab(self.tab_3, icon8, _fromUtf8("")) @@ -598,6 +624,11 @@ class Ui_MainWindow(object): self.treeWidgetYourIdentities.headerItem().setText(0, _translate("MainWindow", "Identities", None)) self.pushButtonNewAddress.setText(_translate("MainWindow", "New Indentitiy", None)) self.inboxSearchLineEdit.setPlaceholderText(_translate("MainWindow", "Search", None)) + self.inboxSearchOption.setItemText(0, _translate("MainWindow", "All", None)) + self.inboxSearchOption.setItemText(1, _translate("MainWindow", "To", None)) + self.inboxSearchOption.setItemText(2, _translate("MainWindow", "From", None)) + self.inboxSearchOption.setItemText(3, _translate("MainWindow", "Subject", None)) + self.inboxSearchOption.setItemText(4, _translate("MainWindow", "Message", None)) self.tableWidgetInbox.setSortingEnabled(True) item = self.tableWidgetInbox.horizontalHeaderItem(0) item.setText(_translate("MainWindow", "To", None)) @@ -610,7 +641,7 @@ class Ui_MainWindow(object): self.tabWidget.setTabText(self.tabWidget.indexOf(self.inbox), _translate("MainWindow", "Messages", None)) self.tableWidgetAddressBook.setSortingEnabled(True) item = self.tableWidgetAddressBook.horizontalHeaderItem(0) - item.setText(_translate("MainWindow", "Name", None)) + item.setText(_translate("MainWindow", "Address book", None)) item = self.tableWidgetAddressBook.horizontalHeaderItem(1) item.setText(_translate("MainWindow", "Address", None)) self.pushButtonAddAddressBook.setText(_translate("MainWindow", "Add Contact", None)) @@ -638,7 +669,12 @@ class Ui_MainWindow(object): self.tabWidget.setTabText(self.tabWidget.indexOf(self.send), _translate("MainWindow", "Send", None)) self.treeWidgetSubscriptions.headerItem().setText(0, _translate("MainWindow", "Subscriptions", None)) self.pushButtonAddSubscription.setText(_translate("MainWindow", "Add new Subscription", None)) - self.inboxSearchLineSubscriptions.setPlaceholderText(_translate("MainWindow", "Search", None)) + self.inboxSearchLineEditSubscriptions.setPlaceholderText(_translate("MainWindow", "Search", None)) + self.inboxSearchOptionSubscriptions.setItemText(0, _translate("MainWindow", "All", None)) + self.inboxSearchOptionSubscriptions.setItemText(1, _translate("MainWindow", "To", None)) + self.inboxSearchOptionSubscriptions.setItemText(2, _translate("MainWindow", "From", None)) + self.inboxSearchOptionSubscriptions.setItemText(3, _translate("MainWindow", "Subject", None)) + self.inboxSearchOptionSubscriptions.setItemText(4, _translate("MainWindow", "Message", None)) self.tableWidgetInboxSubscriptions.setSortingEnabled(True) item = self.tableWidgetInboxSubscriptions.horizontalHeaderItem(0) item.setText(_translate("MainWindow", "To", None)) @@ -649,17 +685,22 @@ class Ui_MainWindow(object): item = self.tableWidgetInboxSubscriptions.horizontalHeaderItem(3) item.setText(_translate("MainWindow", "Received", None)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.subscriptions), _translate("MainWindow", "Subscriptions", None)) - self.treeWidgetChanList.headerItem().setText(0, _translate("MainWindow", "Chans", None)) - self.pushButtonAddChanel.setText(_translate("MainWindow", "Add Chanel", None)) - self.inboxSearchLineEdit_2.setPlaceholderText(_translate("MainWindow", "Search", None)) - self.tableWidgetInbox_2.setSortingEnabled(True) - item = self.tableWidgetInbox_2.horizontalHeaderItem(0) + self.treeWidgetChans.headerItem().setText(0, _translate("MainWindow", "Chans", None)) + self.pushButtonAddChan.setText(_translate("MainWindow", "Add Chan", None)) + self.inboxSearchLineEditChans.setPlaceholderText(_translate("MainWindow", "Search", None)) + self.inboxSearchOptionChans.setItemText(0, _translate("MainWindow", "All", None)) + self.inboxSearchOptionChans.setItemText(1, _translate("MainWindow", "To", None)) + self.inboxSearchOptionChans.setItemText(2, _translate("MainWindow", "From", None)) + self.inboxSearchOptionChans.setItemText(3, _translate("MainWindow", "Subject", None)) + self.inboxSearchOptionChans.setItemText(4, _translate("MainWindow", "Message", None)) + self.tableWidgetInboxChans.setSortingEnabled(True) + item = self.tableWidgetInboxChans.horizontalHeaderItem(0) item.setText(_translate("MainWindow", "To", None)) - item = self.tableWidgetInbox_2.horizontalHeaderItem(1) + item = self.tableWidgetInboxChans.horizontalHeaderItem(1) item.setText(_translate("MainWindow", "From", None)) - item = self.tableWidgetInbox_2.horizontalHeaderItem(2) + item = self.tableWidgetInboxChans.horizontalHeaderItem(2) item.setText(_translate("MainWindow", "Subject", None)) - item = self.tableWidgetInbox_2.horizontalHeaderItem(3) + item = self.tableWidgetInboxChans.horizontalHeaderItem(3) item.setText(_translate("MainWindow", "Received", None)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_3), _translate("MainWindow", "Chans", None)) self.radioButtonBlacklist.setText(_translate("MainWindow", "Use a Blacklist (Allow all incoming messages except those on the Blacklist)", None)) diff --git a/src/bitmessageqt/bitmessageui.ui b/src/bitmessageqt/bitmessageui.ui index 09498ac4..cb50a858 100644 --- a/src/bitmessageqt/bitmessageui.ui +++ b/src/bitmessageqt/bitmessageui.ui @@ -105,7 +105,7 @@ - + @@ -118,6 +118,35 @@ + + + + + All + + + + + To + + + + + From + + + + + Subject + + + + + Message + + + + @@ -255,7 +284,7 @@ - Name + Address book @@ -573,7 +602,6 @@ p, li { white-space: pre-wrap; } - pushButtonFetchNamecoinID @@ -583,9 +611,9 @@ p, li { white-space: pre-wrap; } Subscriptions - + - + @@ -635,11 +663,44 @@ p, li { white-space: pre-wrap; } - - - Search - - + + + + + Search + + + + + + + + All + + + + + To + + + + + From + + + + + Subject + + + + + Message + + + + + @@ -708,7 +769,7 @@ p, li { white-space: pre-wrap; } - + 0 @@ -734,13 +795,13 @@ p, li { white-space: pre-wrap; } Chans - + - + - + 200 @@ -775,7 +836,7 @@ p, li { white-space: pre-wrap; } - + 200 @@ -783,30 +844,56 @@ p, li { white-space: pre-wrap; } - Add Chanel + Add Chan - + - - - 0 - + - + Search + + + + + All + + + + + To + + + + + From + + + + + Subject + + + + + Message + + + + - + QAbstractItemView::NoEditTriggers @@ -872,7 +959,7 @@ p, li { white-space: pre-wrap; } - + 0 From b6a91ed4b3078a5bec29a82a4b79e79f948e36b1 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 20 Jun 2015 09:54:15 +0200 Subject: [PATCH 008/210] Minor changes in cli, PoW, and a couple of new api calls. --- src/api.py | 70 +++++++++++++++++++++++++++++++++++++++++- src/bitmessagecli.py | 68 ++++++++++++++++++++++++++++++++++++++++ src/class_sqlThread.py | 2 +- src/openclpow.py | 14 ++++++--- src/proofofwork.py | 9 ++++-- 5 files changed, 154 insertions(+), 9 deletions(-) diff --git a/src/api.py b/src/api.py index 9e498f46..69e883b3 100644 --- a/src/api.py +++ b/src/api.py @@ -26,7 +26,7 @@ from pyelliptic.openssl import OpenSSL from struct import pack # Classes -from helper_sql import sqlQuery,sqlExecute,SqlBulkExecute +from helper_sql import sqlQuery,sqlExecute,SqlBulkExecute,sqlStoredProcedure from debug import logger # Helper Functions @@ -181,6 +181,12 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): return data elif method == 'listAddressBookEntries' or method == 'listAddressbook': # the listAddressbook alias should be removed eventually. queryreturn = sqlQuery('''SELECT label, address from addressbook''') + if len(params) == 1: + label, = params + label = self._decode(label, "base64") + queryreturn = sqlQuery('''SELECT label, address from addressbook WHERE label = ?''', label) + elif len(params) > 1: + raise APIError(0, "Too many paremeters, max 1") data = '{"addresses":[' for row in queryreturn: label, address = row @@ -190,6 +196,22 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): data += json.dumps({'label':label.encode('base64'), 'address': address}, indent=4, separators=(',', ': ')) data += ']}' return data + elif method == 'getAddressBookEntry': # search by label + if len(params) != 1: + raise APIError(0, "I need a label") + label, = params + label = self._decode(label, "base64") + queryreturn = sqlQuery('''SELECT label, address from addressbook WHERE label = ?''', label) + data = '{"address":[' + for row in queryreturn: + label, address = row + label = shared.fixPotentiallyInvalidUTF8Data(label) + if len(data) > 20: + data += ',' + data += json.dumps({'label':label.encode('base64'), 'address': address}, indent=4, separators=(',', ': ')) + data += ']}' + return data + elif method == 'addAddressBookEntry' or method == 'addAddressbook': # the addAddressbook alias should be deleted eventually. if len(params) != 2: raise APIError(0, "I need label and address") @@ -951,6 +973,52 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): return json.dumps({'status':status, 'addressVersion':addressVersion, 'streamNumber':streamNumber, 'ripe':ripe.encode('base64')}, indent=4, separators=(',', ': ')) + elif method == 'getInboxCount': + #queryreturn = sqlQuery('''SELECT read, received < 'now' - 60 AS old, COUNT (*) AS cnt FROM inbox WHERE folder = 'inbox' GROUP BY read, old''') + ret = {} + queryreturn = sqlQuery('''SELECT COUNT (*) AS cnt FROM inbox WHERE folder = 'inbox' AND read = 0 AND received < 'now' - 60''') + for row in queryreturn: + count, = row + ret['oldread'] = count + queryreturn = sqlQuery('''SELECT COUNT (*) AS cnt FROM inbox WHERE folder = 'inbox' AND read = 1 AND received < 'now' - 60''') + for row in queryreturn: + count, = row + ret['oldunread'] = count + queryreturn = sqlQuery('''SELECT COUNT (*) AS cnt FROM inbox WHERE folder = 'inbox' AND read = 0 AND received >= 'now' - 60''') + for row in queryreturn: + count, = row + ret['newread'] = count + queryreturn = sqlQuery('''SELECT COUNT (*) AS cnt FROM inbox WHERE folder = 'inbox' AND read = 1 AND received >= 'now' - 60''') + for row in queryreturn: + count, = row + ret['newunread'] = count + data = '{"inboxCount":{' + for key in ret: + val = ret[key] + if len(data) > 16: + data += ',' + data += json.dumps({key:val}, indent=4, separators=(',', ': ')) + data += '}}' + elif method == 'getSentCount': + ret = {} + queryreturn = sqlQuery('''SELECT COUNT (*) AS cnt FROM sent WHERE folder = 'sent' AND status = 'msgqueued' ''') + for row in queryreturn: + count, = row + ret['queued'] = count + queryreturn = sqlQuery('''SELECT COUNT (*) AS cnt FROM sent WHERE folder = 'sent' AND status = 'msgsent' ''') + for row in queryreturn: + count, = row + ret['awaitingack'] = count + data = '{"sentCount":{' + for key in ret: + val = ret[key] + if len(data) > 15: + data += ',' + data += json.dumps({key:val}, indent=4, separators=(',', ': ')) + data += '}}' + elif method == 'deleteAndVacuum': + sqlStoredProcedure('deleteandvacuume') + return 'done' else: raise APIError(20, 'Invalid method: %s' % method) diff --git a/src/bitmessagecli.py b/src/bitmessagecli.py index cfe892b6..87d71f05 100644 --- a/src/bitmessagecli.py +++ b/src/bitmessagecli.py @@ -575,6 +575,46 @@ def genAdd(lbl,deterministic, passphrase, numOfAdd, addVNum, streamNum, ripe): # else: return 'Entry Error' +def delMilAddr(): #Generate address + global usrPrompt + try: + response = api.listAddresses2() + # if api is too old just return then fail + if "API Error 0020" in response: return + addresses = json.loads(response) + for entry in addresses['addresses']: + if entry['label'].decode('base64')[:6] == "random": + api.deleteAddress(entry['address']) + except: + print '\n Connection Error\n' + usrPrompt = 0 + main() + +def genMilAddr(): #Generate address + global usrPrompt + maxn = 0 + try: + response = api.listAddresses2() + if "API Error 0020" in response: return + addresses = json.loads(response) + for entry in addresses['addresses']: + if entry['label'].decode('base64')[:6] == "random": + newn = int(entry['label'].decode('base64')[6:]) + if maxn < newn: + maxn = newn + except: + print "\n Some error\n" + print "\n Starting at " + str(maxn) + "\n" + for i in range(maxn, 10000): + lbl = "random" + str(i) + addressLabel = lbl.encode('base64') + try: + generatedAddress = api.createRandomAddress(addressLabel) + except: + print '\n Connection Error\n' + usrPrompt = 0 + main() + def saveFile(fileName, fileData): #Allows attachments and messages/broadcats to be saved #This section finds all invalid characters and replaces them with ~ @@ -1249,6 +1289,19 @@ def markAllMessagesUnread(): if message['read']: markMessageUnread(message['msgid']) +def clientStatus(): + try: + clientStatus = json.loads(api.clientStatus()) + except: + print '\n Connection Error\n' + usrPrompt = 0 + main() + print "\nnetworkStatus: " + clientStatus['networkStatus'] + "\n" + print "\nnetworkConnections: " + str(clientStatus['networkConnections']) + "\n" + print "\nnumberOfPubkeysProcessed: " + str(clientStatus['numberOfPubkeysProcessed']) + "\n" + print "\nnumberOfMessagesProcessed: " + str(clientStatus['numberOfMessagesProcessed']) + "\n" + print "\nnumberOfBroadcastsProcessed: " + str(clientStatus['numberOfBroadcastsProcessed']) + "\n" + def UI(usrInput): #Main user menu global usrPrompt @@ -1665,6 +1718,21 @@ def UI(usrInput): #Main user menu markAllMessagesUnread() usrPrompt = 1 main() + + elif usrInput == "status": + clientStatus() + usrPrompt = 1 + main() + + elif usrInput == "million+": + genMilAddr() + usrPrompt = 1 + main() + + elif usrInput == "million-": + delMilAddr() + usrPrompt = 1 + main() else: print '\n "',usrInput,'" is not a command.\n' diff --git a/src/class_sqlThread.py b/src/class_sqlThread.py index 4eadc6f0..cbceed8f 100644 --- a/src/class_sqlThread.py +++ b/src/class_sqlThread.py @@ -400,7 +400,7 @@ class sqlThread(threading.Thread): queryreturn = self.cur.fetchall() for row in queryreturn: value, = row - if int(value) < int(time.time()) - 2592000: + if int(value) < int(time.time()) - 86400: logger.info('It has been a long time since the messages.dat file has been vacuumed. Vacuuming now...') try: self.cur.execute( ''' VACUUM ''') diff --git a/src/openclpow.py b/src/openclpow.py index 0876aa79..bd9dad3b 100644 --- a/src/openclpow.py +++ b/src/openclpow.py @@ -2,6 +2,7 @@ import numpy from struct import pack, unpack import time import hashlib +import random import pyopencl as cl hash_dt = numpy.dtype([('target', numpy.uint64), ('v', numpy.str_, 73)]) @@ -14,7 +15,7 @@ try: ctx = cl.create_some_context() queue = cl.CommandQueue(ctx) - f = open('kernel.cl', 'r') + f = open('/usr/src/PyBitmessage/src/kernel.cl', 'r') fstr = ''.join(f.readlines()) program = cl.Program(ctx, fstr).build() except: @@ -42,7 +43,10 @@ def do_opencl_pow(hash, target): kernel.set_arg(1, dest_buf) start = time.time() + #startpos = random.getrandbits(32) << 32 | random.getrandbits(32) + #startpos = random.getrandbits(32) startpos = 0 + progress = 0 globamt = worksize*2000 while output[0][0] == 0: @@ -50,17 +54,19 @@ def do_opencl_pow(hash, target): cl.enqueue_nd_range_kernel(queue, kernel, (globamt,), (worksize,)) cl.enqueue_read_buffer(queue, dest_buf, output) queue.finish() + #startpos == (globamt + startpos) & 0xFFFFFFFFFFFFFFFF startpos += globamt + progress += globamt sofar = time.time() - start - print sofar, startpos / sofar, "hashes/sec" + print sofar, progress / sofar, "hashes/sec" taken = time.time() - start - print startpos, taken + print progress, taken return output[0][0] if __name__ == "__main__": target = 54227212183L initialHash = "3758f55b5a8d902fd3597e4ce6a2d3f23daff735f65d9698c270987f4e67ad590b93f3ffeba0ef2fd08a8dc2f87b68ae5a0dc819ab57f22ad2c4c9c8618a43b3".decode("hex") - nonce = do_pow(initialHash.encode("hex"), target) + nonce = do_opencl_pow(initialHash.encode("hex"), target) trialValue, = unpack('>Q',hashlib.sha512(hashlib.sha512(pack('>Q',nonce) + initialHash).digest()).digest()[0:8]) print "{} - value {} < {}".format(nonce, trialValue, target) diff --git a/src/proofofwork.py b/src/proofofwork.py index f05e7eea..fec70287 100644 --- a/src/proofofwork.py +++ b/src/proofofwork.py @@ -6,7 +6,7 @@ from struct import unpack, pack import sys from shared import config, frozen import shared -from openclpow import do_opencl_pow +import openclpow #import os def _set_idle(): @@ -33,6 +33,7 @@ def _pool_worker(nonce, initialHash, target, pool_size): return [trialValue, nonce] def _doSafePoW(target, initialHash): + print "Safe POW\n" nonce = 0 trialValue = float('inf') while trialValue > target: @@ -41,6 +42,7 @@ def _doSafePoW(target, initialHash): return [trialValue, nonce] def _doFastPoW(target, initialHash): + print "Fast POW\n" import time from multiprocessing import Pool, cpu_count try: @@ -72,13 +74,14 @@ def _doFastPoW(target, initialHash): time.sleep(0.2) def _doGPUPow(target, initialHash): - nonce = do_opencl_pow(initialHash.encode("hex"), target) + print "GPU POW\n" + nonce = openclpow.do_opencl_pow(initialHash.encode("hex"), target) trialValue, = unpack('>Q',hashlib.sha512(hashlib.sha512(pack('>Q',nonce) + initialHash).digest()).digest()[0:8]) #print "{} - value {} < {}".format(nonce, trialValue, target) return [trialValue, nonce] def run(target, initialHash): - if has_opencl: + if openclpow.has_opencl(): return _doGPUPow(target, initialHash) elif frozen == "macosx_app" or not frozen: return _doFastPoW(target, initialHash) From c61e41a356e280f22d74666d4aeff313ce8bfd11 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 20 Jun 2015 10:17:39 +0200 Subject: [PATCH 009/210] Reverted the code that tried to randomise starting PoW position. The problem wasn't caused by the starting position but by an int being interpreted as a string. Fixed in upstream and merged. --- src/openclpow.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/openclpow.py b/src/openclpow.py index bd9dad3b..83d7513f 100644 --- a/src/openclpow.py +++ b/src/openclpow.py @@ -43,19 +43,14 @@ def do_opencl_pow(hash, target): kernel.set_arg(1, dest_buf) start = time.time() - #startpos = random.getrandbits(32) << 32 | random.getrandbits(32) - #startpos = random.getrandbits(32) - startpos = 0 progress = 0 globamt = worksize*2000 while output[0][0] == 0: - kernel.set_arg(2, pack(" Date: Sat, 20 Jun 2015 10:20:24 +0200 Subject: [PATCH 010/210] Explain why this fork --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 7a161d04..31db438d 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,14 @@ pseudo-mailing list: BM-2D9QKN4teYRvoq2fyzpiftPh9WP9qggtzh +This fork +--------- + +The purpose of this fork is to add features for server deployment for a +bitmessage/email gateway. It contains merged support for OpenCL PoW and a +couple of new/modified API calls. + + References ---------- * [Project Website](https://bitmessage.org) From cfae964761801b85840f4733c0ba4be4ac7fe05c Mon Sep 17 00:00:00 2001 From: lightrabbit Date: Sat, 22 Aug 2015 16:48:49 +0800 Subject: [PATCH 011/210] Add UPnP support. --- src/bitmessagemain.py | 6 +- src/shared.py | 4 +- src/upnp.py | 198 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 206 insertions(+), 2 deletions(-) create mode 100644 src/upnp.py diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index bf1d74f2..daae1af9 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2.7 +#!/usr/bin/env python2.7 # Copyright (c) 2012 Jonathan Warren # Copyright (c) 2012 The Bitmessage developers # Distributed under the MIT/X11 software license. See the accompanying @@ -44,6 +44,7 @@ from debug import logger # Helper Functions import helper_bootstrap import helper_generic +import upnp def connectToStream(streamNumber): @@ -145,6 +146,9 @@ class Main: # is the application already running? If yes then exit. thisapp = singleton.singleinstance() + import upnp + upnp.createPortMapping() + # get curses flag curses = False if '-c' in sys.argv: diff --git a/src/shared.py b/src/shared.py index d82f00a5..5351947f 100644 --- a/src/shared.py +++ b/src/shared.py @@ -1,4 +1,4 @@ -from __future__ import division +from __future__ import division softwareVersion = '0.4.4' verbose = 1 @@ -370,6 +370,8 @@ def doCleanShutdown(): 'Flushing inventory in memory out to disk. This should normally only take a second...')) flushInventory() + import upnp + upnp.deletePortMapping() # Verify that the objectProcessor has finished exiting. It should have incremented the # shutdown variable from 1 to 2. This must finish before we command the sqlThread to exit. while shutdown == 1: diff --git a/src/upnp.py b/src/upnp.py new file mode 100644 index 00000000..5104ea34 --- /dev/null +++ b/src/upnp.py @@ -0,0 +1,198 @@ +# A simple upnp module to forward port for BitMessage +# Reference: http://mattscodecave.com/posts/using-python-and-upnp-to-forward-a-port +import socket +import httplib +from shared import config + +routers = [] + +def searchRouter(): + SSDP_ADDR = "239.255.255.250" + SSDP_PORT = 1900 + SSDP_MX = 2 + SSDP_ST = "urn:schemas-upnp-org:device:InternetGatewayDevice:1" + + ssdpRequest = "M-SEARCH * HTTP/1.1\r\n" + \ + "HOST: %s:%d\r\n" % (SSDP_ADDR, SSDP_PORT) + \ + "MAN: \"ssdp:discover\"\r\n" + \ + "MX: %d\r\n" % (SSDP_MX, ) + \ + "ST: %s\r\n" % (SSDP_ST, ) + "\r\n" + + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + sock.sendto(ssdpRequest, (SSDP_ADDR, SSDP_PORT)) + routers = [] + sock.settimeout(0.5) + try: + resp,(ip,port) = sock.recvfrom(1000) + while resp: + routers.append(Router(resp, ip)) + resp,(ip,port) = sock.recvfrom(1000) + except:pass + + return routers + +def createRequestXML(service, action, arguments=[]): + from xml.dom.minidom import Document + + doc = Document() + + # create the envelope element and set its attributes + envelope = doc.createElementNS('', 's:Envelope') + envelope.setAttribute('xmlns:s', 'http://schemas.xmlsoap.org/soap/envelope/') + envelope.setAttribute('s:encodingStyle', 'http://schemas.xmlsoap.org/soap/encoding/') + + # create the body element + body = doc.createElementNS('', 's:Body') + + # create the function element and set its attribute + fn = doc.createElementNS('', 'u:%s' % action) + fn.setAttribute('xmlns:u', 'urn:schemas-upnp-org:service:%s' % service) + + # setup the argument element names and values + # using a list of tuples to preserve order + + # container for created nodes + argument_list = [] + + # iterate over arguments, create nodes, create text nodes, + # append text nodes to nodes, and finally add the ready product + # to argument_list + for k, v in arguments: + tmp_node = doc.createElement(k) + tmp_text_node = doc.createTextNode(v) + tmp_node.appendChild(tmp_text_node) + argument_list.append(tmp_node) + + # append the prepared argument nodes to the function element + for arg in argument_list: + fn.appendChild(arg) + + # append function element to the body element + body.appendChild(fn) + + # append body element to envelope element + envelope.appendChild(body) + + # append envelope element to document, making it the root element + doc.appendChild(envelope) + + # our tree is ready, conver it to a string + return doc.toxml() + +class UPnPError(Exception): + def __init__(self, message): + self.message + +class Router: + name = "" + path = "" + address = None + routerPath = None + def __init__(self, ssdpResponse, address): + import urllib2 + from xml.dom.minidom import parseString + from urlparse import urlparse + + self.address = address + + row = ssdpResponse.split('\r\n') + header = {} + for i in range(1, len(row)): + part = row[i].split(': ') + if len(part) == 2: + header[part[0].lower()] = part[1] + + self.routerPath = urlparse(header['location']) + + # get the profile xml file and read it into a variable + directory = urllib2.urlopen(header['location']).read() + + # create a DOM object that represents the `directory` document + dom = parseString(directory) + + self.name = dom.getElementsByTagName('friendlyName')[0].childNodes[0].data + # find all 'serviceType' elements + service_types = dom.getElementsByTagName('serviceType') + + for service in service_types: + if service.childNodes[0].data.find('WANIPConnection') > 0: + self.path = service.parentNode.getElementsByTagName('controlURL')[0].childNodes[0].data + + def AddPortMapping(self, externalPort, internalPort, internalClient, protocol, description, leaseDuration = 0, enabled = 1): + resp = self.soapRequest('WANIPConnection:1', 'AddPortMapping', [ + ('NewExternalPort', str(externalPort)), + ('NewProtocol', protocol), + ('NewInternalPort', str(internalPort)), + ('NewInternalClient', internalClient), + ('NewEnabled', str(enabled)), + ('NewPortMappingDescription', str(description)), + ('NewLeaseDuration', str(leaseDuration)) + ]) + return resp + + def DeletePortMapping(self, externalPort, protocol): + resp = self.soapRequest('WANIPConnection:1', 'DeletePortMapping', [ + ('NewExternalPort', str(externalPort)), + ('NewProtocol', protocol), + ]) + return resp + + def GetExternalIPAddress(self): + from xml.dom.minidom import parseString + resp = self.soapRequest('WANIPConnection:1', 'GetExternalIPAddress') + dom = parseString(resp) + return dom.getElementsByTagName('NewExternalIPAddress')[0].childNodes[0].data + + def soapRequest(self, service, action, arguments=[]): + from xml.dom.minidom import parseString + conn = httplib.HTTPConnection(self.routerPath.hostname, self.routerPath.port) + conn.request( + 'POST', + self.path, + createRequestXML(service, action, arguments), + { + 'SOAPAction': '"urn:schemas-upnp-org:service:%s#%s"' % (service, action), + 'Content-Type': 'text/xml' + } + ) + resp = conn.getresponse().read() + dom = parseString(resp) + errinfo = dom.getElementsByTagName('errorDescription') + if len(errinfo) > 0: + raise UPnPError(errinfo[0].childNodes[0].data) + return resp + + +def createPortMapping(): + from struct import unpack, pack + global routers + routers = searchRouter() + localIPs = socket.gethostbyname_ex(socket.gethostname())[2] + + for i in range(len(localIPs)): + localIPs[i], = unpack('>I', socket.inet_aton(localIPs[i])) + try: + #add port mapping for each router + for router in routers: + routerIP, = unpack('>I', socket.inet_aton(router.address)) + localIP = None + minDiff = 0xFFFFFFFF + #find nearest localIP as clientIP to specified router + for IP in localIPs: + if IP ^ routerIP < minDiff: + minDiff = IP ^ routerIP + localIP = IP + + localIP = socket.inet_ntoa(pack('>I', localIP)) + localPort = config.getint('bitmessagesettings', 'port') + router.AddPortMapping(localPort, localPort, localIP, 'TCP', 'BitMessage') + except UPnPError: + from random import randint + newPort = str(randint(32767, 65535)) + config.set('bitmessagesettings', 'port', newPort) + createPortMapping() + +def deletePortMapping(): + localPort = config.getint('bitmessagesettings', 'port') + for router in routers: + router.DeletePortMapping(localPort, 'TCP') From a2cd3cbfc2394df7297fc8fa0a05fea5a9a50f1f Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 28 Sep 2015 20:22:54 +0200 Subject: [PATCH 012/210] Add: debugging for freezing api call --- src/api.py | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/api.py b/src/api.py index 70e678c9..65075c8a 100644 --- a/src/api.py +++ b/src/api.py @@ -522,15 +522,24 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): data += ']}' return data elif method == 'getAllSentMessages': - queryreturn = sqlQuery('''SELECT msgid, toaddress, fromaddress, subject, lastactiontime, message, encodingtype, status, ackdata FROM sent where folder='sent' ORDER BY lastactiontime''') + import pprint + try: + queryreturn = sqlQuery('''SELECT msgid, toaddress, fromaddress, subject, lastactiontime, message, encodingtype, status, ackdata FROM sent where folder='sent' ORDER BY lastactiontime''') + except: + print "Exception in getallSentMessages" + pprint.pprint (queryreturn) data = '{"sentMessages":[' - for row in queryreturn: - msgid, toAddress, fromAddress, subject, lastactiontime, message, encodingtype, status, ackdata = row - subject = shared.fixPotentiallyInvalidUTF8Data(subject) - message = shared.fixPotentiallyInvalidUTF8Data(message) - if len(data) > 25: - data += ',' - data += json.dumps({'msgid':msgid.encode('hex'), 'toAddress':toAddress, 'fromAddress':fromAddress, 'subject':subject.encode('base64'), 'message':message.encode('base64'), 'encodingType':encodingtype, 'lastActionTime':lastactiontime, 'status':status, 'ackData':ackdata.encode('hex')}, indent=4, separators=(',', ': ')) + if type(queryreturn) is list: + for row in queryreturn: + msgid, toAddress, fromAddress, subject, lastactiontime, message, encodingtype, status, ackdata = row + subject = shared.fixPotentiallyInvalidUTF8Data(subject) + message = shared.fixPotentiallyInvalidUTF8Data(message) + if len(data) > 25: + data += ',' + data += json.dumps({'msgid':msgid.encode('hex'), 'toAddress':toAddress, 'fromAddress':fromAddress, 'subject':subject.encode('base64'), 'message':message.encode('base64'), 'encodingType':encodingtype, 'lastActionTime':lastactiontime, 'status':status, 'ackData':ackdata.encode('hex')}, indent=4, separators=(',', ': ')) + else: + print "queryreturn is not a list" + pprint.pprint (queryreturn) data += ']}' return data elif method == 'getAllSentMessageIds' or method == 'getAllSentMessageIDs': From a5c99ad0eef126b886cf29866f9cc059e95f33d0 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 28 Sep 2015 20:55:40 +0200 Subject: [PATCH 013/210] Add: re-merge API changes API handler was rewritten in upstream, previous merge overwrote the changse. This re-enables them. --- src/api.py | 1044 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1044 insertions(+) create mode 100644 src/api.py diff --git a/src/api.py b/src/api.py new file mode 100644 index 00000000..20568ffc --- /dev/null +++ b/src/api.py @@ -0,0 +1,1044 @@ +# Copyright (c) 2012-2014 Jonathan Warren +# Copyright (c) 2012-2014 The Bitmessage developers + +comment= """ +This is not what you run to run the Bitmessage API. Instead, enable the API +( https://bitmessage.org/wiki/API ) and optionally enable daemon mode +( https://bitmessage.org/wiki/Daemon ) then run bitmessagemain.py. +""" + +if __name__ == "__main__": + print comment + import sys + sys.exit(0) + +from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler +import json + +import shared +import time +from addresses import decodeAddress,addBMIfNotPresent,decodeVarint,calculateInventoryHash,varintDecodeError +import helper_inbox +import helper_sent +import hashlib + +from pyelliptic.openssl import OpenSSL +from struct import pack + +# Classes +from helper_sql import sqlQuery,sqlExecute,SqlBulkExecute,sqlStoredProcedure +from debug import logger + +# Helper Functions +import proofofwork + +str_chan = '[chan]' + + +class APIError(Exception): + def __init__(self, error_number, error_message): + super(APIError, self).__init__() + self.error_number = error_number + self.error_message = error_message + def __str__(self): + return "API Error %04i: %s" % (self.error_number, self.error_message) + +# This is one of several classes that constitute the API +# This class was written by Vaibhav Bhatia. Modified by Jonathan Warren (Atheros). +# http://code.activestate.com/recipes/501148-xmlrpc-serverclient-which-does-cookie-handling-and/ +class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): + + def do_POST(self): + # Handles the HTTP POST request. + # Attempts to interpret all HTTP POST requests as XML-RPC calls, + # which are forwarded to the server's _dispatch method for handling. + + # Note: this method is the same as in SimpleXMLRPCRequestHandler, + # just hacked to handle cookies + + # Check that the path is legal + if not self.is_rpc_path_valid(): + self.report_404() + return + + try: + # Get arguments by reading body of request. + # We read this in chunks to avoid straining + # socket.read(); around the 10 or 15Mb mark, some platforms + # begin to have problems (bug #792570). + max_chunk_size = 10 * 1024 * 1024 + size_remaining = int(self.headers["content-length"]) + L = [] + while size_remaining: + chunk_size = min(size_remaining, max_chunk_size) + L.append(self.rfile.read(chunk_size)) + size_remaining -= len(L[-1]) + data = ''.join(L) + + # In previous versions of SimpleXMLRPCServer, _dispatch + # could be overridden in this class, instead of in + # SimpleXMLRPCDispatcher. To maintain backwards compatibility, + # check to see if a subclass implements _dispatch and dispatch + # using that method if present. + response = self.server._marshaled_dispatch( + data, getattr(self, '_dispatch', None) + ) + except: # This should only happen if the module is buggy + # internal error, report as HTTP server error + self.send_response(500) + self.end_headers() + else: + # got a valid XML RPC response + self.send_response(200) + self.send_header("Content-type", "text/xml") + self.send_header("Content-length", str(len(response))) + + # HACK :start -> sends cookies here + if self.cookies: + for cookie in self.cookies: + self.send_header('Set-Cookie', cookie.output(header='')) + # HACK :end + + self.end_headers() + self.wfile.write(response) + + # shut down the connection + self.wfile.flush() + self.connection.shutdown(1) + + def APIAuthenticateClient(self): + if 'Authorization' in self.headers: + # handle Basic authentication + (enctype, encstr) = self.headers.get('Authorization').split() + (emailid, password) = encstr.decode('base64').split(':') + if emailid == shared.config.get('bitmessagesettings', 'apiusername') and password == shared.config.get('bitmessagesettings', 'apipassword'): + return True + else: + return False + else: + logger.warn('Authentication failed because header lacks Authentication field') + time.sleep(2) + return False + + return False + + def _decode(self, text, decode_type): + try: + return text.decode(decode_type) + except Exception as e: + raise APIError(22, "Decode error - " + str(e) + ". Had trouble while decoding string: " + repr(text)) + + def _verifyAddress(self, address): + status, addressVersionNumber, streamNumber, ripe = decodeAddress(address) + if status != 'success': + logger.warn('API Error 0007: Could not decode address %s. Status: %s.', address, status) + + if status == 'checksumfailed': + raise APIError(8, 'Checksum failed for address: ' + address) + if status == 'invalidcharacters': + raise APIError(9, 'Invalid characters in address: ' + address) + if status == 'versiontoohigh': + raise APIError(10, 'Address version number too high (or zero) in address: ' + address) + if status == 'varintmalformed': + raise APIError(26, 'Malformed varint in address: ' + address) + raise APIError(7, 'Could not decode address: ' + address + ' : ' + status) + if addressVersionNumber < 2 or addressVersionNumber > 4: + raise APIError(11, 'The address version number currently must be 2, 3 or 4. Others aren\'t supported. Check the address.') + if streamNumber != 1: + raise APIError(12, 'The stream number must be 1. Others aren\'t supported. Check the address.') + + return (status, addressVersionNumber, streamNumber, ripe) + + + #Request Handlers + + def HandleListAddresses(self, method): + data = '{"addresses":[' + configSections = shared.config.sections() + for addressInKeysFile in configSections: + if addressInKeysFile != 'bitmessagesettings': + status, addressVersionNumber, streamNumber, hash01 = decodeAddress( + addressInKeysFile) + if len(data) > 20: + data += ',' + if shared.config.has_option(addressInKeysFile, 'chan'): + chan = shared.config.getboolean(addressInKeysFile, 'chan') + else: + chan = False + label = shared.config.get(addressInKeysFile, 'label') + if method == 'listAddresses2': + label = label.encode('base64') + data += json.dumps({'label': label, 'address': addressInKeysFile, 'stream': + streamNumber, 'enabled': shared.config.getboolean(addressInKeysFile, 'enabled'), 'chan': chan}, indent=4, separators=(',', ': ')) + data += ']}' + return data + + def HandleListAddressBookEntries(self, params): + if len(params) == 1: + label, = params + label = self._decode(label, "base64") + queryreturn = sqlQuery('''SELECT label, address from addressbook WHERE label = ?''', label) + elif len(params) > 1: + raise APIError(0, "Too many paremeters, max 1") + else: + queryreturn = sqlQuery('''SELECT label, address from addressbook''') + data = '{"addresses":[' + for row in queryreturn: + label, address = row + label = shared.fixPotentiallyInvalidUTF8Data(label) + if len(data) > 20: + data += ',' + data += json.dumps({'label':label.encode('base64'), 'address': address}, indent=4, separators=(',', ': ')) + data += ']}' + return data + + def HandleAddAddressBookEntry(self, params): + if len(params) != 2: + raise APIError(0, "I need label and address") + address, label = params + label = self._decode(label, "base64") + address = addBMIfNotPresent(address) + self._verifyAddress(address) + queryreturn = sqlQuery("SELECT address FROM addressbook WHERE address=?", address) + if queryreturn != []: + raise APIError(16, 'You already have this address in your address book.') + + sqlExecute("INSERT INTO addressbook VALUES(?,?)", label, address) + shared.UISignalQueue.put(('rerenderInboxFromLabels','')) + shared.UISignalQueue.put(('rerenderSentToLabels','')) + shared.UISignalQueue.put(('rerenderAddressBook','')) + return "Added address %s to address book" % address + + def HandleDeleteAddressBookEntry(self, params): + if len(params) != 1: + raise APIError(0, "I need an address") + address, = params + address = addBMIfNotPresent(address) + self._verifyAddress(address) + sqlExecute('DELETE FROM addressbook WHERE address=?', address) + shared.UISignalQueue.put(('rerenderInboxFromLabels','')) + shared.UISignalQueue.put(('rerenderSentToLabels','')) + shared.UISignalQueue.put(('rerenderAddressBook','')) + return "Deleted address book entry for %s if it existed" % address + + def HandleCreateRandomAddress(self, params): + if len(params) == 0: + raise APIError(0, 'I need parameters!') + elif len(params) == 1: + label, = params + eighteenByteRipe = False + nonceTrialsPerByte = shared.config.get( + 'bitmessagesettings', 'defaultnoncetrialsperbyte') + payloadLengthExtraBytes = shared.config.get( + 'bitmessagesettings', 'defaultpayloadlengthextrabytes') + elif len(params) == 2: + label, eighteenByteRipe = params + nonceTrialsPerByte = shared.config.get( + 'bitmessagesettings', 'defaultnoncetrialsperbyte') + payloadLengthExtraBytes = shared.config.get( + 'bitmessagesettings', 'defaultpayloadlengthextrabytes') + elif len(params) == 3: + label, eighteenByteRipe, totalDifficulty = params + nonceTrialsPerByte = int( + shared.networkDefaultProofOfWorkNonceTrialsPerByte * totalDifficulty) + payloadLengthExtraBytes = shared.config.get( + 'bitmessagesettings', 'defaultpayloadlengthextrabytes') + elif len(params) == 4: + label, eighteenByteRipe, totalDifficulty, smallMessageDifficulty = params + nonceTrialsPerByte = int( + shared.networkDefaultProofOfWorkNonceTrialsPerByte * totalDifficulty) + payloadLengthExtraBytes = int( + shared.networkDefaultPayloadLengthExtraBytes * smallMessageDifficulty) + else: + raise APIError(0, 'Too many parameters!') + label = self._decode(label, "base64") + try: + unicode(label, 'utf-8') + except: + raise APIError(17, 'Label is not valid UTF-8 data.') + shared.apiAddressGeneratorReturnQueue.queue.clear() + streamNumberForAddress = 1 + shared.addressGeneratorQueue.put(( + 'createRandomAddress', 4, streamNumberForAddress, label, 1, "", eighteenByteRipe, nonceTrialsPerByte, payloadLengthExtraBytes)) + return shared.apiAddressGeneratorReturnQueue.get() + + def HandleCreateDeterministicAddresses(self, params): + if len(params) == 0: + raise APIError(0, 'I need parameters!') + elif len(params) == 1: + passphrase, = params + numberOfAddresses = 1 + addressVersionNumber = 0 + streamNumber = 0 + eighteenByteRipe = False + nonceTrialsPerByte = shared.config.get( + 'bitmessagesettings', 'defaultnoncetrialsperbyte') + payloadLengthExtraBytes = shared.config.get( + 'bitmessagesettings', 'defaultpayloadlengthextrabytes') + elif len(params) == 2: + passphrase, numberOfAddresses = params + addressVersionNumber = 0 + streamNumber = 0 + eighteenByteRipe = False + nonceTrialsPerByte = shared.config.get( + 'bitmessagesettings', 'defaultnoncetrialsperbyte') + payloadLengthExtraBytes = shared.config.get( + 'bitmessagesettings', 'defaultpayloadlengthextrabytes') + elif len(params) == 3: + passphrase, numberOfAddresses, addressVersionNumber = params + streamNumber = 0 + eighteenByteRipe = False + nonceTrialsPerByte = shared.config.get( + 'bitmessagesettings', 'defaultnoncetrialsperbyte') + payloadLengthExtraBytes = shared.config.get( + 'bitmessagesettings', 'defaultpayloadlengthextrabytes') + elif len(params) == 4: + passphrase, numberOfAddresses, addressVersionNumber, streamNumber = params + eighteenByteRipe = False + nonceTrialsPerByte = shared.config.get( + 'bitmessagesettings', 'defaultnoncetrialsperbyte') + payloadLengthExtraBytes = shared.config.get( + 'bitmessagesettings', 'defaultpayloadlengthextrabytes') + elif len(params) == 5: + passphrase, numberOfAddresses, addressVersionNumber, streamNumber, eighteenByteRipe = params + nonceTrialsPerByte = shared.config.get( + 'bitmessagesettings', 'defaultnoncetrialsperbyte') + payloadLengthExtraBytes = shared.config.get( + 'bitmessagesettings', 'defaultpayloadlengthextrabytes') + elif len(params) == 6: + passphrase, numberOfAddresses, addressVersionNumber, streamNumber, eighteenByteRipe, totalDifficulty = params + nonceTrialsPerByte = int( + shared.networkDefaultProofOfWorkNonceTrialsPerByte * totalDifficulty) + payloadLengthExtraBytes = shared.config.get( + 'bitmessagesettings', 'defaultpayloadlengthextrabytes') + elif len(params) == 7: + passphrase, numberOfAddresses, addressVersionNumber, streamNumber, eighteenByteRipe, totalDifficulty, smallMessageDifficulty = params + nonceTrialsPerByte = int( + shared.networkDefaultProofOfWorkNonceTrialsPerByte * totalDifficulty) + payloadLengthExtraBytes = int( + shared.networkDefaultPayloadLengthExtraBytes * smallMessageDifficulty) + else: + raise APIError(0, 'Too many parameters!') + if len(passphrase) == 0: + raise APIError(1, 'The specified passphrase is blank.') + if not isinstance(eighteenByteRipe, bool): + raise APIError(23, 'Bool expected in eighteenByteRipe, saw %s instead' % type(eighteenByteRipe)) + passphrase = self._decode(passphrase, "base64") + if addressVersionNumber == 0: # 0 means "just use the proper addressVersionNumber" + addressVersionNumber = 4 + if addressVersionNumber != 3 and addressVersionNumber != 4: + raise APIError(2,'The address version number currently must be 3, 4, or 0 (which means auto-select). ' + addressVersionNumber + ' isn\'t supported.') + if streamNumber == 0: # 0 means "just use the most available stream" + streamNumber = 1 + if streamNumber != 1: + raise APIError(3,'The stream number must be 1 (or 0 which means auto-select). Others aren\'t supported.') + if numberOfAddresses == 0: + raise APIError(4, 'Why would you ask me to generate 0 addresses for you?') + if numberOfAddresses > 999: + raise APIError(5, 'You have (accidentally?) specified too many addresses to make. Maximum 999. This check only exists to prevent mischief; if you really want to create more addresses than this, contact the Bitmessage developers and we can modify the check or you can do it yourself by searching the source code for this message.') + shared.apiAddressGeneratorReturnQueue.queue.clear() + logger.debug('Requesting that the addressGenerator create %s addresses.', numberOfAddresses) + shared.addressGeneratorQueue.put( + ('createDeterministicAddresses', addressVersionNumber, streamNumber, + 'unused API address', numberOfAddresses, passphrase, eighteenByteRipe, nonceTrialsPerByte, payloadLengthExtraBytes)) + data = '{"addresses":[' + queueReturn = shared.apiAddressGeneratorReturnQueue.get() + for item in queueReturn: + if len(data) > 20: + data += ',' + data += "\"" + item + "\"" + data += ']}' + return data + + def HandleGetDeterministicAddress(self, params): + if len(params) != 3: + raise APIError(0, 'I need exactly 3 parameters.') + passphrase, addressVersionNumber, streamNumber = params + numberOfAddresses = 1 + eighteenByteRipe = False + if len(passphrase) == 0: + raise APIError(1, 'The specified passphrase is blank.') + passphrase = self._decode(passphrase, "base64") + if addressVersionNumber != 3 and addressVersionNumber != 4: + raise APIError(2, 'The address version number currently must be 3 or 4. ' + addressVersionNumber + ' isn\'t supported.') + if streamNumber != 1: + raise APIError(3, ' The stream number must be 1. Others aren\'t supported.') + shared.apiAddressGeneratorReturnQueue.queue.clear() + logger.debug('Requesting that the addressGenerator create %s addresses.', numberOfAddresses) + shared.addressGeneratorQueue.put( + ('getDeterministicAddress', addressVersionNumber, + streamNumber, 'unused API address', numberOfAddresses, passphrase, eighteenByteRipe)) + return shared.apiAddressGeneratorReturnQueue.get() + + def HandleCreateChan(self, params): + if len(params) == 0: + raise APIError(0, 'I need parameters.') + elif len(params) == 1: + passphrase, = params + passphrase = self._decode(passphrase, "base64") + if len(passphrase) == 0: + raise APIError(1, 'The specified passphrase is blank.') + # It would be nice to make the label the passphrase but it is + # possible that the passphrase contains non-utf-8 characters. + try: + unicode(passphrase, 'utf-8') + label = str_chan + ' ' + passphrase + except: + label = str_chan + ' ' + repr(passphrase) + + addressVersionNumber = 4 + streamNumber = 1 + shared.apiAddressGeneratorReturnQueue.queue.clear() + logger.debug('Requesting that the addressGenerator create chan %s.', passphrase) + shared.addressGeneratorQueue.put(('createChan', addressVersionNumber, streamNumber, label, passphrase)) + queueReturn = shared.apiAddressGeneratorReturnQueue.get() + if len(queueReturn) == 0: + raise APIError(24, 'Chan address is already present.') + address = queueReturn[0] + return address + + def HandleJoinChan(self, params): + if len(params) < 2: + raise APIError(0, 'I need two parameters.') + elif len(params) == 2: + passphrase, suppliedAddress= params + passphrase = self._decode(passphrase, "base64") + if len(passphrase) == 0: + raise APIError(1, 'The specified passphrase is blank.') + # It would be nice to make the label the passphrase but it is + # possible that the passphrase contains non-utf-8 characters. + try: + unicode(passphrase, 'utf-8') + label = str_chan + ' ' + passphrase + except: + label = str_chan + ' ' + repr(passphrase) + + status, addressVersionNumber, streamNumber, toRipe = self._verifyAddress(suppliedAddress) + suppliedAddress = addBMIfNotPresent(suppliedAddress) + shared.apiAddressGeneratorReturnQueue.queue.clear() + shared.addressGeneratorQueue.put(('joinChan', suppliedAddress, label, passphrase)) + addressGeneratorReturnValue = shared.apiAddressGeneratorReturnQueue.get() + + if addressGeneratorReturnValue == 'chan name does not match address': + raise APIError(18, 'Chan name does not match address.') + if len(addressGeneratorReturnValue) == 0: + raise APIError(24, 'Chan address is already present.') + #TODO: this variable is not used to anything + createdAddress = addressGeneratorReturnValue[0] # in case we ever want it for anything. + return "success" + + def HandleLeaveChan(self, params): + if len(params) == 0: + raise APIError(0, 'I need parameters.') + elif len(params) == 1: + address, = params + status, addressVersionNumber, streamNumber, toRipe = self._verifyAddress(address) + address = addBMIfNotPresent(address) + if not shared.config.has_section(address): + raise APIError(13, 'Could not find this address in your keys.dat file.') + if not shared.safeConfigGetBoolean(address, 'chan'): + raise APIError(25, 'Specified address is not a chan address. Use deleteAddress API call instead.') + shared.config.remove_section(address) + with open(shared.appdata + 'keys.dat', 'wb') as configfile: + shared.config.write(configfile) + return 'success' + + def HandleDeleteAddress(self, params): + if len(params) == 0: + raise APIError(0, 'I need parameters.') + elif len(params) == 1: + address, = params + status, addressVersionNumber, streamNumber, toRipe = self._verifyAddress(address) + address = addBMIfNotPresent(address) + if not shared.config.has_section(address): + raise APIError(13, 'Could not find this address in your keys.dat file.') + shared.config.remove_section(address) + with open(shared.appdata + 'keys.dat', 'wb') as configfile: + shared.config.write(configfile) + shared.UISignalQueue.put(('rerenderInboxFromLabels','')) + shared.UISignalQueue.put(('rerenderSentToLabels','')) + shared.reloadMyAddressHashes() + return 'success' + + def HandleGetAllInboxMessages(self, params): + queryreturn = sqlQuery( + '''SELECT msgid, toaddress, fromaddress, subject, received, message, encodingtype, read FROM inbox where folder='inbox' ORDER BY received''') + data = '{"inboxMessages":[' + for row in queryreturn: + msgid, toAddress, fromAddress, subject, received, message, encodingtype, read = row + subject = shared.fixPotentiallyInvalidUTF8Data(subject) + message = shared.fixPotentiallyInvalidUTF8Data(message) + if len(data) > 25: + data += ',' + data += json.dumps({'msgid': msgid.encode('hex'), 'toAddress': toAddress, 'fromAddress': fromAddress, 'subject': subject.encode( + 'base64'), 'message': message.encode('base64'), 'encodingType': encodingtype, 'receivedTime': received, 'read': read}, indent=4, separators=(',', ': ')) + data += ']}' + return data + + def HandleGetAllInboxMessageIds(self, params): + queryreturn = sqlQuery( + '''SELECT msgid FROM inbox where folder='inbox' ORDER BY received''') + data = '{"inboxMessageIds":[' + for row in queryreturn: + msgid = row[0] + if len(data) > 25: + data += ',' + data += json.dumps({'msgid': msgid.encode('hex')}, indent=4, separators=(',', ': ')) + data += ']}' + return data + + def HandleGetInboxMessageById(self, params): + if len(params) == 0: + raise APIError(0, 'I need parameters!') + elif len(params) == 1: + msgid = self._decode(params[0], "hex") + elif len(params) >= 2: + msgid = self._decode(params[0], "hex") + readStatus = params[1] + if not isinstance(readStatus, bool): + raise APIError(23, 'Bool expected in readStatus, saw %s instead.' % type(readStatus)) + queryreturn = sqlQuery('''SELECT read FROM inbox WHERE msgid=?''', msgid) + # UPDATE is slow, only update if status is different + if queryreturn != [] and (queryreturn[0][0] == 1) != readStatus: + sqlExecute('''UPDATE inbox set read = ? WHERE msgid=?''', readStatus, msgid) + shared.UISignalQueue.put(('changedInboxUnread', None)) + queryreturn = sqlQuery('''SELECT msgid, toaddress, fromaddress, subject, received, message, encodingtype, read FROM inbox WHERE msgid=?''', msgid) + data = '{"inboxMessage":[' + for row in queryreturn: + msgid, toAddress, fromAddress, subject, received, message, encodingtype, read = row + subject = shared.fixPotentiallyInvalidUTF8Data(subject) + message = shared.fixPotentiallyInvalidUTF8Data(message) + data += json.dumps({'msgid':msgid.encode('hex'), 'toAddress':toAddress, 'fromAddress':fromAddress, 'subject':subject.encode('base64'), 'message':message.encode('base64'), 'encodingType':encodingtype, 'receivedTime':received, 'read': read}, indent=4, separators=(',', ': ')) + data += ']}' + return data + + def HandleGetAllSentMessages(self, params): + queryreturn = sqlQuery('''SELECT msgid, toaddress, fromaddress, subject, lastactiontime, message, encodingtype, status, ackdata FROM sent where folder='sent' ORDER BY lastactiontime''') + data = '{"sentMessages":[' + for row in queryreturn: + msgid, toAddress, fromAddress, subject, lastactiontime, message, encodingtype, status, ackdata = row + subject = shared.fixPotentiallyInvalidUTF8Data(subject) + message = shared.fixPotentiallyInvalidUTF8Data(message) + if len(data) > 25: + data += ',' + data += json.dumps({'msgid':msgid.encode('hex'), 'toAddress':toAddress, 'fromAddress':fromAddress, 'subject':subject.encode('base64'), 'message':message.encode('base64'), 'encodingType':encodingtype, 'lastActionTime':lastactiontime, 'status':status, 'ackData':ackdata.encode('hex')}, indent=4, separators=(',', ': ')) + data += ']}' + return data + + def HandleGetAllSentMessageIds(self, params): + queryreturn = sqlQuery('''SELECT msgid FROM sent where folder='sent' ORDER BY lastactiontime''') + data = '{"sentMessageIds":[' + for row in queryreturn: + msgid = row[0] + if len(data) > 25: + data += ',' + data += json.dumps({'msgid':msgid.encode('hex')}, indent=4, separators=(',', ': ')) + data += ']}' + return data + + def HandleInboxMessagesByReceiver(self, params): + if len(params) == 0: + raise APIError(0, 'I need parameters!') + toAddress = params[0] + queryreturn = sqlQuery('''SELECT msgid, toaddress, fromaddress, subject, received, message, encodingtype FROM inbox WHERE folder='inbox' AND toAddress=?''', toAddress) + data = '{"inboxMessages":[' + for row in queryreturn: + msgid, toAddress, fromAddress, subject, received, message, encodingtype = row + subject = shared.fixPotentiallyInvalidUTF8Data(subject) + message = shared.fixPotentiallyInvalidUTF8Data(message) + if len(data) > 25: + data += ',' + data += json.dumps({'msgid':msgid.encode('hex'), 'toAddress':toAddress, 'fromAddress':fromAddress, 'subject':subject.encode('base64'), 'message':message.encode('base64'), 'encodingType':encodingtype, 'receivedTime':received}, indent=4, separators=(',', ': ')) + data += ']}' + return data + + def HandleGetSentMessageById(self, params): + if len(params) == 0: + raise APIError(0, 'I need parameters!') + msgid = self._decode(params[0], "hex") + queryreturn = sqlQuery('''SELECT msgid, toaddress, fromaddress, subject, lastactiontime, message, encodingtype, status, ackdata FROM sent WHERE msgid=?''', msgid) + data = '{"sentMessage":[' + for row in queryreturn: + msgid, toAddress, fromAddress, subject, lastactiontime, message, encodingtype, status, ackdata = row + subject = shared.fixPotentiallyInvalidUTF8Data(subject) + message = shared.fixPotentiallyInvalidUTF8Data(message) + data += json.dumps({'msgid':msgid.encode('hex'), 'toAddress':toAddress, 'fromAddress':fromAddress, 'subject':subject.encode('base64'), 'message':message.encode('base64'), 'encodingType':encodingtype, 'lastActionTime':lastactiontime, 'status':status, 'ackData':ackdata.encode('hex')}, indent=4, separators=(',', ': ')) + data += ']}' + return data + + def HandleGetSentMessagesByAddress(self, params): + if len(params) == 0: + raise APIError(0, 'I need parameters!') + fromAddress = params[0] + queryreturn = sqlQuery('''SELECT msgid, toaddress, fromaddress, subject, lastactiontime, message, encodingtype, status, ackdata FROM sent WHERE folder='sent' AND fromAddress=? ORDER BY lastactiontime''', + fromAddress) + data = '{"sentMessages":[' + for row in queryreturn: + msgid, toAddress, fromAddress, subject, lastactiontime, message, encodingtype, status, ackdata = row + subject = shared.fixPotentiallyInvalidUTF8Data(subject) + message = shared.fixPotentiallyInvalidUTF8Data(message) + if len(data) > 25: + data += ',' + data += json.dumps({'msgid':msgid.encode('hex'), 'toAddress':toAddress, 'fromAddress':fromAddress, 'subject':subject.encode('base64'), 'message':message.encode('base64'), 'encodingType':encodingtype, 'lastActionTime':lastactiontime, 'status':status, 'ackData':ackdata.encode('hex')}, indent=4, separators=(',', ': ')) + data += ']}' + return data + + def HandleGetSentMessagesByAckData(self, params): + if len(params) == 0: + raise APIError(0, 'I need parameters!') + ackData = self._decode(params[0], "hex") + queryreturn = sqlQuery('''SELECT msgid, toaddress, fromaddress, subject, lastactiontime, message, encodingtype, status, ackdata FROM sent WHERE ackdata=?''', + ackData) + data = '{"sentMessage":[' + for row in queryreturn: + msgid, toAddress, fromAddress, subject, lastactiontime, message, encodingtype, status, ackdata = row + subject = shared.fixPotentiallyInvalidUTF8Data(subject) + message = shared.fixPotentiallyInvalidUTF8Data(message) + data += json.dumps({'msgid':msgid.encode('hex'), 'toAddress':toAddress, 'fromAddress':fromAddress, 'subject':subject.encode('base64'), 'message':message.encode('base64'), 'encodingType':encodingtype, 'lastActionTime':lastactiontime, 'status':status, 'ackData':ackdata.encode('hex')}, indent=4, separators=(',', ': ')) + data += ']}' + return data + + def HandleTrashMessage(self, params): + if len(params) == 0: + raise APIError(0, 'I need parameters!') + msgid = self._decode(params[0], "hex") + + # Trash if in inbox table + helper_inbox.trash(msgid) + # Trash if in sent table + sqlExecute('''UPDATE sent SET folder='trash' WHERE msgid=?''', msgid) + return 'Trashed message (assuming message existed).' + + def HandleTrashInboxMessage(self, params): + if len(params) == 0: + raise APIError(0, 'I need parameters!') + msgid = self._decode(params[0], "hex") + helper_inbox.trash(msgid) + return 'Trashed inbox message (assuming message existed).' + + def HandleTrashSentMessage(self, params): + if len(params) == 0: + raise APIError(0, 'I need parameters!') + msgid = self._decode(params[0], "hex") + sqlExecute('''UPDATE sent SET folder='trash' WHERE msgid=?''', msgid) + return 'Trashed sent message (assuming message existed).' + + def HandleSendMessage(self, params): + if len(params) == 0: + raise APIError(0, 'I need parameters!') + elif len(params) == 4: + toAddress, fromAddress, subject, message = params + encodingType = 2 + TTL = 4*24*60*60 + elif len(params) == 5: + toAddress, fromAddress, subject, message, encodingType = params + TTL = 4*24*60*60 + elif len(params) == 6: + toAddress, fromAddress, subject, message, encodingType, TTL = params + if encodingType != 2: + raise APIError(6, 'The encoding type must be 2 because that is the only one this program currently supports.') + subject = self._decode(subject, "base64") + message = self._decode(message, "base64") + if len(subject + message) > (2 ** 18 - 500): + raise APIError(27, 'Message is too long.') + if TTL < 60*60: + TTL = 60*60 + if TTL > 28*24*60*60: + TTL = 28*24*60*60 + toAddress = addBMIfNotPresent(toAddress) + fromAddress = addBMIfNotPresent(fromAddress) + status, addressVersionNumber, streamNumber, toRipe = self._verifyAddress(toAddress) + self._verifyAddress(fromAddress) + try: + fromAddressEnabled = shared.config.getboolean( + fromAddress, 'enabled') + except: + raise APIError(13, 'Could not find your fromAddress in the keys.dat file.') + if not fromAddressEnabled: + raise APIError(14, 'Your fromAddress is disabled. Cannot send.') + + ackdata = OpenSSL.rand(32) + + t = ('', + toAddress, + toRipe, + fromAddress, + subject, + message, + ackdata, + int(time.time()), # sentTime (this won't change) + int(time.time()), # lastActionTime + 0, + 'msgqueued', + 0, + 'sent', + 2, + TTL) + helper_sent.insert(t) + + toLabel = '' + queryreturn = sqlQuery('''select label from addressbook where address=?''', toAddress) + if queryreturn != []: + for row in queryreturn: + toLabel, = row + # apiSignalQueue.put(('displayNewSentMessage',(toAddress,toLabel,fromAddress,subject,message,ackdata))) + shared.UISignalQueue.put(('displayNewSentMessage', ( + toAddress, toLabel, fromAddress, subject, message, ackdata))) + + shared.workerQueue.put(('sendmessage', toAddress)) + + return ackdata.encode('hex') + + def HandleSendBroadcast(self, params): + if len(params) == 0: + raise APIError(0, 'I need parameters!') + if len(params) == 3: + fromAddress, subject, message = params + encodingType = 2 + TTL = 4*24*60*60 + elif len(params) == 4: + fromAddress, subject, message, encodingType = params + TTL = 4*24*60*60 + elif len(params) == 5: + fromAddress, subject, message, encodingType, TTL = params + if encodingType != 2: + raise APIError(6, 'The encoding type must be 2 because that is the only one this program currently supports.') + subject = self._decode(subject, "base64") + message = self._decode(message, "base64") + if len(subject + message) > (2 ** 18 - 500): + raise APIError(27, 'Message is too long.') + if TTL < 60*60: + TTL = 60*60 + if TTL > 28*24*60*60: + TTL = 28*24*60*60 + fromAddress = addBMIfNotPresent(fromAddress) + self._verifyAddress(fromAddress) + try: + fromAddressEnabled = shared.config.getboolean( + fromAddress, 'enabled') + except: + raise APIError(13, 'could not find your fromAddress in the keys.dat file.') + ackdata = OpenSSL.rand(32) + toAddress = '[Broadcast subscribers]' + ripe = '' + + t = ('', + toAddress, + ripe, + fromAddress, + subject, + message, + ackdata, + int(time.time()), # sentTime (this doesn't change) + int(time.time()), # lastActionTime + 0, + 'broadcastqueued', + 0, + 'sent', + 2, + TTL) + helper_sent.insert(t) + + toLabel = '[Broadcast subscribers]' + shared.UISignalQueue.put(('displayNewSentMessage', ( + toAddress, toLabel, fromAddress, subject, message, ackdata))) + shared.workerQueue.put(('sendbroadcast', '')) + + return ackdata.encode('hex') + + def HandleGetStatus(self, params): + if len(params) != 1: + raise APIError(0, 'I need one parameter!') + ackdata, = params + if len(ackdata) != 64: + raise APIError(15, 'The length of ackData should be 32 bytes (encoded in hex thus 64 characters).') + ackdata = self._decode(ackdata, "hex") + queryreturn = sqlQuery( + '''SELECT status FROM sent where ackdata=?''', + ackdata) + if queryreturn == []: + return 'notfound' + for row in queryreturn: + status, = row + return status + + def HandleAddSubscription(self, params): + if len(params) == 0: + raise APIError(0, 'I need parameters!') + if len(params) == 1: + address, = params + label = '' + if len(params) == 2: + address, label = params + label = self._decode(label, "base64") + try: + unicode(label, 'utf-8') + except: + raise APIError(17, 'Label is not valid UTF-8 data.') + if len(params) > 2: + raise APIError(0, 'I need either 1 or 2 parameters!') + address = addBMIfNotPresent(address) + self._verifyAddress(address) + # First we must check to see if the address is already in the + # subscriptions list. + queryreturn = sqlQuery('''select * from subscriptions where address=?''', address) + if queryreturn != []: + raise APIError(16, 'You are already subscribed to that address.') + sqlExecute('''INSERT INTO subscriptions VALUES (?,?,?)''',label, address, True) + shared.reloadBroadcastSendersForWhichImWatching() + shared.UISignalQueue.put(('rerenderInboxFromLabels', '')) + shared.UISignalQueue.put(('rerenderSubscriptions', '')) + return 'Added subscription.' + + def HandleDeleteSubscription(self, params): + if len(params) != 1: + raise APIError(0, 'I need 1 parameter!') + address, = params + address = addBMIfNotPresent(address) + sqlExecute('''DELETE FROM subscriptions WHERE address=?''', address) + shared.reloadBroadcastSendersForWhichImWatching() + shared.UISignalQueue.put(('rerenderInboxFromLabels', '')) + shared.UISignalQueue.put(('rerenderSubscriptions', '')) + return 'Deleted subscription if it existed.' + + def ListSubscriptions(self, params): + queryreturn = sqlQuery('''SELECT label, address, enabled FROM subscriptions''') + data = '{"subscriptions":[' + for row in queryreturn: + label, address, enabled = row + label = shared.fixPotentiallyInvalidUTF8Data(label) + if len(data) > 20: + data += ',' + data += json.dumps({'label':label.encode('base64'), 'address': address, 'enabled': enabled == 1}, indent=4, separators=(',',': ')) + data += ']}' + return data + + def HandleDisseminatePreEncryptedMsg(self, params): + # The device issuing this command to PyBitmessage supplies a msg object that has + # already been encrypted but which still needs the POW to be done. PyBitmessage + # accepts this msg object and sends it out to the rest of the Bitmessage network + # as if it had generated the message itself. Please do not yet add this to the + # api doc. + if len(params) != 3: + raise APIError(0, 'I need 3 parameter!') + encryptedPayload, requiredAverageProofOfWorkNonceTrialsPerByte, requiredPayloadLengthExtraBytes = params + encryptedPayload = self._decode(encryptedPayload, "hex") + # Let us do the POW and attach it to the front + target = 2**64 / ((len(encryptedPayload)+requiredPayloadLengthExtraBytes+8) * requiredAverageProofOfWorkNonceTrialsPerByte) + with shared.printLock: + print '(For msg message via API) Doing proof of work. Total required difficulty:', float(requiredAverageProofOfWorkNonceTrialsPerByte) / shared.networkDefaultProofOfWorkNonceTrialsPerByte, 'Required small message difficulty:', float(requiredPayloadLengthExtraBytes) / shared.networkDefaultPayloadLengthExtraBytes + powStartTime = time.time() + initialHash = hashlib.sha512(encryptedPayload).digest() + trialValue, nonce = proofofwork.run(target, initialHash) + with shared.printLock: + print '(For msg message via API) Found proof of work', trialValue, 'Nonce:', nonce + try: + print 'POW took', int(time.time() - powStartTime), 'seconds.', nonce / (time.time() - powStartTime), 'nonce trials per second.' + except: + pass + encryptedPayload = pack('>Q', nonce) + encryptedPayload + toStreamNumber = decodeVarint(encryptedPayload[16:26])[0] + inventoryHash = calculateInventoryHash(encryptedPayload) + objectType = 2 + TTL = 2.5 * 24 * 60 * 60 + shared.inventory[inventoryHash] = ( + objectType, toStreamNumber, encryptedPayload, int(time.time()) + TTL,'') + shared.inventorySets[toStreamNumber].add(inventoryHash) + with shared.printLock: + print 'Broadcasting inv for msg(API disseminatePreEncryptedMsg command):', inventoryHash.encode('hex') + shared.broadcastToSendDataQueues(( + toStreamNumber, 'advertiseobject', inventoryHash)) + + def HandleTrashSentMessageByAckDAta(self, params): + # This API method should only be used when msgid is not available + if len(params) == 0: + raise APIError(0, 'I need parameters!') + ackdata = self._decode(params[0], "hex") + sqlExecute('''UPDATE sent SET folder='trash' WHERE ackdata=?''', ackdata) + return 'Trashed sent message (assuming message existed).' + + def HandleDissimatePubKey(self, params): + # The device issuing this command to PyBitmessage supplies a pubkey object to be + # disseminated to the rest of the Bitmessage network. PyBitmessage accepts this + # pubkey object and sends it out to the rest of the Bitmessage network as if it + # had generated the pubkey object itself. Please do not yet add this to the api + # doc. + if len(params) != 1: + raise APIError(0, 'I need 1 parameter!') + payload, = params + payload = self._decode(payload, "hex") + + # Let us do the POW + target = 2 ** 64 / ((len(payload) + shared.networkDefaultPayloadLengthExtraBytes + + 8) * shared.networkDefaultProofOfWorkNonceTrialsPerByte) + print '(For pubkey message via API) Doing proof of work...' + initialHash = hashlib.sha512(payload).digest() + trialValue, nonce = proofofwork.run(target, initialHash) + print '(For pubkey message via API) Found proof of work', trialValue, 'Nonce:', nonce + payload = pack('>Q', nonce) + payload + + pubkeyReadPosition = 8 # bypass the nonce + if payload[pubkeyReadPosition:pubkeyReadPosition+4] == '\x00\x00\x00\x00': # if this pubkey uses 8 byte time + pubkeyReadPosition += 8 + else: + pubkeyReadPosition += 4 + addressVersion, addressVersionLength = decodeVarint(payload[pubkeyReadPosition:pubkeyReadPosition+10]) + pubkeyReadPosition += addressVersionLength + pubkeyStreamNumber = decodeVarint(payload[pubkeyReadPosition:pubkeyReadPosition+10])[0] + inventoryHash = calculateInventoryHash(payload) + objectType = 1 + #todo: support v4 pubkeys + TTL = 28 * 24 * 60 * 60 + shared.inventory[inventoryHash] = ( + objectType, pubkeyStreamNumber, payload, int(time.time()) + TTL,'') + shared.inventorySets[pubkeyStreamNumber].add(inventoryHash) + with shared.printLock: + print 'broadcasting inv within API command disseminatePubkey with hash:', inventoryHash.encode('hex') + shared.broadcastToSendDataQueues(( + streamNumber, 'advertiseobject', inventoryHash)) + + def HandleGetMessageDataByDestinationHash(self, params): + # Method will eventually be used by a particular Android app to + # select relevant messages. Do not yet add this to the api + # doc. + if len(params) != 1: + raise APIError(0, 'I need 1 parameter!') + requestedHash, = params + if len(requestedHash) != 32: + raise APIError(19, 'The length of hash should be 32 bytes (encoded in hex thus 64 characters).') + requestedHash = self._decode(requestedHash, "hex") + + # This is not a particularly commonly used API function. Before we + # use it we'll need to fill out a field in our inventory database + # which is blank by default (first20bytesofencryptedmessage). + queryreturn = sqlQuery( + '''SELECT hash, payload FROM inventory WHERE tag = '' and objecttype = 2 ; ''') + with SqlBulkExecute() as sql: + for row in queryreturn: + hash01, payload = row + readPosition = 16 # Nonce length + time length + readPosition += decodeVarint(payload[readPosition:readPosition+10])[1] # Stream Number length + t = (payload[readPosition:readPosition+32],hash01) + sql.execute('''UPDATE inventory SET tag=? WHERE hash=?; ''', *t) + + queryreturn = sqlQuery('''SELECT payload FROM inventory WHERE tag = ?''', + requestedHash) + data = '{"receivedMessageDatas":[' + for row in queryreturn: + payload, = row + if len(data) > 25: + data += ',' + data += json.dumps({'data':payload.encode('hex')}, indent=4, separators=(',', ': ')) + data += ']}' + return data + + def HandleClientStatus(self, params): + if len(shared.connectedHostsList) == 0: + networkStatus = 'notConnected' + elif len(shared.connectedHostsList) > 0 and not shared.clientHasReceivedIncomingConnections: + networkStatus = 'connectedButHaveNotReceivedIncomingConnections' + else: + networkStatus = 'connectedAndReceivingIncomingConnections' + return json.dumps({'networkConnections':len(shared.connectedHostsList),'numberOfMessagesProcessed':shared.numberOfMessagesProcessed, 'numberOfBroadcastsProcessed':shared.numberOfBroadcastsProcessed, 'numberOfPubkeysProcessed':shared.numberOfPubkeysProcessed, 'networkStatus':networkStatus, 'softwareName':'PyBitmessage','softwareVersion':shared.softwareVersion}, indent=4, separators=(',', ': ')) + + def HandleDecodeAddress(self, params): + # Return a meaningful decoding of an address. + if len(params) != 1: + raise APIError(0, 'I need 1 parameter!') + address, = params + status, addressVersion, streamNumber, ripe = decodeAddress(address) + return json.dumps({'status':status, 'addressVersion':addressVersion, + 'streamNumber':streamNumber, 'ripe':ripe.encode('base64')}, indent=4, + separators=(',', ': ')) + + def HandleHelloWorld(self, params): + (a, b) = params + return a + '-' + b + + def HandleAdd(self, params): + (a, b) = params + return a + b + + def HandleStatusBar(self, params): + message, = params + shared.UISignalQueue.put(('updateStatusBar', message)) + + def HandleDeleteAndVacuum(self, params): + sqlStoredProcedure('deleteandvacuume') + return 'done' + + handlers = {} + handlers['helloWorld'] = HandleHelloWorld + handlers['add'] = HandleAdd + handlers['statusBar'] = HandleStatusBar + handlers['listAddresses'] = HandleListAddresses + handlers['listAddressBookEntries'] = HandleListAddressBookEntries; + handlers['listAddressbook'] = HandleListAddressBookEntries # the listAddressbook alias should be removed eventually. + handlers['addAddressBookEntry'] = HandleAddAddressBookEntry + handlers['addAddressbook'] = HandleAddAddressBookEntry # the addAddressbook alias should be deleted eventually. + handlers['deleteAddressBookEntry'] = HandleDeleteAddressBookEntry + handlers['deleteAddressbook'] = HandleDeleteAddressBookEntry # The deleteAddressbook alias should be deleted eventually. + handlers['createRandomAddress'] = HandleCreateRandomAddress + handlers['createDeterministicAddresses'] = HandleCreateDeterministicAddresses + handlers['getDeterministicAddress'] = HandleGetDeterministicAddress + handlers['createChan'] = HandleCreateChan + handlers['joinChan'] = HandleJoinChan + handlers['leaveChan'] = HandleLeaveChan + handlers['deleteAddress'] = HandleDeleteAddress + handlers['getAllInboxMessages'] = HandleGetAllInboxMessages + handlers['getAllInboxMessageIds'] = HandleGetAllInboxMessageIds + handlers['getAllInboxMessageIDs'] = HandleGetAllInboxMessageIds + handlers['getInboxMessageById'] = HandleGetInboxMessageById + handlers['getInboxMessageByID'] = HandleGetInboxMessageById + handlers['getAllSentMessages'] = HandleGetAllSentMessages + handlers['getAllSentMessageIds'] = HandleGetAllSentMessageIds + handlers['getAllSentMessageIDs'] = HandleGetAllSentMessageIds + handlers['getInboxMessagesByReceiver'] = HandleInboxMessagesByReceiver + handlers['getInboxMessagesByAddress'] = HandleInboxMessagesByReceiver #after some time getInboxMessagesByAddress should be removed + handlers['getSentMessageById'] = HandleGetSentMessageById + handlers['getSentMessageByID'] = HandleGetSentMessageById + handlers['getSentMessagesByAddress'] = HandleGetSentMessagesByAddress + handlers['getSentMessagesBySender'] = HandleGetSentMessagesByAddress + handlers['getSentMessageByAckData'] = HandleGetSentMessagesByAckData + handlers['trashMessage'] = HandleTrashMessage + handlers['trashInboxMessage'] = HandleTrashInboxMessage + handlers['trashSentMessage'] = HandleTrashSentMessage + handlers['trashSentMessageByAckData'] = HandleTrashSentMessageByAckDAta + handlers['sendMessage'] = HandleSendMessage + handlers['sendBroadcast'] = HandleSendBroadcast + handlers['getStatus'] = HandleGetStatus + handlers['addSubscription'] = HandleAddSubscription + handlers['deleteSubscription'] = HandleDeleteSubscription + handlers['listSubscriptions'] = ListSubscriptions + handlers['disseminatePreEncryptedMsg'] = HandleDisseminatePreEncryptedMsg + handlers['disseminatePubkey'] = HandleDissimatePubKey + handlers['getMessageDataByDestinationHash'] = HandleGetMessageDataByDestinationHash + handlers['getMessageDataByDestinationTag'] = HandleGetMessageDataByDestinationHash + handlers['clientStatus'] = HandleClientStatus + handlers['decodeAddress'] = HandleDecodeAddress + handlers['deleteAndVacuum'] = HandleDeleteAndVacuum + + def _handle_request(self, method, params): + if (self.handlers.has_key(method)): + return self.handlers[method](self ,params) + else: + raise APIError(20, 'Invalid method: %s' % method) + + def _dispatch(self, method, params): + self.cookies = [] + + validuser = self.APIAuthenticateClient() + if not validuser: + time.sleep(2) + return "RPC Username or password incorrect or HTTP header lacks authentication at all." + + try: + return self._handle_request(method, params) + except APIError as e: + return str(e) + except varintDecodeError as e: + logger.error(e) + return "API Error 0026: Data contains a malformed varint. Some details: %s" % e + except Exception as e: + logger.exception(e) + return "API Error 0021: Unexpected API Failure - %s" % str(e) From 42b63da7a276991b4eb1a5d5a6def3c4bac6a3d8 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Wed, 30 Sep 2015 10:22:41 +0200 Subject: [PATCH 014/210] Fix: UI --- src/bitmessageqt/__init__.py | 48 ++++++++++++++++++++++++++++++------ src/openclpow.py | 10 ++++++-- src/proofofwork.py | 4 +-- 3 files changed, 50 insertions(+), 12 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 6382d73f..184cd28c 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2026,9 +2026,9 @@ more work your computer must do to send the message. A Time-To-Live of four or f if self.ui.tabWidgetSend.currentIndex() == 0: # message to specific people sendMessageToPeople = True - fromAddress = self.ui.comboBoxSendFrom.itemData( + fromAddress = str(self.ui.comboBoxSendFrom.itemData( self.ui.comboBoxSendFrom.currentIndex(), - Qt.UserRole).toString() + Qt.UserRole).toString()) toAddresses = str(self.ui.lineEditTo.text()) subject = str(self.ui.lineEditSubject.text().toUtf8()) message = str( @@ -2036,9 +2036,9 @@ more work your computer must do to send the message. A Time-To-Live of four or f else: # broadcast message sendMessageToPeople = False - fromAddress = self.ui.comboBoxSendFromBroadcast.itemData( + fromAddress = str(self.ui.comboBoxSendFromBroadcast.itemData( self.ui.comboBoxSendFromBroadcast.currentIndex(), - Qt.UserRole).toString() + Qt.UserRole).toString()) subject = str(self.ui.lineEditSubjectBroadcast.text().toUtf8()) message = str( self.ui.textEditMessageBroadcast.document().toPlainText().toUtf8()) @@ -2096,6 +2096,7 @@ more work your computer must do to send the message. A Time-To-Live of four or f "MainWindow", "Error: You must specify a From address. If you don\'t have one, go to the \'Your Identities\' tab.")) else: toAddress = addBMIfNotPresent(toAddress) + if addressVersionNumber > 4 or addressVersionNumber <= 1: QMessageBox.about(self, _translate("MainWindow", "Address version number"), _translate( "MainWindow", "Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version.").arg(toAddress).arg(str(addressVersionNumber))) @@ -2260,6 +2261,8 @@ more work your computer must do to send the message. A Time-To-Live of four or f # pseudo-mailing-list. The message will be broadcast out. This function # puts the message on the 'Sent' tab. def displayNewSentMessage(self, toAddress, toLabel, fromAddress, subject, message, ackdata): + if self.getCurrentFolder() != "sent": + return subject = shared.fixPotentiallyInvalidUTF8Data(subject) message = shared.fixPotentiallyInvalidUTF8Data(message) try: @@ -2301,10 +2304,12 @@ more work your computer must do to send the message. A Time-To-Live of four or f newItem.setData(Qt.UserRole, QByteArray(ackdata)) newItem.setData(33, int(time.time())) self.ui.tableWidgetInbox.setItem(0, 3, newItem) - self.ui.textEditSentMessage.setPlainText(unicode(message, 'utf-8)')) + self.ui.textEditInboxMessage.setPlainText(unicode(message, 'utf-8)')) self.ui.tableWidgetInbox.setSortingEnabled(True) def displayNewInboxMessage(self, inventoryHash, toAddress, fromAddress, subject, message): + if self.getCurrentFolder() != "inbox": + return subject = shared.fixPotentiallyInvalidUTF8Data(subject) fromLabel = '' queryreturn = sqlQuery( @@ -3533,16 +3538,43 @@ more work your computer must do to send the message. A Time-To-Live of four or f ackdata = self.getCurrentMessageId() if ackdata and messageTextedit: queryreturn = sqlQuery( - '''select message from sent where ackdata=?''', ackdata) + '''select message, 1 from sent where ackdata=?''', ackdata) else: msgid = self.getCurrentMessageId() if msgid and messageTextedit: queryreturn = sqlQuery( - '''select message from inbox where msgid=?''', msgid) + '''select message, read from inbox where msgid=?''', msgid) if queryreturn != []: + refresh = False for row in queryreturn: - message, = row + message, read = row + if folder == 'inbox' and read == 0: + markread = sqlQuery( + '''UPDATE inbox SET read = 1 WHERE msgid = ?''', msgid) + refresh = True + if refresh: + tableWidget = self.getCurrentMessagelist() + if not tableWidget: + return + font = QFont() + font.setBold(False) +# inventoryHashesToMarkRead = [] + currentRow = self.getCurrentMessagelist().currentRow() +# inventoryHashToMarkRead = str(tableWidget.item( +# currentRow, 3).data(Qt.UserRole).toPyObject()) +# inventoryHashesToMarkRead.append(inventoryHashToMarkRead) + tableWidget.item(currentRow, 0).setFont(font) + tableWidget.item(currentRow, 1).setFont(font) + tableWidget.item(currentRow, 2).setFont(font) + tableWidget.item(currentRow, 3).setFont(font) + self.changedInboxUnread() +# if self.ui.tabWidget.currentIndex() == 0: +# self.rerenderTabTreeMessages() +# elif self.ui.tabWidget.currentIndex() == 2: +# self.rerenderTabTreeSubscriptions() +# elif self.ui.tabWidget.currentIndex() == 3: +# self.rerenderTabTreeChans() else: data = self.getCurrentMessageId() if data != False: diff --git a/src/openclpow.py b/src/openclpow.py index 83d7513f..65449b85 100644 --- a/src/openclpow.py +++ b/src/openclpow.py @@ -15,10 +15,16 @@ try: ctx = cl.create_some_context() queue = cl.CommandQueue(ctx) - f = open('/usr/src/PyBitmessage/src/kernel.cl', 'r') + #f = open('/usr/src/PyBitmessage/src/kernel.cl', 'r') + import os + print "working directory: " + os.getcwd() +# time.sleep(5) + f = open('kernel.cl', 'r') fstr = ''.join(f.readlines()) program = cl.Program(ctx, fstr).build() -except: +except Exception as e: + print "opencl fail:" + str(e) +# time.sleep(5) ctx = False def has_opencl(): diff --git a/src/proofofwork.py b/src/proofofwork.py index f2e8096f..85f79430 100644 --- a/src/proofofwork.py +++ b/src/proofofwork.py @@ -82,8 +82,8 @@ def _doGPUPow(target, initialHash): def run(target, initialHash): target = int(target) - if openclpow.has_opencl(): - return _doGPUPow(target, initialHash) + if shared.safeConfigGetBoolean('bitmessagesettings', 'opencl') and openclpow.has_opencl(): + return _doGPUPow(target, initialHash) elif frozen == "macosx_app" or not frozen: return _doFastPoW(target, initialHash) else: From 7879719816e6265982c100e592efabfc6beff1c7 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Wed, 30 Sep 2015 11:14:56 +0200 Subject: [PATCH 015/210] UI fix All new messages appeared in current account message list, as opposed to only new messages associated with that account. --- src/bitmessageqt/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 184cd28c..dd267cda 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2261,7 +2261,7 @@ more work your computer must do to send the message. A Time-To-Live of four or f # pseudo-mailing-list. The message will be broadcast out. This function # puts the message on the 'Sent' tab. def displayNewSentMessage(self, toAddress, toLabel, fromAddress, subject, message, ackdata): - if self.getCurrentFolder() != "sent": + if self.getCurrentFolder() != "sent" or self.getCurrentAccount() != fromAddress: return subject = shared.fixPotentiallyInvalidUTF8Data(subject) message = shared.fixPotentiallyInvalidUTF8Data(message) @@ -2308,7 +2308,7 @@ more work your computer must do to send the message. A Time-To-Live of four or f self.ui.tableWidgetInbox.setSortingEnabled(True) def displayNewInboxMessage(self, inventoryHash, toAddress, fromAddress, subject, message): - if self.getCurrentFolder() != "inbox": + if self.getCurrentFolder() != "inbox" or self.getCurrentAccount() != toAddress: return subject = shared.fixPotentiallyInvalidUTF8Data(subject) fromLabel = '' From 9b78708e785a4d79314505d507cee0f68f340fd4 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Wed, 30 Sep 2015 11:30:21 +0200 Subject: [PATCH 016/210] Shorten trashed summary Trash UI now exists, there is no need to tell the user it doesn't. --- src/bitmessageqt/__init__.py | 2 +- src/translations/bitmessage_ar.ts | 4 ++-- src/translations/bitmessage_cs.ts | 4 ++-- src/translations/bitmessage_de.ts | 4 ++-- src/translations/bitmessage_en_pirate.ts | 2 +- src/translations/bitmessage_eo.ts | 4 ++-- src/translations/bitmessage_fr.ts | 4 ++-- src/translations/bitmessage_ja.ts | 4 ++-- src/translations/bitmessage_nl.ts | 2 +- src/translations/bitmessage_no.ts | 4 ++-- src/translations/bitmessage_ru.ts | 4 ++-- src/translations/bitmessage_zh_cn.ts | 4 ++-- 12 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index dd267cda..75a3df36 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2994,7 +2994,7 @@ more work your computer must do to send the message. A Time-To-Live of four or f self.ui.textEditInboxMessage.setText("") tableWidget.removeRow(currentRow) self.statusBar().showMessage(_translate( - "MainWindow", "Moved items to trash. There is no user interface to view your trash, but it is still on disk if you are desperate to get it back.")) + "MainWindow", "Moved items to trash.")) if currentRow == 0: tableWidget.selectRow(currentRow) else: diff --git a/src/translations/bitmessage_ar.ts b/src/translations/bitmessage_ar.ts index b37dcc0b..195db516 100644 --- a/src/translations/bitmessage_ar.ts +++ b/src/translations/bitmessage_ar.ts @@ -582,8 +582,8 @@ It is important that you back up this file. Would you like to open the file now? - Moved items to trash. There is no user interface to view your trash, but it is still on disk if you are desperate to get it back. - تم نقل المادة لسلة المهملات، لا يتوفر واجهة مستخدم لإظهار سلة المهملات حالياً، و لكن يمكنك إيجاد الرسالة المحذوفة على القرص الصلب إذا أردت استرجاعها. + Moved items to trash. + تم نقل المادة لسلة المهملات. diff --git a/src/translations/bitmessage_cs.ts b/src/translations/bitmessage_cs.ts index c16012c9..438e5907 100755 --- a/src/translations/bitmessage_cs.ts +++ b/src/translations/bitmessage_cs.ts @@ -556,8 +556,8 @@ Je důležité si tento soubor zazálohovat. Přejete si tento soubor nyní otev - Moved items to trash. There is no user interface to view your trash, but it is still on disk if you are desperate to get it back. - Položky byly přesunuty do koše. Koš nemá uživatelské rozhraní, ale jeho obsah je stále na disku, pro případ že ho nutně potřebujete obnovit. + Moved items to trash. + Položky byly přesunuty do koše. diff --git a/src/translations/bitmessage_de.ts b/src/translations/bitmessage_de.ts index 5d937bf9..1c817a98 100644 --- a/src/translations/bitmessage_de.ts +++ b/src/translations/bitmessage_de.ts @@ -491,8 +491,8 @@ Es ist wichtig, dass Sie diese Datei sichern. Möchten Sie die datei jetzt öffn - Moved items to trash. There is no user interface to view your trash, but it is still on disk if you are desperate to get it back. - Objekt in den Papierkorb verschoben. Es gibt kein Benutzerinterface für den Papierkorb, aber die Daten sind noch auf Ihrer Festplatte wenn Sie sie wirklich benötigen. + Moved items to trash. + Objekt in den Papierkorb verschoben. diff --git a/src/translations/bitmessage_en_pirate.ts b/src/translations/bitmessage_en_pirate.ts index ba97978a..02330897 100644 --- a/src/translations/bitmessage_en_pirate.ts +++ b/src/translations/bitmessage_en_pirate.ts @@ -551,7 +551,7 @@ It is important that you back up this file. Would you like to open the file now? - Moved items to trash. There is no user interface to view your trash, but it is still on disk if you are desperate to get it back. + Moved items to trash. diff --git a/src/translations/bitmessage_eo.ts b/src/translations/bitmessage_eo.ts index 2f09691d..00e33f9e 100644 --- a/src/translations/bitmessage_eo.ts +++ b/src/translations/bitmessage_eo.ts @@ -491,8 +491,8 @@ Estas grava ke vi faru savkopion de tiu dosiero. Ĉu vi volas malfermi la dosier - Moved items to trash. There is no user interface to view your trash, but it is still on disk if you are desperate to get it back. - Movis elementojn al rubujo. Ne estas fasado por rigardi vian rubujon, sed ankoraŭ estas sur disko se vi esperas ĝin retrovi. + Moved items to trash. + Movis elementojn al rubujo. diff --git a/src/translations/bitmessage_fr.ts b/src/translations/bitmessage_fr.ts index e32eb5ef..a5483303 100644 --- a/src/translations/bitmessage_fr.ts +++ b/src/translations/bitmessage_fr.ts @@ -573,8 +573,8 @@ Il est important de faire des sauvegardes de ce fichier. Souhaitez-vous l'o - Moved items to trash. There is no user interface to view your trash, but it is still on disk if you are desperate to get it back. - Messages déplacés dans la corbeille. Il n'y a pas d'interface utilisateur pour voir votre corbeille, mais ils sont toujours présents sur le disque si vous souhaitez les récupérer. + Moved items to trash. + Messages déplacés dans la corbeille. diff --git a/src/translations/bitmessage_ja.ts b/src/translations/bitmessage_ja.ts index da9edc69..08061f61 100644 --- a/src/translations/bitmessage_ja.ts +++ b/src/translations/bitmessage_ja.ts @@ -550,8 +550,8 @@ It is important that you back up this file. Would you like to open the file now? - Moved items to trash. There is no user interface to view your trash, but it is still on disk if you are desperate to get it back. - アイテムをゴミ箱へ移動。ゴミ箱の内容を表示する画面はありませんが、もし元に戻したくなった場合に備えてディスク上に残されます。 + Moved items to trash. + アイテムをゴミ箱へ移動。 diff --git a/src/translations/bitmessage_nl.ts b/src/translations/bitmessage_nl.ts index 54237fdd..46969c0d 100644 --- a/src/translations/bitmessage_nl.ts +++ b/src/translations/bitmessage_nl.ts @@ -552,7 +552,7 @@ It is important that you back up this file. Would you like to open the file now? - Moved items to trash. There is no user interface to view your trash, but it is still on disk if you are desperate to get it back. + Moved items to trash. diff --git a/src/translations/bitmessage_no.ts b/src/translations/bitmessage_no.ts index 72ce1f4f..0b12acfb 100644 --- a/src/translations/bitmessage_no.ts +++ b/src/translations/bitmessage_no.ts @@ -573,8 +573,8 @@ Det er viktig at du tar en sikkerhetskopi av denne filen. Vil du åpne denne - Moved items to trash. There is no user interface to view your trash, but it is still on disk if you are desperate to get it back. - Kastet innholdet. Det finnes ikke noe brukergrensesnitt enda for kastet innhold, men ingenting er slettet enda. Alt ligger fortsatt på disken hvis du er interessert i å få det tilbake. + Moved items to trash. + Kastet innholdet. diff --git a/src/translations/bitmessage_ru.ts b/src/translations/bitmessage_ru.ts index e72011ca..04ef8ab4 100644 --- a/src/translations/bitmessage_ru.ts +++ b/src/translations/bitmessage_ru.ts @@ -495,8 +495,8 @@ It is important that you back up this file. Would you like to open the file now? - Moved items to trash. There is no user interface to view your trash, but it is still on disk if you are desperate to get it back. - Удалено в корзину. Чтобы попасть в корзину, Вам нужно будет найти файл корзины на диске. + Moved items to trash. + Удалено в корзину. diff --git a/src/translations/bitmessage_zh_cn.ts b/src/translations/bitmessage_zh_cn.ts index adc055e5..40c648e9 100644 --- a/src/translations/bitmessage_zh_cn.ts +++ b/src/translations/bitmessage_zh_cn.ts @@ -861,8 +861,8 @@ It is important that you back up this file. Would you like to open the file now? - Moved items to trash. There is no user interface to view your trash, but it is still on disk if you are desperate to get it back. - 已经移动项目到回收站。没有图形化的界面可以查看您的回收站,不过如果您还想找回的话它还在您的硬盘上。 + Moved items to trash. + 已经移动项目到回收站。 From 552a0e3e462c13d7f957f54eece103abcbc25ae9 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Wed, 30 Sep 2015 13:32:24 +0200 Subject: [PATCH 017/210] Typo --- src/bitmessageqt/bitmessageui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitmessageqt/bitmessageui.py b/src/bitmessageqt/bitmessageui.py index d795f17c..36ccbbb0 100644 --- a/src/bitmessageqt/bitmessageui.py +++ b/src/bitmessageqt/bitmessageui.py @@ -622,7 +622,7 @@ class Ui_MainWindow(object): def retranslateUi(self, MainWindow): MainWindow.setWindowTitle(_translate("MainWindow", "Bitmessage", None)) self.treeWidgetYourIdentities.headerItem().setText(0, _translate("MainWindow", "Identities", None)) - self.pushButtonNewAddress.setText(_translate("MainWindow", "New Indentitiy", None)) + self.pushButtonNewAddress.setText(_translate("MainWindow", "New Identity", None)) self.inboxSearchLineEdit.setPlaceholderText(_translate("MainWindow", "Search", None)) self.inboxSearchOption.setItemText(0, _translate("MainWindow", "All", None)) self.inboxSearchOption.setItemText(1, _translate("MainWindow", "To", None)) From 18cc252d425f836a20c99668ee47e51e50901809 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Thu, 1 Oct 2015 09:42:31 +0200 Subject: [PATCH 018/210] Migration Wizard - from standard PyBitmessage to this one - not working yet --- src/bitmessageqt/migrationwizard.py | 84 +++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 src/bitmessageqt/migrationwizard.py diff --git a/src/bitmessageqt/migrationwizard.py b/src/bitmessageqt/migrationwizard.py new file mode 100644 index 00000000..945adefa --- /dev/null +++ b/src/bitmessageqt/migrationwizard.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python2.7 +from PyQt4 import QtCore, QtGui + +class MigrationWizardIntroPage(QtGui.QWizardPage): + def __init__(self): + super(QtGui.QWizardPage, self).__init__() + self.setTitle("Migrating configuration") + + label = QtGui.QLabel("This wizard will help you to migrate your configuration. " + "You can still keep using PyBitMessage once you migrate, the changes are backwards compatible.") + label.setWordWrap(True) + + layout = QtGui.QVBoxLayout() + layout.addWidget(label) + self.setLayout(layout) + + def nextId(self): + return 1 + + +class MigrationWizardAddressesPage(QtGui.QWizardPage): + def __init__(self, addresses): + super(QtGui.QWizardPage, self).__init__() + self.setTitle("Addresses") + + label = QtGui.QLabel("Please select addresses that you are already using with mailchuck. ") + label.setWordWrap(True) + + layout = QtGui.QVBoxLayout() + layout.addWidget(label) + self.setLayout(layout) + + def nextId(self): + return 10 + + +class MigrationWizardGPUPage(QtGui.QWizardPage): + def __init__(self): + super(QtGui.QWizardPage, self).__init__() + self.setTitle("GPU") + + label = QtGui.QLabel("Are you using a GPU? ") + label.setWordWrap(True) + + layout = QtGui.QVBoxLayout() + layout.addWidget(label) + self.setLayout(layout) + + def nextId(self): + return 10 + + +class MigrationWizardConclusionPage(QtGui.QWizardPage): + def __init__(self): + super(QtGui.QWizardPage, self).__init__() + self.setTitle("All done!") + + label = QtGui.QLabel("You successfully migrated.") + label.setWordWrap(True) + + layout = QtGui.QVBoxLayout() + layout.addWidget(label) + self.setLayout(layout) + + +class Ui_MigrationWizard(QtGui.QWizard): + def __init__(self, addresses): + super(QtGui.QWizard, self).__init__() + + self.pages = {} + + page = MigrationWizardIntroPage() + self.setPage(0, page) + self.setStartId(0) + page = MigrationWizardAddressesPage() + self.setPage(1, page) + page = MigrationWizardGPUPage(addresses) + self.setPage(2, page) + page = MigrationWizardConclusionPage() + self.setPage(10, page) + + self.setWindowTitle("Migration from PyBitMessage wizard") + self.adjustSize() + self.show() \ No newline at end of file From db9276f34964917d631c2320c140e778c1182768 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Thu, 1 Oct 2015 09:42:47 +0200 Subject: [PATCH 019/210] New address wizard Not working yet, just UI --- src/bitmessageqt/__init__.py | 13 + src/bitmessageqt/newaddresswizard.py | 354 +++++++++++++++++++++++++++ 2 files changed, 367 insertions(+) create mode 100644 src/bitmessageqt/newaddresswizard.py diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 75a3df36..849b2cde 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -11,6 +11,8 @@ import shared from bitmessageui import * from namecoin import namecoinConnection, ensureNamecoinOptions from newaddressdialog import * +from newaddresswizard import * +from migrationwizard import * from addaddressdialog import * from newsubscriptiondialog import * from regenerateaddresses import * @@ -2756,6 +2758,17 @@ more work your computer must do to send the message. A Time-To-Live of four or f self.rerenderInboxToLabels() def click_NewAddressDialog(self): + addresses = [] + configSections = shared.config.sections() + for addressInKeysFile in configSections: + if addressInKeysFile == 'bitmessagesettings': + continue + addresses.append(addressInKeysFile) + self.dialog = Ui_NewAddressWizard(addresses) + self.dialog.exec_() +# print "Name: " + self.dialog.field("name").toString() +# print "Email: " + self.dialog.field("email").toString() + return self.dialog = NewAddressDialog(self) # For Modal dialogs if self.dialog.exec_(): diff --git a/src/bitmessageqt/newaddresswizard.py b/src/bitmessageqt/newaddresswizard.py new file mode 100644 index 00000000..e54b18c3 --- /dev/null +++ b/src/bitmessageqt/newaddresswizard.py @@ -0,0 +1,354 @@ +#!/usr/bin/env python2.7 +from PyQt4 import QtCore, QtGui + +class NewAddressWizardIntroPage(QtGui.QWizardPage): + def __init__(self): + super(QtGui.QWizardPage, self).__init__() + self.setTitle("Creating a new address") + + label = QtGui.QLabel("This wizard will help you create as many addresses as you like. Indeed, creating and abandoning addresses is encouraged.\n\n" + "What type of address would you like? Would you like to send emails or not?\n" + "You can still change your mind later, and register/unregister with an email service provider.\n\n") + label.setWordWrap(True) + + self.emailAsWell = QtGui.QRadioButton("Combined email and bitmessage address") + self.onlyBM = QtGui.QRadioButton("Bitmessage-only address (no email)") + self.emailAsWell.setChecked(True) + self.registerField("emailAsWell", self.emailAsWell) + self.registerField("onlyBM", self.onlyBM) + + layout = QtGui.QVBoxLayout() + layout.addWidget(label) + layout.addWidget(self.emailAsWell) + layout.addWidget(self.onlyBM) + self.setLayout(layout) + + def nextId(self): + if self.emailAsWell.isChecked(): + return 4 + else: + return 1 + + +class NewAddressWizardRngPassphrasePage(QtGui.QWizardPage): + def __init__(self): + super(QtGui.QWizardPage, self).__init__() + self.setTitle("Random or Passphrase") + + label = QtGui.QLabel("

You may generate addresses by using either random numbers or by using a passphrase. " + "If you use a passphrase, the address is called a "deterministic" address. " + "The \'Random Number\' option is selected by default but deterministic addresses have several pros and cons:

" + "" + "" + "
Pros:Cons:
You can recreate your addresses on any computer from memory. " + "You need-not worry about backing up your keys.dat file as long as you can remember your passphrase.You must remember (or write down) your passphrase if you expect to be able " + "to recreate your keys if they are lost. " +# "You must remember the address version number and the stream number along with your passphrase. " + "If you choose a weak passphrase and someone on the Internet can brute-force it, they can read your messages and send messages as you." + "

") + label.setWordWrap(True) + + self.randomAddress = QtGui.QRadioButton("Use a random number generator to make an address") + self.deterministicAddress = QtGui.QRadioButton("Use a passphrase to make an address") + self.randomAddress.setChecked(True) + + layout = QtGui.QVBoxLayout() + layout.addWidget(label) + layout.addWidget(self.randomAddress) + layout.addWidget(self.deterministicAddress) + self.setLayout(layout) + + def nextId(self): + if self.randomAddress.isChecked(): + return 2 + else: + return 3 + +class NewAddressWizardRandomPage(QtGui.QWizardPage): + def __init__(self, addresses): + super(QtGui.QWizardPage, self).__init__() + self.setTitle("Random") + + label = QtGui.QLabel("Random address.") + label.setWordWrap(True) + + labelLabel = QtGui.QLabel("Label (not shown to anyone except you):") + self.labelLineEdit = QtGui.QLineEdit() + + self.radioButtonMostAvailable = QtGui.QRadioButton("Use the most available stream\n" + "(best if this is the first of many addresses you will create)") + self.radioButtonExisting = QtGui.QRadioButton("Use the same stream as an existing address\n" + "(saves you some bandwidth and processing power)") + self.radioButtonMostAvailable.setChecked(True) + self.comboBoxExisting = QtGui.QComboBox() + self.comboBoxExisting.setEnabled(False) + self.comboBoxExisting.setEditable(True) + + for address in addresses: + self.comboBoxExisting.addItem(address) + +# self.comboBoxExisting.setObjectName(_fromUtf8("comboBoxExisting")) + self.checkBoxEighteenByteRipe = QtGui.QCheckBox("Spend several minutes of extra computing time to make the address(es) 1 or 2 characters shorter") + + layout = QtGui.QGridLayout() + layout.addWidget(label, 0, 0) + layout.addWidget(labelLabel, 1, 0) + layout.addWidget(self.labelLineEdit, 2, 0) + layout.addWidget(self.radioButtonMostAvailable, 3, 0) + layout.addWidget(self.radioButtonExisting, 4, 0) + layout.addWidget(self.comboBoxExisting, 5, 0) + layout.addWidget(self.checkBoxEighteenByteRipe, 6, 0) + self.setLayout(layout) + + QtCore.QObject.connect(self.radioButtonExisting, QtCore.SIGNAL("toggled(bool)"), self.comboBoxExisting.setEnabled) + + self.registerField("label", self.labelLineEdit) + self.registerField("radioButtonMostAvailable", self.radioButtonMostAvailable) + self.registerField("radioButtonExisting", self.radioButtonExisting) + self.registerField("comboBoxExisting", self.comboBoxExisting) + +# self.emailAsWell = QtGui.QRadioButton("Combined email and bitmessage account") +# self.onlyBM = QtGui.QRadioButton("Bitmessage-only account (no email)") +# self.emailAsWell.setChecked(True) + + def nextId(self): + return 6 + + +class NewAddressWizardPassphrasePage(QtGui.QWizardPage): + def __init__(self): + super(QtGui.QWizardPage, self).__init__() + self.setTitle("Passphrase") + + label = QtGui.QLabel("Deterministric address.") + label.setWordWrap(True) + + passphraseLabel = QtGui.QLabel("Passphrase") + self.lineEditPassphrase = QtGui.QLineEdit() + self.lineEditPassphrase.setEchoMode(QtGui.QLineEdit.Password) + self.lineEditPassphrase.setInputMethodHints(QtCore.Qt.ImhHiddenText|QtCore.Qt.ImhNoAutoUppercase|QtCore.Qt.ImhNoPredictiveText) + retypePassphraseLabel = QtGui.QLabel("Retype passphrase") + self.lineEditPassphraseAgain = QtGui.QLineEdit() + self.lineEditPassphraseAgain.setEchoMode(QtGui.QLineEdit.Password) + + numberLabel = QtGui.QLabel("Number of addresses to make based on your passphrase:") + self.spinBoxNumberOfAddressesToMake = QtGui.QSpinBox() + self.spinBoxNumberOfAddressesToMake.setMinimum(1) + self.spinBoxNumberOfAddressesToMake.setProperty("value", 8) +# self.spinBoxNumberOfAddressesToMake.setObjectName(_fromUtf8("spinBoxNumberOfAddressesToMake")) + label2 = QtGui.QLabel("In addition to your passphrase, you must remember these numbers:") + label3 = QtGui.QLabel("Address version number: 4") + label4 = QtGui.QLabel("Stream number: 1") + + layout = QtGui.QGridLayout() + layout.addWidget(label, 0, 0, 1, 4) + layout.addWidget(passphraseLabel, 1, 0, 1, 4) + layout.addWidget(self.lineEditPassphrase, 2, 0, 1, 4) + layout.addWidget(retypePassphraseLabel, 3, 0, 1, 4) + layout.addWidget(self.lineEditPassphraseAgain, 4, 0, 1, 4) + layout.addWidget(numberLabel, 5, 0, 1, 3) + layout.addWidget(self.spinBoxNumberOfAddressesToMake, 5, 3) + layout.setColumnMinimumWidth(3, 1) + layout.addWidget(label2, 6, 0, 1, 4) + layout.addWidget(label3, 7, 0, 1, 2) + layout.addWidget(label4, 7, 2, 1, 2) + self.setLayout(layout) + + def nextId(self): + return 6 + + +class NewAddressWizardEmailProviderPage(QtGui.QWizardPage): + def __init__(self): + super(QtGui.QWizardPage, self).__init__() + self.setTitle("Choose email provider") + + label = QtGui.QLabel("Currently only Mailchuck email gateway is available " + "(@mailchuck.com email address). In the future, maybe other gateways will be available. " + "Press Next.") + label.setWordWrap(True) + +# self.mailchuck = QtGui.QRadioButton("Mailchuck email gateway (@mailchuck.com)") +# self.mailchuck.setChecked(True) + + layout = QtGui.QVBoxLayout() + layout.addWidget(label) +# layout.addWidget(self.mailchuck) + self.setLayout(layout) + + def nextId(self): + return 5 + + +class NewAddressWizardEmailAddressPage(QtGui.QWizardPage): + def __init__(self): + super(QtGui.QWizardPage, self).__init__() + self.setTitle("Email address") + + label = QtGui.QLabel("Choosing an email address. Address must end with @mailchuck.com") + label.setWordWrap(True) + + self.specificEmail = QtGui.QRadioButton("Pick your own email address:") + self.specificEmail.setChecked(True) + self.emailLineEdit = QtGui.QLineEdit() + self.randomEmail = QtGui.QRadioButton("Generate a random email address") + + QtCore.QObject.connect(self.specificEmail, QtCore.SIGNAL("toggled(bool)"), self.emailLineEdit.setEnabled) + + layout = QtGui.QVBoxLayout() + layout.addWidget(label) + layout.addWidget(self.specificEmail) + layout.addWidget(self.emailLineEdit) + layout.addWidget(self.randomEmail) + self.setLayout(layout) + + def nextId(self): + return 6 + + +class NewAddressWizardWaitPage(QtGui.QWizardPage): + def __init__(self): + super(QtGui.QWizardPage, self).__init__() + self.setTitle("Wait") + + self.label = QtGui.QLabel("Wait!") + self.label.setWordWrap(True) + self.progressBar = QtGui.QProgressBar() + self.progressBar.setMinimum(0) + self.progressBar.setMaximum(100) + self.progressBar.setValue(0) + +# self.emailAsWell = QtGui.QRadioButton("Combined email and bitmessage account") +# self.onlyBM = QtGui.QRadioButton("Bitmessage-only account (no email)") +# self.emailAsWell.setChecked(True) + + layout = QtGui.QVBoxLayout() + layout.addWidget(self.label) + layout.addWidget(self.progressBar) +# layout.addWidget(self.emailAsWell) +# layout.addWidget(self.onlyBM) + self.setLayout(layout) + + def update(self, i): + if i == 101 and self.wizard().currentId() == 6: + self.wizard().button(QtGui.QWizard.NextButton).click() + return + elif i == 101: + print "haha" + return + self.progressBar.setValue(i) + if i == 50: + self.emit(QtCore.SIGNAL('completeChanged()')) + + def isComplete(self): +# print "val = " + str(self.progressBar.value()) + if self.progressBar.value() >= 50: + return True + else: + return False + + def initializePage(self): + if self.field("emailAsWell").toBool(): + val = "yes/" + else: + val = "no/" + if self.field("onlyBM").toBool(): + val += "yes" + else: + val += "no" + + self.label.setText("Wait! " + val) +# self.wizard().button(QtGui.QWizard.NextButton).setEnabled(False) + self.progressBar.setValue(0) + self.thread = NewAddressThread() + self.connect(self.thread, self.thread.signal, self.update) + self.thread.start() + + def nextId(self): + return 10 + + +class NewAddressWizardConclusionPage(QtGui.QWizardPage): + def __init__(self): + super(QtGui.QWizardPage, self).__init__() + self.setTitle("All done!") + + label = QtGui.QLabel("You successfully created a new address.") + label.setWordWrap(True) + + layout = QtGui.QVBoxLayout() + layout.addWidget(label) + self.setLayout(layout) + +class Ui_NewAddressWizard(QtGui.QWizard): + def __init__(self, addresses): + super(QtGui.QWizard, self).__init__() + + self.pages = {} + + page = NewAddressWizardIntroPage() + self.setPage(0, page) + self.setStartId(0) + page = NewAddressWizardRngPassphrasePage() + self.setPage(1, page) + page = NewAddressWizardRandomPage(addresses) + self.setPage(2, page) + page = NewAddressWizardPassphrasePage() + self.setPage(3, page) + page = NewAddressWizardEmailProviderPage() + self.setPage(4, page) + page = NewAddressWizardEmailAddressPage() + self.setPage(5, page) + page = NewAddressWizardWaitPage() + self.setPage(6, page) + page = NewAddressWizardConclusionPage() + self.setPage(10, page) + + self.setWindowTitle("New address wizard") + self.adjustSize() + self.show() + +class NewAddressThread(QtCore.QThread): + def __init__(self): + QtCore.QThread.__init__(self) + self.signal = QtCore.SIGNAL("signal") + + def __del__(self): + self.wait() + + def createDeterministic(): + pass + + def createPassphrase(): + pass + + def broadcastAddress(): + pass + + def registerMailchuck(): + pass + + def waitRegistration(): + pass + + def run(self): + import time + for i in range(1, 101): + time.sleep(0.1) # artificial time delay + self.emit(self.signal, i) + self.emit(self.signal, 101) +# self.terminate() + +if __name__ == '__main__': + + import sys + + app = QtGui.QApplication(sys.argv) + + wizard = Ui_NewAddressWizard(["a", "b", "c", "d"]) + if (wizard.exec_()): + print "Email: " + ("yes" if wizard.field("emailAsWell").toBool() else "no") + print "BM: " + ("yes" if wizard.field("onlyBM").toBool() else "no") + else: + print "Wizard cancelled" + sys.exit() From 9c611178e111ad8d35695ec95b26859f4df5ae83 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Thu, 1 Oct 2015 10:08:12 +0200 Subject: [PATCH 020/210] Address tree sorting Sorts addresses in the address tree --- src/bitmessageqt/__init__.py | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 849b2cde..5dfaec72 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -168,6 +168,28 @@ def change_translation(locale): qtranslator.load(translationpath) QtGui.QApplication.installTranslator(qtranslator) +def address_compare(x, y): + if x == "bitmessagesettings": + return -1 + elif y == "bitmessagesettings": + return 1 + if shared.config.getboolean(x, 'enabled') == shared.config.getboolean(y, 'enabled'): + if shared.config.get(x, 'label'): + x1 = shared.config.get(x, 'label') + else: + x1 = x + if shared.config.get(y, 'label'): + y1 = shared.config.get(y, 'label') + else: + y1 = y + if x1 > y1: + return 1 + elif x1 < y1: + return -1 + else: + return 0 + else: + return (1 if shared.config.getboolean(x, 'enabled') else '-1') class MyForm(QtGui.QMainWindow): @@ -536,7 +558,7 @@ class MyForm(QtGui.QMainWindow): def rerenderTabTreeChans(self): self.rerenderTabTree('chan') - + def rerenderTabTree(self, tab): folders = ['inbox', 'sent', 'trash'] if tab == 'messages': @@ -553,7 +575,7 @@ class MyForm(QtGui.QMainWindow): toaddress, folder, cnt = row cntUnreadMsg[toaddress + folder] = cnt - configSections = shared.config.sections() + configSections = sorted(shared.config.sections(), cmp=address_compare) for addressInKeysFile in configSections: if addressInKeysFile != 'bitmessagesettings': isEnabled = shared.config.getboolean( From 92a30a5f6641535b57aa4aedd78ba8bc679c1477 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Thu, 1 Oct 2015 10:10:30 +0200 Subject: [PATCH 021/210] Typo fix --- src/bitmessageqt/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 5dfaec72..9f1b5bae 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -189,7 +189,7 @@ def address_compare(x, y): else: return 0 else: - return (1 if shared.config.getboolean(x, 'enabled') else '-1') + return (1 if shared.config.getboolean(x, 'enabled') else -1) class MyForm(QtGui.QMainWindow): From f4841bd2effc60a97c044c8504bbb032fa064e07 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Thu, 1 Oct 2015 10:12:01 +0200 Subject: [PATCH 022/210] Order reverse Disabled addresses should go at the end, not at the beginning. --- src/bitmessageqt/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 9f1b5bae..901f7220 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -189,7 +189,7 @@ def address_compare(x, y): else: return 0 else: - return (1 if shared.config.getboolean(x, 'enabled') else -1) + return (-1 if shared.config.getboolean(x, 'enabled') else 1) class MyForm(QtGui.QMainWindow): From 02186690ca2d812c8f164a029870947b7df1c2ec Mon Sep 17 00:00:00 2001 From: mailchuck Date: Thu, 1 Oct 2015 10:15:23 +0200 Subject: [PATCH 023/210] Formatting --- src/bitmessageqt/__init__.py | 83 ++++++++++++++++++------------------ 1 file changed, 42 insertions(+), 41 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 901f7220..1bb00457 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -577,53 +577,54 @@ class MyForm(QtGui.QMainWindow): configSections = sorted(shared.config.sections(), cmp=address_compare) for addressInKeysFile in configSections: - if addressInKeysFile != 'bitmessagesettings': - isEnabled = shared.config.getboolean( - addressInKeysFile, 'enabled') - isChan = shared.safeConfigGetBoolean( - addressInKeysFile, 'chan') - isMaillinglist = shared.safeConfigGetBoolean( - addressInKeysFile, 'mailinglist') + if addressInKeysFile == 'bitmessagesettings': + continue + isEnabled = shared.config.getboolean( + addressInKeysFile, 'enabled') + isChan = shared.safeConfigGetBoolean( + addressInKeysFile, 'chan') + isMaillinglist = shared.safeConfigGetBoolean( + addressInKeysFile, 'mailinglist') - if tab == 'messages': - if isChan: - continue - elif tab == 'chan': - if not isChan: - continue + if tab == 'messages': + if isChan: + continue + elif tab == 'chan': + if not isChan: + continue - newItem = QtGui.QTreeWidgetItem(treeWidget) - newItem.setExpanded(True) - newItem.setIcon(0, avatarize(addressInKeysFile)) - newItem.setText(0, unicode( - shared.config.get(addressInKeysFile, 'label'), 'utf-8)') - + ' (' + addressInKeysFile + ')') - newItem.setData(0, Qt.UserRole, [str(addressInKeysFile), "inbox"]) - #set text color - if isEnabled: - if isMaillinglist: - brush = QtGui.QBrush(QtGui.QColor(137, 04, 177)) - else: - brush = QtGui.QBrush(QApplication.palette().text().color()) + newItem = QtGui.QTreeWidgetItem(treeWidget) + newItem.setExpanded(True) + newItem.setIcon(0, avatarize(addressInKeysFile)) + newItem.setText(0, unicode( + shared.config.get(addressInKeysFile, 'label'), 'utf-8)') + + ' (' + addressInKeysFile + ')') + newItem.setData(0, Qt.UserRole, [str(addressInKeysFile), "inbox"]) + #set text color + if isEnabled: + if isMaillinglist: + brush = QtGui.QBrush(QtGui.QColor(137, 04, 177)) else: - brush = QtGui.QBrush(QtGui.QColor(128, 128, 128)) - brush.setStyle(QtCore.Qt.NoBrush) - newItem.setForeground(0, brush) + brush = QtGui.QBrush(QApplication.palette().text().color()) + else: + brush = QtGui.QBrush(QtGui.QColor(128, 128, 128)) + brush.setStyle(QtCore.Qt.NoBrush) + newItem.setForeground(0, brush) - for folder in folders: - newSubItem = QtGui.QTreeWidgetItem(newItem) + for folder in folders: + newSubItem = QtGui.QTreeWidgetItem(newItem) - cnt = cntUnreadMsg.get(addressInKeysFile + folder, False) - if cnt: - unreadText = " (" + str(cnt) + ")" - font = QtGui.QFont() - font.setBold(True) - newSubItem.setFont(0, font) - else: - unreadText = "" + cnt = cntUnreadMsg.get(addressInKeysFile + folder, False) + if cnt: + unreadText = " (" + str(cnt) + ")" + font = QtGui.QFont() + font.setBold(True) + newSubItem.setFont(0, font) + else: + unreadText = "" - newSubItem.setText(0, _translate("MainWindow", folder) + unreadText) - newSubItem.setData(0, Qt.UserRole, [str(addressInKeysFile), folder]) + newSubItem.setText(0, _translate("MainWindow", folder) + unreadText) + newSubItem.setData(0, Qt.UserRole, [str(addressInKeysFile), folder]) def __init__(self, parent=None): QtGui.QWidget.__init__(self, parent) From 6e0e160fb8fe28bc5dd685dd7c6259021bae478f Mon Sep 17 00:00:00 2001 From: mailchuck Date: Thu, 1 Oct 2015 10:18:33 +0200 Subject: [PATCH 024/210] Collapse disabled accounts --- src/bitmessageqt/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 1bb00457..5595850d 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -608,6 +608,7 @@ class MyForm(QtGui.QMainWindow): brush = QtGui.QBrush(QApplication.palette().text().color()) else: brush = QtGui.QBrush(QtGui.QColor(128, 128, 128)) + newItem.setExpanded(False) brush.setStyle(QtCore.Qt.NoBrush) newItem.setForeground(0, brush) From 7ee299919e277a93986e351604dad3ac04f82c40 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Thu, 1 Oct 2015 10:25:31 +0200 Subject: [PATCH 025/210] Address tree order sort case insensitive --- src/bitmessageqt/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 5595850d..7da486d2 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -175,13 +175,13 @@ def address_compare(x, y): return 1 if shared.config.getboolean(x, 'enabled') == shared.config.getboolean(y, 'enabled'): if shared.config.get(x, 'label'): - x1 = shared.config.get(x, 'label') + x1 = shared.config.get(x, 'label').decode('utf-8').lower() else: - x1 = x + x1 = x.decode('utf-8').lower() if shared.config.get(y, 'label'): - y1 = shared.config.get(y, 'label') + y1 = shared.config.get(y, 'label').decode('utf-8').lower() else: - y1 = y + y1 = y.decode('utf-8').lower() if x1 > y1: return 1 elif x1 < y1: From 4ab255d43034458a6d59f71ca3b91847e6a147d9 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Fri, 2 Oct 2015 15:04:16 +0200 Subject: [PATCH 026/210] OpenCL Fixes Still not fully working and hardcoded device number --- src/openclpow.py | 33 +++++++++++++++------------------ src/proofofwork.py | 9 +++++++-- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/openclpow.py b/src/openclpow.py index 65449b85..7ab4dba8 100644 --- a/src/openclpow.py +++ b/src/openclpow.py @@ -1,31 +1,28 @@ -import numpy +#!/usr/bin/env python2.7 from struct import pack, unpack import time import hashlib import random -import pyopencl as cl +import os -hash_dt = numpy.dtype([('target', numpy.uint64), ('v', numpy.str_, 73)]) ctx = False queue = False program = False try: - if (len(cl.get_platforms()) > 0): - ctx = cl.create_some_context() - queue = cl.CommandQueue(ctx) - - #f = open('/usr/src/PyBitmessage/src/kernel.cl', 'r') - import os - print "working directory: " + os.getcwd() -# time.sleep(5) - f = open('kernel.cl', 'r') - fstr = ''.join(f.readlines()) - program = cl.Program(ctx, fstr).build() + import numpy + import pyopencl as cl + hash_dt = numpy.dtype([('target', numpy.uint64), ('v', numpy.str_, 73)]) + if (len(cl.get_platforms()) > 0): + ctx = cl.create_some_context() + queue = cl.CommandQueue(ctx) + full_path = os.path.dirname(os.path.realpath(__file__)) + f = open(os.path.join(full_path, 'kernel.cl'), 'r') + fstr = ''.join(f.readlines()) + program = cl.Program(ctx, fstr).build(options="") except Exception as e: - print "opencl fail:" + str(e) -# time.sleep(5) - ctx = False + print "opencl fail:" + str(e) + ctx = False def has_opencl(): return (ctx != False) @@ -43,7 +40,7 @@ def do_opencl_pow(hash, target): dest_buf = cl.Buffer(ctx, cl.mem_flags.WRITE_ONLY, output.nbytes) kernel = program.kernel_sha512 - worksize = kernel.get_work_group_info(cl.kernel_work_group_info.WORK_GROUP_SIZE, cl.get_platforms()[0].get_devices()[0]) + worksize = kernel.get_work_group_info(cl.kernel_work_group_info.WORK_GROUP_SIZE, cl.get_platforms()[0].get_devices()[1]) kernel.set_arg(0, hash_buf) kernel.set_arg(1, dest_buf) diff --git a/src/proofofwork.py b/src/proofofwork.py index 85f79430..81f7e3df 100644 --- a/src/proofofwork.py +++ b/src/proofofwork.py @@ -73,7 +73,7 @@ def _doFastPoW(target, initialHash): return result[0], result[1] time.sleep(0.2) -def _doGPUPow(target, initialHash): +def _doGPUPoW(target, initialHash): print "GPU POW\n" nonce = openclpow.do_opencl_pow(initialHash.encode("hex"), target) trialValue, = unpack('>Q',hashlib.sha512(hashlib.sha512(pack('>Q',nonce) + initialHash).digest()).digest()[0:8]) @@ -83,7 +83,12 @@ def _doGPUPow(target, initialHash): def run(target, initialHash): target = int(target) if shared.safeConfigGetBoolean('bitmessagesettings', 'opencl') and openclpow.has_opencl(): - return _doGPUPow(target, initialHash) +# trialvalue1, nonce1 = _doGPUPoW(target, initialHash) +# trialvalue, nonce = _doFastPoW(target, initialHash) +# print "GPU: %s, %s" % (trialvalue1, nonce1) +# print "Fast: %s, %s" % (trialvalue, nonce) +# return [trialvalue, nonce] + return _doGPUPoW(target, initialHash) elif frozen == "macosx_app" or not frozen: return _doFastPoW(target, initialHash) else: From 943e0fdaa86a5b5e351aecb03d54eab19499f0a0 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Fri, 2 Oct 2015 15:04:47 +0200 Subject: [PATCH 027/210] Fixes Some pages were initialised with incorrect number of parameters. --- src/bitmessageqt/migrationwizard.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bitmessageqt/migrationwizard.py b/src/bitmessageqt/migrationwizard.py index 945adefa..6e80f1dc 100644 --- a/src/bitmessageqt/migrationwizard.py +++ b/src/bitmessageqt/migrationwizard.py @@ -72,9 +72,9 @@ class Ui_MigrationWizard(QtGui.QWizard): page = MigrationWizardIntroPage() self.setPage(0, page) self.setStartId(0) - page = MigrationWizardAddressesPage() + page = MigrationWizardAddressesPage(addresses) self.setPage(1, page) - page = MigrationWizardGPUPage(addresses) + page = MigrationWizardGPUPage() self.setPage(2, page) page = MigrationWizardConclusionPage() self.setPage(10, page) From 8d7fefe0e63d7ab72220d66991f2369094cd238f Mon Sep 17 00:00:00 2001 From: mailchuck Date: Fri, 2 Oct 2015 15:05:47 +0200 Subject: [PATCH 028/210] Migration Wizard - Not fully working yet - show this wizard and connection confirmation dialog before showing the main window, and don't open main window until these two have finished or were canceled --- src/bitmessageqt/__init__.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 7da486d2..b53d672f 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -1627,6 +1627,13 @@ class MyForm(QtGui.QMainWindow): else: self.click_actionSettings() + def showMigrationWizard(self, level): + self.migrationWizardInstance = Ui_MigrationWizard(["a"]) + if self.migrationWizardInstance.exec_(): + pass + else: + pass + def changeEvent(self, event): if event.type() == QtCore.QEvent.LanguageChange: self.ui.retranslateUi(self) @@ -4146,12 +4153,20 @@ def run(): app.setStyleSheet("QStatusBar::item { border: 0px solid black }") myapp = MyForm() - if not shared.config.getboolean('bitmessagesettings', 'startintray'): - myapp.show() - myapp.appIndicatorInit(app) myapp.ubuntuMessagingMenuInit() myapp.notifierInit() if shared.safeConfigGetBoolean('bitmessagesettings', 'dontconnect'): myapp.showConnectDialog() # ask the user if we may connect + + try: + if shared.config.get('bitmessagesettings', 'mailchuck') < 1: + myapp.showMigrationWizard(shared.config.get('bitmessagesettings', 'mailchuck')) + except: + myapp.showMigrationWizard(0) + + # only show after wizards and connect dialogs have completed + if not shared.config.getboolean('bitmessagesettings', 'startintray'): + myapp.show() + sys.exit(app.exec_()) From f2d1bd8c2f569fbcee06512c193c7fee3ba72149 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Fri, 2 Oct 2015 22:24:46 +0200 Subject: [PATCH 029/210] Address Tree updates The Address tree now is sorted and updates when number of unread messages changes. --- src/bitmessageqt/__init__.py | 279 +++++++++++---------------------- src/bitmessageqt/foldertree.py | 133 ++++++++++++++++ src/bitmessageqt/utils.py | 104 ++++++++++++ 3 files changed, 328 insertions(+), 188 deletions(-) create mode 100644 src/bitmessageqt/foldertree.py create mode 100644 src/bitmessageqt/utils.py diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index b53d672f..830daa43 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -6,6 +6,21 @@ try: except ImportError: MessagingMenu = None +try: + from PyQt4 import QtCore, QtGui + from PyQt4.QtCore import * + from PyQt4.QtGui import * + +except Exception as err: + print 'PyBitmessage requires PyQt unless you want to run it as a daemon and interact with it using the API. You can download it from http://www.riverbankcomputing.com/software/pyqt/download or by searching Google for \'PyQt Download\' (without quotes).' + print 'Error message:', err + sys.exit() + +try: + _encoding = QtGui.QApplication.UnicodeUTF8 +except AttributeError: + print 'QtGui.QApplication.UnicodeUTF8 error:', err + from addresses import * import shared from bitmessageui import * @@ -13,6 +28,7 @@ from namecoin import namecoinConnection, ensureNamecoinOptions from newaddressdialog import * from newaddresswizard import * from migrationwizard import * +from foldertree import * from addaddressdialog import * from newsubscriptiondialog import * from regenerateaddresses import * @@ -38,125 +54,12 @@ import subprocess import datetime from helper_sql import * import l10n - -try: - from PyQt4 import QtCore, QtGui - from PyQt4.QtCore import * - from PyQt4.QtGui import * - -except Exception as err: - print 'PyBitmessage requires PyQt unless you want to run it as a daemon and interact with it using the API. You can download it from http://www.riverbankcomputing.com/software/pyqt/download or by searching Google for \'PyQt Download\' (without quotes).' - print 'Error message:', err - sys.exit() - -try: - _encoding = QtGui.QApplication.UnicodeUTF8 -except AttributeError: - print 'QtGui.QApplication.UnicodeUTF8 error:', err +from utils import * +from collections import OrderedDict def _translate(context, text): return QtGui.QApplication.translate(context, text) - -def identiconize(address): - size = 48 - - # If you include another identicon library, please generate an - # example identicon with the following md5 hash: - # 3fd4bf901b9d4ea1394f0fb358725b28 - - try: - identicon_lib = shared.config.get('bitmessagesettings', 'identiconlib') - except: - # default to qidenticon_two_x - identicon_lib = 'qidenticon_two_x' - - # As an 'identiconsuffix' you could put "@bitmessge.ch" or "@bm.addr" to make it compatible with other identicon generators. (Note however, that E-Mail programs might convert the BM-address to lowercase first.) - # It can be used as a pseudo-password to salt the generation of the identicons to decrease the risk - # of attacks where someone creates an address to mimic someone else's identicon. - identiconsuffix = shared.config.get('bitmessagesettings', 'identiconsuffix') - - if not shared.config.getboolean('bitmessagesettings', 'useidenticons'): - idcon = QtGui.QIcon() - return idcon - - if (identicon_lib[:len('qidenticon')] == 'qidenticon'): - # print identicon_lib - # originally by: - # :Author:Shin Adachi - # Licesensed under FreeBSD License. - # stripped from PIL and uses QT instead (by sendiulo, same license) - import qidenticon - hash = hashlib.md5(addBMIfNotPresent(address)+identiconsuffix).hexdigest() - use_two_colors = (identicon_lib[:len('qidenticon_two')] == 'qidenticon_two') - opacity = int(not((identicon_lib == 'qidenticon_x') | (identicon_lib == 'qidenticon_two_x') | (identicon_lib == 'qidenticon_b') | (identicon_lib == 'qidenticon_two_b')))*255 - penwidth = 0 - image = qidenticon.render_identicon(int(hash, 16), size, use_two_colors, opacity, penwidth) - # filename = './images/identicons/'+hash+'.png' - # image.save(filename) - idcon = QtGui.QIcon() - idcon.addPixmap(image, QtGui.QIcon.Normal, QtGui.QIcon.Off) - return idcon - elif identicon_lib == 'pydenticon': - # print identicon_lib - # Here you could load pydenticon.py (just put it in the "src" folder of your Bitmessage source) - from pydenticon import Pydenticon - # It is not included in the source, because it is licensed under GPLv3 - # GPLv3 is a copyleft license that would influence our licensing - # Find the source here: http://boottunes.googlecode.com/svn-history/r302/trunk/src/pydenticon.py - # note that it requires PIL to be installed: http://www.pythonware.com/products/pil/ - idcon_render = Pydenticon(addBMIfNotPresent(address)+identiconsuffix, size*3) - rendering = idcon_render._render() - data = rendering.convert("RGBA").tostring("raw", "RGBA") - qim = QImage(data, size, size, QImage.Format_ARGB32) - pix = QPixmap.fromImage(qim) - idcon = QtGui.QIcon() - idcon.addPixmap(pix, QtGui.QIcon.Normal, QtGui.QIcon.Off) - return idcon - -def avatarize(address): - """ - loads a supported image for the given address' hash form 'avatars' folder - falls back to default avatar if 'default.*' file exists - falls back to identiconize(address) - """ - idcon = QtGui.QIcon() - hash = hashlib.md5(addBMIfNotPresent(address)).hexdigest() - str_broadcast_subscribers = '[Broadcast subscribers]' - if address == str_broadcast_subscribers: - # don't hash [Broadcast subscribers] - hash = address - # http://pyqt.sourceforge.net/Docs/PyQt4/qimagereader.html#supportedImageFormats - # print QImageReader.supportedImageFormats () - # QImageReader.supportedImageFormats () - extensions = ['PNG', 'GIF', 'JPG', 'JPEG', 'SVG', 'BMP', 'MNG', 'PBM', 'PGM', 'PPM', 'TIFF', 'XBM', 'XPM', 'TGA'] - # try to find a specific avatar - for ext in extensions: - lower_hash = shared.appdata + 'avatars/' + hash + '.' + ext.lower() - upper_hash = shared.appdata + 'avatars/' + hash + '.' + ext.upper() - if os.path.isfile(lower_hash): - # print 'found avatar of ', address - idcon.addFile(lower_hash) - return idcon - elif os.path.isfile(upper_hash): - # print 'found avatar of ', address - idcon.addFile(upper_hash) - return idcon - # if we haven't found any, try to find a default avatar - for ext in extensions: - lower_default = shared.appdata + 'avatars/' + 'default.' + ext.lower() - upper_default = shared.appdata + 'avatars/' + 'default.' + ext.upper() - if os.path.isfile(lower_default): - default = lower_default - idcon.addFile(lower_default) - return idcon - elif os.path.isfile(upper_default): - default = upper_default - idcon.addFile(upper_default) - return idcon - # If no avatar is found - return identiconize(address) - def change_translation(locale): global qtranslator qtranslator = QtCore.QTranslator() @@ -168,29 +71,6 @@ def change_translation(locale): qtranslator.load(translationpath) QtGui.QApplication.installTranslator(qtranslator) -def address_compare(x, y): - if x == "bitmessagesettings": - return -1 - elif y == "bitmessagesettings": - return 1 - if shared.config.getboolean(x, 'enabled') == shared.config.getboolean(y, 'enabled'): - if shared.config.get(x, 'label'): - x1 = shared.config.get(x, 'label').decode('utf-8').lower() - else: - x1 = x.decode('utf-8').lower() - if shared.config.get(y, 'label'): - y1 = shared.config.get(y, 'label').decode('utf-8').lower() - else: - y1 = y.decode('utf-8').lower() - if x1 > y1: - return 1 - elif x1 < y1: - return -1 - else: - return 0 - else: - return (-1 if shared.config.getboolean(x, 'enabled') else 1) - class MyForm(QtGui.QMainWindow): # sound type constants @@ -566,25 +446,21 @@ class MyForm(QtGui.QMainWindow): elif tab == 'chan': treeWidget = self.ui.treeWidgetChans - treeWidget.clear() + #treeWidget.clear() - # get number of (unread) messages - cntUnreadMsg = {} - queryreturn = sqlQuery('SELECT toaddress, folder, count(msgid) as cnt FROM inbox WHERE read = 0 GROUP BY toaddress, folder') - for row in queryreturn: - toaddress, folder, cnt = row - cntUnreadMsg[toaddress + folder] = cnt - - configSections = sorted(shared.config.sections(), cmp=address_compare) - for addressInKeysFile in configSections: - if addressInKeysFile == 'bitmessagesettings': + # init dictionary + db = {} + enabled = {} + + for toAddress in shared.config.sections(): + if toAddress == 'bitmessagesettings': continue isEnabled = shared.config.getboolean( - addressInKeysFile, 'enabled') + toAddress, 'enabled') isChan = shared.safeConfigGetBoolean( - addressInKeysFile, 'chan') + toAddress, 'chan') isMaillinglist = shared.safeConfigGetBoolean( - addressInKeysFile, 'mailinglist') + toAddress, 'mailinglist') if tab == 'messages': if isChan: @@ -593,39 +469,65 @@ class MyForm(QtGui.QMainWindow): if not isChan: continue - newItem = QtGui.QTreeWidgetItem(treeWidget) - newItem.setExpanded(True) - newItem.setIcon(0, avatarize(addressInKeysFile)) - newItem.setText(0, unicode( - shared.config.get(addressInKeysFile, 'label'), 'utf-8)') - + ' (' + addressInKeysFile + ')') - newItem.setData(0, Qt.UserRole, [str(addressInKeysFile), "inbox"]) - #set text color - if isEnabled: - if isMaillinglist: - brush = QtGui.QBrush(QtGui.QColor(137, 04, 177)) - else: - brush = QtGui.QBrush(QApplication.palette().text().color()) - else: - brush = QtGui.QBrush(QtGui.QColor(128, 128, 128)) - newItem.setExpanded(False) - brush.setStyle(QtCore.Qt.NoBrush) - newItem.setForeground(0, brush) - + db[toAddress] = {} for folder in folders: - newSubItem = QtGui.QTreeWidgetItem(newItem) + db[toAddress][folder] = 0 + + enabled[toAddress] = isEnabled - cnt = cntUnreadMsg.get(addressInKeysFile + folder, False) - if cnt: - unreadText = " (" + str(cnt) + ")" - font = QtGui.QFont() - font.setBold(True) - newSubItem.setFont(0, font) - else: - unreadText = "" + # get number of (unread) messages + queryreturn = sqlQuery('SELECT toaddress, folder, count(msgid) as cnt FROM inbox WHERE read = 0 GROUP BY toaddress, folder') + for row in queryreturn: + toaddress, folder, cnt = row + if toaddress in db and folder in db[toaddress]: + db[toaddress][folder] = cnt + + if treeWidget.isSortingEnabled(): + treeWidget.setSortingEnabled(False) + + widgets = {} + for i in range (0, treeWidget.topLevelItemCount()): + widget = treeWidget.topLevelItem(i) + toAddress = widget.address + + if not toAddress in db: + treeWidget.takeTopLevelItem(i) + i -= 1 + continue + unread = 0 + for j in range (0, widget.childCount()): + subwidget = widget.child(j) + try: + subwidget.setUnreadCount(db[toAddress][subwidget.folderName]) + unread += db[toAddress][subwidget.folderName] + db[toAddress].pop(subwidget.folderName, None) + except: + widget.takeChild(i) + j -= 1 - newSubItem.setText(0, _translate("MainWindow", folder) + unreadText) - newSubItem.setData(0, Qt.UserRole, [str(addressInKeysFile), folder]) + # add missing folders + if len(db[toAddress]) > 0: + i = 0 + for f, c in db[toAddress].iteritems(): + print "adding %s, %i" % (f, c) + subwidget = Ui_FolderWidget(widget, i, toAddress, f, c) + i += 1 + widget.setUnreadCount(unread) + db.pop(toAddress, None) + + i = 0 + for toAddress in db: + widget = Ui_AddressWidget(treeWidget, i, toAddress, db[toAddress]["inbox"]) + j = 0 + unread = 0 + for folder in folders: + subwidget = Ui_FolderWidget(widget, j, toAddress, folder, db[toAddress][folder]) + unread += db[toAddress][folder] + j += 1 + widget.setUnreadCount(unread) + i += 1 + + treeWidget.setSortingEnabled(True) def __init__(self, parent=None): QtGui.QWidget.__init__(self, parent) @@ -1851,6 +1753,12 @@ class MyForm(QtGui.QMainWindow): def changedInboxUnread(self): self.drawTrayIcon(self.currentTrayIconFileName, self.findInboxUnreadCount()) + if self.ui.tabWidget.currentIndex() == 0: + self.rerenderTabTreeMessages() + elif self.ui.tabWidget.currentIndex() == 2: + self.rerenderTabTreeSubscriptions() + elif self.ui.tabWidget.currentIndex() == 3: + self.rerenderTabTreeChans() def findInboxUnreadCount(self): queryreturn = sqlQuery('''SELECT count(*) from inbox WHERE folder='inbox' and read=0''') @@ -3613,12 +3521,7 @@ more work your computer must do to send the message. A Time-To-Live of four or f tableWidget.item(currentRow, 2).setFont(font) tableWidget.item(currentRow, 3).setFont(font) self.changedInboxUnread() -# if self.ui.tabWidget.currentIndex() == 0: -# self.rerenderTabTreeMessages() -# elif self.ui.tabWidget.currentIndex() == 2: -# self.rerenderTabTreeSubscriptions() -# elif self.ui.tabWidget.currentIndex() == 3: -# self.rerenderTabTreeChans() + else: data = self.getCurrentMessageId() if data != False: diff --git a/src/bitmessageqt/foldertree.py b/src/bitmessageqt/foldertree.py new file mode 100644 index 00000000..ca90ad60 --- /dev/null +++ b/src/bitmessageqt/foldertree.py @@ -0,0 +1,133 @@ +from PyQt4 import QtCore, QtGui + +from utils import * +import shared + +class Ui_FolderWidget(QtGui.QTreeWidgetItem): + folderWeight = {"inbox": 1, "sent": 2, "trash": 3} + def __init__(self, parent, pos = 0, address = "", folderName = "", unreadCount = 0): + super(QtGui.QTreeWidgetItem, self).__init__() + self.address = address + self.folderName = folderName + self.unreadCount = unreadCount + parent.insertChild(pos, self) + self.updateText() + + def setAddress(self, address): + self.address = str(address) + self.updateText() + + def setUnreadCount(self, cnt): + self.unreadCount = int(cnt) + self.updateText() + + def setFolderName(self, fname): + self.folderName = str(fname) + self.updateText() + + def updateText(self): + text = QtGui.QApplication.translate("MainWindow", self.folderName) + font = QtGui.QFont() + if self.unreadCount > 0: + text += " (" + str(self.unreadCount) + ")" + font.setBold(True) + else: + font.setBold(False) + self.setFont(0, font) + self.setText(0, text) + self.setToolTip(0, text) + self.setData(0, QtCore.Qt.UserRole, [self.address, self.folderName]) + + # inbox, sent, thrash first, rest alphabetically + def __lt__(self, other): + if (isinstance(other, Ui_FolderWidget)): + if self.folderName in self.folderWeight: + x = self.folderWeight[self.folderName] + else: + x = 4 + if other.folderName in self.folderWeight: + y = self.folderWeight[other.folderName] + else: + y = 4 + + if x == y: + return self.folderName > other.folderName + else: + return x > y + + return super(QtGui.QTreeWidgetItem, self).__lt__(other) + + +class Ui_AddressWidget(QtGui.QTreeWidgetItem): + def __init__(self, parent, pos = 0, address = "", unreadCount = 0): + super(QtGui.QTreeWidgetItem, self).__init__() + self.address = address + self.unreadCount = unreadCount + parent.insertTopLevelItem(pos, self) + # only set default when creating + #super(QtGui.QTreeWidgetItem, self).setExpanded(shared.config.getboolean(self.address, 'enabled')) + self.setExpanded(shared.safeConfigGetBoolean(self.address, 'enabled')) + self.updateText() + + def setAddress(self, address): + self.address = str(address) + self.updateText() + + def setUnreadCount(self, cnt): + self.unreadCount = int(cnt) + self.updateText() + + def updateText(self): + text = QtGui.QApplication.translate("MainWindow", + unicode(shared.config.get(self.address, 'label'), 'utf-8)') + + ' (' + self.address + ')') + + font = QtGui.QFont() + if self.unreadCount > 0: + # only show message count if the child doesn't show + if not self.isExpanded(): + text += " (" + str(self.unreadCount) + ")" + font.setBold(True) + else: + font.setBold(False) + self.setFont(0, font) + + #set text color + if shared.safeConfigGetBoolean(self.address, 'enabled'): + if shared.safeConfigGetBoolean(self.address, 'mailinglist'): + brush = QtGui.QBrush(QtGui.QColor(137, 04, 177)) + else: + brush = QtGui.QBrush(QtGui.QApplication.palette().text().color()) + #self.setExpanded(True) + else: + brush = QtGui.QBrush(QtGui.QColor(128, 128, 128)) + #self.setExpanded(False) + brush.setStyle(QtCore.Qt.NoBrush) + self.setForeground(0, brush) + + self.setIcon(0, avatarize(self.address)) + self.setText(0, text) + self.setToolTip(0, text) + self.setData(0, QtCore.Qt.UserRole, [self.address, "inbox"]) + + def setExpanded(self, expand): + super(Ui_AddressWidget, self).setExpanded(expand) + self.updateText() + + # label (or address) alphabetically, disabled at the end + def __lt__(self, other): + if (isinstance(other, Ui_AddressWidget)): + if shared.config.getboolean(self.address, 'enabled') == shared.config.getboolean(other.address, 'enabled'): + if shared.config.get(self.address, 'label'): + x = shared.config.get(self.address, 'label').decode('utf-8').lower() + else: + x = self.address.decode('utf-8').lower() + if shared.config.get(other.address, 'label'): + y = shared.config.get(other.address, 'label').decode('utf-8').lower() + else: + y = other.address.decode('utf-8').lower() + return y < x +# else: + return (False if shared.config.getboolean(self.address, 'enabled') else True) + + return super(QtGui.QTreeWidgetItem, self).__lt__(other) diff --git a/src/bitmessageqt/utils.py b/src/bitmessageqt/utils.py new file mode 100644 index 00000000..af58fd37 --- /dev/null +++ b/src/bitmessageqt/utils.py @@ -0,0 +1,104 @@ +from PyQt4 import QtGui +import hashlib +import os +import shared +from addresses import addBMIfNotPresent + +def identiconize(address): + size = 48 + + # If you include another identicon library, please generate an + # example identicon with the following md5 hash: + # 3fd4bf901b9d4ea1394f0fb358725b28 + + try: + identicon_lib = shared.config.get('bitmessagesettings', 'identiconlib') + except: + # default to qidenticon_two_x + identicon_lib = 'qidenticon_two_x' + + # As an 'identiconsuffix' you could put "@bitmessge.ch" or "@bm.addr" to make it compatible with other identicon generators. (Note however, that E-Mail programs might convert the BM-address to lowercase first.) + # It can be used as a pseudo-password to salt the generation of the identicons to decrease the risk + # of attacks where someone creates an address to mimic someone else's identicon. + identiconsuffix = shared.config.get('bitmessagesettings', 'identiconsuffix') + + if not shared.config.getboolean('bitmessagesettings', 'useidenticons'): + idcon = QtGui.QIcon() + return idcon + + if (identicon_lib[:len('qidenticon')] == 'qidenticon'): + # print identicon_lib + # originally by: + # :Author:Shin Adachi + # Licesensed under FreeBSD License. + # stripped from PIL and uses QT instead (by sendiulo, same license) + import qidenticon + hash = hashlib.md5(addBMIfNotPresent(address)+identiconsuffix).hexdigest() + use_two_colors = (identicon_lib[:len('qidenticon_two')] == 'qidenticon_two') + opacity = int(not((identicon_lib == 'qidenticon_x') | (identicon_lib == 'qidenticon_two_x') | (identicon_lib == 'qidenticon_b') | (identicon_lib == 'qidenticon_two_b')))*255 + penwidth = 0 + image = qidenticon.render_identicon(int(hash, 16), size, use_two_colors, opacity, penwidth) + # filename = './images/identicons/'+hash+'.png' + # image.save(filename) + idcon = QtGui.QIcon() + idcon.addPixmap(image, QtGui.QIcon.Normal, QtGui.QIcon.Off) + return idcon + elif identicon_lib == 'pydenticon': + # print identicon_lib + # Here you could load pydenticon.py (just put it in the "src" folder of your Bitmessage source) + from pydenticon import Pydenticon + # It is not included in the source, because it is licensed under GPLv3 + # GPLv3 is a copyleft license that would influence our licensing + # Find the source here: http://boottunes.googlecode.com/svn-history/r302/trunk/src/pydenticon.py + # note that it requires PIL to be installed: http://www.pythonware.com/products/pil/ + idcon_render = Pydenticon(addBMIfNotPresent(address)+identiconsuffix, size*3) + rendering = idcon_render._render() + data = rendering.convert("RGBA").tostring("raw", "RGBA") + qim = QImage(data, size, size, QImage.Format_ARGB32) + pix = QPixmap.fromImage(qim) + idcon = QtGui.QIcon() + idcon.addPixmap(pix, QtGui.QIcon.Normal, QtGui.QIcon.Off) + return idcon + +def avatarize(address): + """ + loads a supported image for the given address' hash form 'avatars' folder + falls back to default avatar if 'default.*' file exists + falls back to identiconize(address) + """ + idcon = QtGui.QIcon() + hash = hashlib.md5(addBMIfNotPresent(address)).hexdigest() + str_broadcast_subscribers = '[Broadcast subscribers]' + if address == str_broadcast_subscribers: + # don't hash [Broadcast subscribers] + hash = address + # http://pyqt.sourceforge.net/Docs/PyQt4/qimagereader.html#supportedImageFormats + # print QImageReader.supportedImageFormats () + # QImageReader.supportedImageFormats () + extensions = ['PNG', 'GIF', 'JPG', 'JPEG', 'SVG', 'BMP', 'MNG', 'PBM', 'PGM', 'PPM', 'TIFF', 'XBM', 'XPM', 'TGA'] + # try to find a specific avatar + for ext in extensions: + lower_hash = shared.appdata + 'avatars/' + hash + '.' + ext.lower() + upper_hash = shared.appdata + 'avatars/' + hash + '.' + ext.upper() + if os.path.isfile(lower_hash): + # print 'found avatar of ', address + idcon.addFile(lower_hash) + return idcon + elif os.path.isfile(upper_hash): + # print 'found avatar of ', address + idcon.addFile(upper_hash) + return idcon + # if we haven't found any, try to find a default avatar + for ext in extensions: + lower_default = shared.appdata + 'avatars/' + 'default.' + ext.lower() + upper_default = shared.appdata + 'avatars/' + 'default.' + ext.upper() + if os.path.isfile(lower_default): + default = lower_default + idcon.addFile(lower_default) + return idcon + elif os.path.isfile(upper_default): + default = upper_default + idcon.addFile(upper_default) + return idcon + # If no avatar is found + return identiconize(address) From bf65aa60cb99ea6c28ff347433c60e922a659f27 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sat, 3 Oct 2015 01:17:47 +0200 Subject: [PATCH 030/210] Email Gateway UI implementation First steps, only a tiny part works --- src/bitmessageqt/__init__.py | 47 ++++++++++++++++--------- src/bitmessageqt/account.py | 63 ++++++++++++++++++++++++++++++++++ src/bitmessageqt/foldertree.py | 4 +-- 3 files changed, 96 insertions(+), 18 deletions(-) create mode 100644 src/bitmessageqt/account.py diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 830daa43..54098a80 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -56,6 +56,7 @@ from helper_sql import * import l10n from utils import * from collections import OrderedDict +from account import * def _translate(context, text): return QtGui.QApplication.translate(context, text) @@ -1004,9 +1005,13 @@ class MyForm(QtGui.QMainWindow): font = QFont() font.setBold(True) queryreturn = sqlQuery(sqlStatement, account, folder, what) + acct = None for row in queryreturn: msgid, toAddress, fromAddress, subject, received, read = row + if acct is None: + acct = accountClass(toAddress) subject = shared.fixPotentiallyInvalidUTF8Data(subject) + acct.parseMessage(toAddress, fromAddress, subject, "") try: if toAddress == self.str_broadcast_subscribers: toLabel = self.str_broadcast_subscribers @@ -1018,6 +1023,8 @@ class MyForm(QtGui.QMainWindow): toLabel = toAddress fromLabel = '' + if type(acct) == MailchuckAccount: + fromLabel = acct.fromAddress if shared.config.has_section(fromAddress): fromLabel = shared.config.get(fromAddress, 'label') @@ -1066,8 +1073,8 @@ class MyForm(QtGui.QMainWindow): from_item.setIcon(avatarize(fromAddress)) tableWidget.setItem(0, 1, from_item) # subject - subject_item = QtGui.QTableWidgetItem(unicode(subject, 'utf-8')) - subject_item.setToolTip(unicode(subject, 'utf-8')) + subject_item = QtGui.QTableWidgetItem(unicode(acct.subject, 'utf-8')) + subject_item.setToolTip(unicode(acct.subject, 'utf-8')) subject_item.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) if not read: @@ -1751,11 +1758,12 @@ class MyForm(QtGui.QMainWindow): def drawTrayIcon(self, iconFileName, inboxUnreadCount): self.tray.setIcon(self.calcTrayIcon(iconFileName, inboxUnreadCount)) - def changedInboxUnread(self): + def changedInboxUnread(self, row = None): self.drawTrayIcon(self.currentTrayIconFileName, self.findInboxUnreadCount()) - if self.ui.tabWidget.currentIndex() == 0: - self.rerenderTabTreeMessages() - elif self.ui.tabWidget.currentIndex() == 2: + self.rerenderTabTreeMessages() + if not row is None: + row[1], row[6] + if self.ui.tabWidget.currentIndex() == 2: self.rerenderTabTreeSubscriptions() elif self.ui.tabWidget.currentIndex() == 3: self.rerenderTabTreeChans() @@ -2279,6 +2287,8 @@ more work your computer must do to send the message. A Time-To-Live of four or f font = QFont() font.setBold(True) self.ui.tableWidgetInbox.setSortingEnabled(False) + account = accountClass(toAddress) + account.parseMessage(toAddress, fromAddress, subject, message) newItem = QtGui.QTableWidgetItem(unicode(toLabel, 'utf-8')) newItem.setToolTip(unicode(toLabel, 'utf-8')) newItem.setFont(font) @@ -2291,7 +2301,12 @@ more work your computer must do to send the message. A Time-To-Live of four or f newItem.setIcon(avatarize(toAddress)) self.ui.tableWidgetInbox.setItem(0, 0, newItem) - if fromLabel == '': + if type(account) is MailchuckAccount: + newItem = QtGui.QTableWidgetItem(unicode(account.fromAddress, 'utf-8')) + newItem.setToolTip(unicode(account.fromAddress, 'utf-8')) + if shared.config.getboolean('bitmessagesettings', 'showtraynotifications'): + self.notifierShow(unicode(_translate("MainWindow",'New Message').toUtf8(),'utf-8'), unicode(_translate("MainWindow",'From ').toUtf8(),'utf-8') + unicode(account.fromAddress, 'utf-8'), self.SOUND_UNKNOWN, None) + elif fromLabel == '': newItem = QtGui.QTableWidgetItem(unicode(fromAddress, 'utf-8')) newItem.setToolTip(unicode(fromAddress, 'utf-8')) if shared.config.getboolean('bitmessagesettings', 'showtraynotifications'): @@ -2306,7 +2321,7 @@ more work your computer must do to send the message. A Time-To-Live of four or f newItem.setIcon(avatarize(fromAddress)) self.ui.tableWidgetInbox.setItem(0, 1, newItem) newItem = QtGui.QTableWidgetItem(unicode(subject, 'utf-8)')) - newItem.setToolTip(unicode(subject, 'utf-8)')) + newItem.setToolTip(unicode(account.subject, 'utf-8)')) #newItem.setData(Qt.UserRole, unicode(message, 'utf-8)')) # No longer hold the message in the table; we'll use a SQL query to display it as needed. newItem.setFont(font) self.ui.tableWidgetInbox.setItem(0, 2, newItem) @@ -3270,25 +3285,25 @@ more work your computer must do to send the message. A Time-To-Live of four or f # Group of functions for the Your Identities dialog box def getCurrentAccount(self): - treeWidget = self.getCurrentTreeWidget() + #treeWidget = self.getCurrentTreeWidget() + treeWidget = self.ui.treeWidgetYourIdentities if treeWidget: currentItem = treeWidget.currentItem() if currentItem: - accountFolder = currentItem.data(0, Qt.UserRole).toPyObject() - account = accountFolder[0] - return str(account) + account = currentItem.address + return account else: # TODO need debug msg? return False def getCurrentFolder(self): - treeWidget = self.getCurrentTreeWidget() + #treeWidget = self.getCurrentTreeWidget() + treeWidget = self.ui.treeWidgetYourIdentities if treeWidget: currentItem = treeWidget.currentItem() if currentItem: - accountFolder = currentItem.data(0, Qt.UserRole).toPyObject() - folder = accountFolder[1] - return str(folder) + account = currentItem.folderName + return account else: # TODO need debug msg? return False diff --git a/src/bitmessageqt/account.py b/src/bitmessageqt/account.py new file mode 100644 index 00000000..b8171bc6 --- /dev/null +++ b/src/bitmessageqt/account.py @@ -0,0 +1,63 @@ +from PyQt4 import QtCore, QtGui + +import shared +import re +import sys + +def accountClass(address): + if not shared.config.has_section(address): + return None + try: + gateway = shared.config.get(address, "gateway") + if (gateway == "mailchuck"): + return MailchuckAccount(address) + else: + return GatewayAccount(address) + except: + return BMAccount(address) + +class BMAccount(object): + def __init__(self, address): + self.address = address + + def parseMessage(self, toAddress, fromAddress, subject, message): + self.toAddress = toAddress + self.fromAddress = fromAddress + self.subject = subject + self.message = message + +class GatewayAccount(BMAccount): + def __init__(self, address): + super(BMAccount, self).__init__(address) + + def parseMessage(self, toAddress, fromAddress, subject, message): + super(BMAccount, self).parseMessage(toAddress, fromAddress, subject, message) + +class MailchuckAccount(GatewayAccount): + registrationAddress = "BM-2cVYYrhaY5Gbi3KqrX9Eae2NRNrkfrhCSA" + unregistrationAddress = "BM-2cVMAHTRjZHCTPMue75XBK5Tco175DtJ9J" + relayAddress = "BM-2cWim8aZwUNqxzjMxstnUMtVEUQJeezstf" + regExpIncoming = re.compile("(.*)MAILCHUCK-FROM::(\S+) \| (.*)") + regExpOutgoing = re.compile("(\S+) (.*)") + def __init__(self, address): + super(GatewayAccount, self).__init__(address) + + def parseMessage(self, toAddress, fromAddress, subject, message): + super(GatewayAccount, self).parseMessage(toAddress, fromAddress, subject, message) + if fromAddress == self.relayAddress: + matches = self.regExpIncoming.search(subject) + if not matches is None: + self.subject = "" + if not matches.group(1) is None: + self.subject += matches.group(1) + if not matches.group(3) is None: + self.subject += matches.group(3) + if not matches.group(2) is None: + self.fromAddress = matches.group(2) + if toAddress == self.relayAddress: + matches = self.regExpOutgoing.search(subject) + if not matches is None: + if not matches.group(2) is None: + self.subject = matches.group(2) + if not matches.group(1) is None: + self.toAddress = matches.group(1) \ No newline at end of file diff --git a/src/bitmessageqt/foldertree.py b/src/bitmessageqt/foldertree.py index ca90ad60..78524187 100644 --- a/src/bitmessageqt/foldertree.py +++ b/src/bitmessageqt/foldertree.py @@ -36,7 +36,7 @@ class Ui_FolderWidget(QtGui.QTreeWidgetItem): self.setFont(0, font) self.setText(0, text) self.setToolTip(0, text) - self.setData(0, QtCore.Qt.UserRole, [self.address, self.folderName]) + # self.setData(0, QtCore.Qt.UserRole, [self.address, self.folderName]) # inbox, sent, thrash first, rest alphabetically def __lt__(self, other): @@ -108,7 +108,7 @@ class Ui_AddressWidget(QtGui.QTreeWidgetItem): self.setIcon(0, avatarize(self.address)) self.setText(0, text) self.setToolTip(0, text) - self.setData(0, QtCore.Qt.UserRole, [self.address, "inbox"]) +# self.setData(0, QtCore.Qt.UserRole, [self.address, "inbox"]) def setExpanded(self, expand): super(Ui_AddressWidget, self).setExpanded(expand) From 04059a6f0e872a2d2b1f9bb84f2666a5d391a9c1 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sat, 3 Oct 2015 12:12:18 +0200 Subject: [PATCH 031/210] Gateway update and sort fix - shows gateway parser results more accurately - gateway class assigned dynamically - inbox sort order is aware of what you click on and defaults to ascending --- src/bitmessageqt/__init__.py | 228 +++++++++++---------------------- src/bitmessageqt/account.py | 46 +++++-- src/bitmessageqt/foldertree.py | 15 ++- 3 files changed, 123 insertions(+), 166 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 54098a80..fee80891 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -447,7 +447,9 @@ class MyForm(QtGui.QMainWindow): elif tab == 'chan': treeWidget = self.ui.treeWidgetChans - #treeWidget.clear() + # sort ascending when creating + if treeWidget.topLevelItemCount() == 0: + treeWidget.header().setSortIndicator(0, Qt.AscendingOrder) # init dictionary db = {} @@ -860,6 +862,10 @@ class MyForm(QtGui.QMainWindow): else: where = "toaddress || fromaddress || subject || message" + tableWidget.setColumnHidden(0, False) + tableWidget.setColumnHidden(1, True) + tableWidget.setSortingEnabled(False) + sqlStatement = ''' SELECT toaddress, fromaddress, subject, status, ackdata, lastactiontime FROM sent WHERE fromaddress=? AND folder="sent" AND %s LIKE ? @@ -868,60 +874,34 @@ class MyForm(QtGui.QMainWindow): while tableWidget.rowCount() > 0: tableWidget.removeRow(0) - + acct = None queryreturn = sqlQuery(sqlStatement, account, what) for row in queryreturn: toAddress, fromAddress, subject, status, ackdata, lastactiontime = row subject = shared.fixPotentiallyInvalidUTF8Data(subject) - - if shared.config.has_section(fromAddress): - fromLabel = shared.config.get(fromAddress, 'label') - else: - fromLabel = fromAddress - - toLabel = '' - queryreturn = sqlQuery( - '''select label from addressbook where address=?''', toAddress) - if queryreturn != []: - for row in queryreturn: - toLabel, = row - if toLabel == '': - # It might be a broadcast message. We should check for that - # label. - queryreturn = sqlQuery( - '''select label from subscriptions where address=?''', toAddress) - - if queryreturn != []: - for row in queryreturn: - toLabel, = row - - if toLabel == '': - if shared.config.has_section(toAddress): - toLabel = shared.config.get(toAddress, 'label') - if toLabel == '': - toLabel = toAddress + if acct is None: + acct = accountClass(fromAddress) + acct.parseMessage(toAddress, fromAddress, subject, "") tableWidget.insertRow(0) - toAddressItem = QtGui.QTableWidgetItem(unicode(toLabel, 'utf-8')) - toAddressItem.setToolTip(unicode(toLabel, 'utf-8')) + toAddressItem = QtGui.QTableWidgetItem(unicode(acct.toLabel, 'utf-8')) + toAddressItem.setToolTip(unicode(acct.toLabel, 'utf-8')) toAddressItem.setIcon(avatarize(toAddress)) toAddressItem.setData(Qt.UserRole, str(toAddress)) toAddressItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) tableWidget.setItem(0, 0, toAddressItem) - if fromLabel == '': - fromLabel = fromAddress - fromAddressItem = QtGui.QTableWidgetItem(unicode(fromLabel, 'utf-8')) - fromAddressItem.setToolTip(unicode(fromLabel, 'utf-8')) + fromAddressItem = QtGui.QTableWidgetItem(unicode(acct.fromLabel, 'utf-8')) + fromAddressItem.setToolTip(unicode(acct.fromLabel, 'utf-8')) fromAddressItem.setIcon(avatarize(fromAddress)) fromAddressItem.setData(Qt.UserRole, str(fromAddress)) fromAddressItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) tableWidget.setItem(0, 1, fromAddressItem) - subjectItem = QtGui.QTableWidgetItem(unicode(subject, 'utf-8')) - subjectItem.setToolTip(unicode(subject, 'utf-8')) + subjectItem = QtGui.QTableWidgetItem(unicode(acct.subject, 'utf-8')) + subjectItem.setToolTip(unicode(acct.subject, 'utf-8')) subjectItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) tableWidget.setItem(0, 2, subjectItem) @@ -972,7 +952,9 @@ class MyForm(QtGui.QMainWindow): newItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) tableWidget.setItem(0, 3, newItem) - tableWidget.sortItems(3, Qt.DescendingOrder) + + tableWidget.setSortingEnabled(False) + tableWidget.horizontalHeader().setSortIndicator(3, Qt.DescendingOrder) tableWidget.keyPressEvent = self.tableWidgetInboxKeyPressEvent # Load messages from database file @@ -993,62 +975,43 @@ class MyForm(QtGui.QMainWindow): else: where = "toaddress || fromaddress || subject || message" - sqlStatement = ''' - SELECT msgid, toaddress, fromaddress, subject, received, read - FROM inbox WHERE toaddress=? AND folder=? AND %s LIKE ? - ORDER BY received - ''' % (where) + if folder != False: + sqlStatement = ''' + SELECT folder, msgid, toaddress, fromaddress, subject, received, read + FROM inbox WHERE toaddress=? AND folder=? AND %s LIKE ? + ORDER BY received + ''' % (where) + queryreturn = sqlQuery(sqlStatement, account, folder, what) + else: + sqlStatement = ''' + SELECT folder, msgid, toaddress, fromaddress, subject, received, read + FROM inbox WHERE toaddress=? AND folder != "trash" AND %s LIKE ? + ORDER BY received + ''' % (where) + queryreturn = sqlQuery(sqlStatement, account, what) while tableWidget.rowCount() > 0: tableWidget.removeRow(0) + tableWidget.setColumnHidden(0, True) + tableWidget.setColumnHidden(1, False) + tableWidget.setSortingEnabled(False) + font = QFont() font.setBold(True) - queryreturn = sqlQuery(sqlStatement, account, folder, what) acct = None for row in queryreturn: - msgid, toAddress, fromAddress, subject, received, read = row + msgfolder, msgid, toAddress, fromAddress, subject, received, read = row if acct is None: acct = accountClass(toAddress) subject = shared.fixPotentiallyInvalidUTF8Data(subject) acct.parseMessage(toAddress, fromAddress, subject, "") - try: - if toAddress == self.str_broadcast_subscribers: - toLabel = self.str_broadcast_subscribers - else: - toLabel = shared.config.get(toAddress, 'label') - except: - toLabel = '' - if toLabel == '': - toLabel = toAddress - - fromLabel = '' - if type(acct) == MailchuckAccount: - fromLabel = acct.fromAddress - if shared.config.has_section(fromAddress): - fromLabel = shared.config.get(fromAddress, 'label') - - if fromLabel == '': # If the fromAddress isn't one of our addresses and isn't a chan - queryreturn = sqlQuery( - '''select label from addressbook where address=?''', fromAddress) - if queryreturn != []: - for row in queryreturn: - fromLabel, = row - - if fromLabel == '': # If this address wasn't in our address book... - queryreturn = sqlQuery( - '''select label from subscriptions where address=?''', fromAddress) - if queryreturn != []: - for row in queryreturn: - fromLabel, = row - if fromLabel == '': - fromLabel = fromAddress # message row tableWidget.insertRow(0) # to - to_item = QtGui.QTableWidgetItem(unicode(toLabel, 'utf-8')) - to_item.setToolTip(unicode(toLabel, 'utf-8')) + to_item = QtGui.QTableWidgetItem(unicode(acct.toLabel, 'utf-8')) + to_item.setToolTip(unicode(acct.toLabel, 'utf-8')) to_item.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) if not read: @@ -1061,8 +1024,8 @@ class MyForm(QtGui.QMainWindow): to_item.setIcon(avatarize(toAddress)) tableWidget.setItem(0, 0, to_item) # from - from_item = QtGui.QTableWidgetItem(unicode(fromLabel, 'utf-8')) - from_item.setToolTip(unicode(fromLabel, 'utf-8')) + from_item = QtGui.QTableWidgetItem(unicode(acct.fromLabel, 'utf-8')) + from_item.setToolTip(unicode(acct.fromLabel, 'utf-8')) from_item.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) if not read: @@ -1091,7 +1054,8 @@ class MyForm(QtGui.QMainWindow): time_item.setFont(font) tableWidget.setItem(0, 3, time_item) - tableWidget.sortItems(3, Qt.DescendingOrder) + tableWidget.horizontalHeader().setSortIndicator(3, Qt.DescendingOrder) + tableWidget.setSortingEnabled(True) tableWidget.keyPressEvent = self.tableWidgetInboxKeyPressEvent # create application indicator @@ -1761,8 +1725,8 @@ class MyForm(QtGui.QMainWindow): def changedInboxUnread(self, row = None): self.drawTrayIcon(self.currentTrayIconFileName, self.findInboxUnreadCount()) self.rerenderTabTreeMessages() - if not row is None: - row[1], row[6] +# if not row is None: +# row[1], row[6] if self.ui.tabWidget.currentIndex() == 2: self.rerenderTabTreeSubscriptions() elif self.ui.tabWidget.currentIndex() == 3: @@ -2214,35 +2178,23 @@ more work your computer must do to send the message. A Time-To-Live of four or f return subject = shared.fixPotentiallyInvalidUTF8Data(subject) message = shared.fixPotentiallyInvalidUTF8Data(message) - try: - fromLabel = shared.config.get(fromAddress, 'label') - except: - fromLabel = '' - if fromLabel == '': - fromLabel = fromAddress + acct = accountClass(fromAddress) + acct.parseMessage(toAddress, fromAddress, subject, message) self.ui.tableWidgetInbox.setSortingEnabled(False) self.ui.tableWidgetInbox.insertRow(0) - if toLabel == '': - newItem = QtGui.QTableWidgetItem(unicode(toAddress, 'utf-8')) - newItem.setToolTip(unicode(toAddress, 'utf-8')) - else: - newItem = QtGui.QTableWidgetItem(unicode(toLabel, 'utf-8')) - newItem.setToolTip(unicode(toLabel, 'utf-8')) + newItem = QtGui.QTableWidgetItem(unicode(acct.toLabel, 'utf-8')) + newItem.setToolTip(unicode(acct.toLabel, 'utf-8')) newItem.setData(Qt.UserRole, str(toAddress)) newItem.setIcon(avatarize(toAddress)) self.ui.tableWidgetInbox.setItem(0, 0, newItem) - if fromLabel == '': - newItem = QtGui.QTableWidgetItem(unicode(fromAddress, 'utf-8')) - newItem.setToolTip(unicode(fromAddress, 'utf-8')) - else: - newItem = QtGui.QTableWidgetItem(unicode(fromLabel, 'utf-8')) - newItem.setToolTip(unicode(fromLabel, 'utf-8')) + newItem = QtGui.QTableWidgetItem(unicode(acct.fromLabel, 'utf-8')) + newItem.setToolTip(unicode(acct.fromLabel, 'utf-8')) newItem.setData(Qt.UserRole, str(fromAddress)) newItem.setIcon(avatarize(fromAddress)) self.ui.tableWidgetInbox.setItem(0, 1, newItem) - newItem = QtGui.QTableWidgetItem(unicode(subject, 'utf-8)')) - newItem.setToolTip(unicode(subject, 'utf-8)')) + newItem = QtGui.QTableWidgetItem(unicode(acct.subject, 'utf-8)')) + newItem.setToolTip(unicode(acct.subject, 'utf-8)')) #newItem.setData(Qt.UserRole, unicode(message, 'utf-8)')) # No longer hold the message in the table; we'll use a SQL query to display it as needed. self.ui.tableWidgetInbox.setItem(0, 2, newItem) # newItem = QtGui.QTableWidgetItem('Doing work necessary to send @@ -2257,40 +2209,17 @@ more work your computer must do to send the message. A Time-To-Live of four or f self.ui.tableWidgetInbox.setSortingEnabled(True) def displayNewInboxMessage(self, inventoryHash, toAddress, fromAddress, subject, message): - if self.getCurrentFolder() != "inbox" or self.getCurrentAccount() != toAddress: + if (self.getCurrentFolder() != "inbox" and self.getCurrentFolder() != False) or self.getCurrentAccount() != toAddress: return subject = shared.fixPotentiallyInvalidUTF8Data(subject) - fromLabel = '' - queryreturn = sqlQuery( - '''select label from addressbook where address=?''', fromAddress) - if queryreturn != []: - for row in queryreturn: - fromLabel, = row - else: - # There might be a label in the subscriptions table - queryreturn = sqlQuery( - '''select label from subscriptions where address=?''', fromAddress) - if queryreturn != []: - for row in queryreturn: - fromLabel, = row - - try: - if toAddress == self.str_broadcast_subscribers: - toLabel = self.str_broadcast_subscribers - else: - toLabel = shared.config.get(toAddress, 'label') - except: - toLabel = '' - if toLabel == '': - toLabel = toAddress + acct = accountClass(toAddress) + acct.parseMessage(toAddress, fromAddress, subject, message) font = QFont() font.setBold(True) self.ui.tableWidgetInbox.setSortingEnabled(False) - account = accountClass(toAddress) - account.parseMessage(toAddress, fromAddress, subject, message) - newItem = QtGui.QTableWidgetItem(unicode(toLabel, 'utf-8')) - newItem.setToolTip(unicode(toLabel, 'utf-8')) + newItem = QtGui.QTableWidgetItem(unicode(acct.toLabel, 'utf-8')) + newItem.setToolTip(unicode(acct.toLabel, 'utf-8')) newItem.setFont(font) newItem.setData(Qt.UserRole, str(toAddress)) if shared.safeConfigGetBoolean(str(toAddress), 'mailinglist'): @@ -2301,27 +2230,16 @@ more work your computer must do to send the message. A Time-To-Live of four or f newItem.setIcon(avatarize(toAddress)) self.ui.tableWidgetInbox.setItem(0, 0, newItem) - if type(account) is MailchuckAccount: - newItem = QtGui.QTableWidgetItem(unicode(account.fromAddress, 'utf-8')) - newItem.setToolTip(unicode(account.fromAddress, 'utf-8')) - if shared.config.getboolean('bitmessagesettings', 'showtraynotifications'): - self.notifierShow(unicode(_translate("MainWindow",'New Message').toUtf8(),'utf-8'), unicode(_translate("MainWindow",'From ').toUtf8(),'utf-8') + unicode(account.fromAddress, 'utf-8'), self.SOUND_UNKNOWN, None) - elif fromLabel == '': - newItem = QtGui.QTableWidgetItem(unicode(fromAddress, 'utf-8')) - newItem.setToolTip(unicode(fromAddress, 'utf-8')) - if shared.config.getboolean('bitmessagesettings', 'showtraynotifications'): - self.notifierShow(unicode(_translate("MainWindow",'New Message').toUtf8(),'utf-8'), unicode(_translate("MainWindow",'From ').toUtf8(),'utf-8') + unicode(fromAddress, 'utf-8'), self.SOUND_UNKNOWN, None) - else: - newItem = QtGui.QTableWidgetItem(unicode(fromLabel, 'utf-8')) - newItem.setToolTip(unicode(unicode(fromLabel, 'utf-8'))) - if shared.config.getboolean('bitmessagesettings', 'showtraynotifications'): - self.notifierShow(unicode(_translate("MainWindow",'New Message').toUtf8(),'utf-8'), unicode(_translate("MainWindow",'From ').toUtf8(),'utf-8') + unicode(fromLabel, 'utf-8'), self.SOUND_KNOWN, unicode(fromLabel, 'utf-8')) + newItem = QtGui.QTableWidgetItem(unicode(acct.fromLabel, 'utf-8')) + newItem.setToolTip(unicode(acct.fromLabel, 'utf-8')) + if shared.config.getboolean('bitmessagesettings', 'showtraynotifications'): + self.notifierShow(unicode(_translate("MainWindow",'New Message').toUtf8(),'utf-8'), unicode(_translate("MainWindow",'From ').toUtf8(),'utf-8') + unicode(acct.fromLabel, 'utf-8'), self.SOUND_UNKNOWN, None) newItem.setData(Qt.UserRole, str(fromAddress)) newItem.setFont(font) newItem.setIcon(avatarize(fromAddress)) self.ui.tableWidgetInbox.setItem(0, 1, newItem) - newItem = QtGui.QTableWidgetItem(unicode(subject, 'utf-8)')) - newItem.setToolTip(unicode(account.subject, 'utf-8)')) + newItem = QtGui.QTableWidgetItem(unicode(acct.subject, 'utf-8)')) + newItem.setToolTip(unicode(acct.subject, 'utf-8)')) #newItem.setData(Qt.UserRole, unicode(message, 'utf-8)')) # No longer hold the message in the table; we'll use a SQL query to display it as needed. newItem.setFont(font) self.ui.tableWidgetInbox.setItem(0, 2, newItem) @@ -2332,7 +2250,7 @@ more work your computer must do to send the message. A Time-To-Live of four or f newItem.setFont(font) self.ui.tableWidgetInbox.setItem(0, 3, newItem) self.ui.tableWidgetInbox.setSortingEnabled(True) - self.ubuntuMessagingMenuUpdate(True, newItem, toLabel) + self.ubuntuMessagingMenuUpdate(True, newItem, self.toLabel) def click_pushButtonAddAddressBook(self): self.AddAddressDialogInstance = AddAddressDialog(self) @@ -2953,11 +2871,14 @@ more work your computer must do to send the message. A Time-To-Live of four or f tableWidget = self.getCurrentMessagelist() if not tableWidget: return + unread = False while tableWidget.selectedIndexes() != []: currentRow = tableWidget.selectedIndexes()[0].row() inventoryHashToTrash = str(tableWidget.item( currentRow, 3).data(Qt.UserRole).toPyObject()) sqlExecute('''UPDATE inbox SET folder='trash' WHERE msgid=?''', inventoryHashToTrash) + if tableWidget.item(currentRow, 0).font().bold(): + unread = True self.ui.textEditInboxMessage.setText("") tableWidget.removeRow(currentRow) self.statusBar().showMessage(_translate( @@ -2966,6 +2887,8 @@ more work your computer must do to send the message. A Time-To-Live of four or f tableWidget.selectRow(currentRow) else: tableWidget.selectRow(currentRow - 1) + if unread: + changedInboxUnread() def on_action_InboxSaveMessageAs(self): tableWidget = self.getCurrentMessagelist() @@ -3301,9 +3224,8 @@ more work your computer must do to send the message. A Time-To-Live of four or f treeWidget = self.ui.treeWidgetYourIdentities if treeWidget: currentItem = treeWidget.currentItem() - if currentItem: - account = currentItem.folderName - return account + if currentItem and hasattr(currentItem, 'folderName'): + return currentItem.folderName else: # TODO need debug msg? return False @@ -3516,7 +3438,7 @@ more work your computer must do to send the message. A Time-To-Live of four or f refresh = False for row in queryreturn: message, read = row - if folder == 'inbox' and read == 0: + if folder != 'sent' and read == 0: markread = sqlQuery( '''UPDATE inbox SET read = 1 WHERE msgid = ?''', msgid) refresh = True diff --git a/src/bitmessageqt/account.py b/src/bitmessageqt/account.py index b8171bc6..0ff0fa23 100644 --- a/src/bitmessageqt/account.py +++ b/src/bitmessageqt/account.py @@ -3,30 +3,58 @@ from PyQt4 import QtCore, QtGui import shared import re import sys +import inspect +from helper_sql import * def accountClass(address): if not shared.config.has_section(address): return None try: gateway = shared.config.get(address, "gateway") - if (gateway == "mailchuck"): - return MailchuckAccount(address) - else: - return GatewayAccount(address) + for name, cls in inspect.getmembers(sys.modules[__name__], inspect.isclass): +# obj = g(address) + if issubclass(cls, GatewayAccount) and cls.gatewayName == gateway: + return cls(address) + # general gateway + return GatewayAccount(address) except: - return BMAccount(address) + pass + # no gateway + return BMAccount(address) class BMAccount(object): - def __init__(self, address): + def __init__(self, address = None): self.address = address + def getLabel(self, address = None): + if address is None: + address = self.address + label = address + if shared.config.has_section(address): + label = shared.config.get(address, 'label') + queryreturn = sqlQuery( + '''select label from addressbook where address=?''', address) + if queryreturn != []: + for row in queryreturn: + label, = row + else: + queryreturn = sqlQuery( + '''select label from subscriptions where address=?''', address) + if queryreturn != []: + for row in queryreturn: + label, = row + return label + def parseMessage(self, toAddress, fromAddress, subject, message): self.toAddress = toAddress self.fromAddress = fromAddress self.subject = subject self.message = message + self.fromLabel = self.getLabel(fromAddress) + self.toLabel = self.getLabel(toAddress) class GatewayAccount(BMAccount): + gatewayName = None def __init__(self, address): super(BMAccount, self).__init__(address) @@ -34,6 +62,8 @@ class GatewayAccount(BMAccount): super(BMAccount, self).parseMessage(toAddress, fromAddress, subject, message) class MailchuckAccount(GatewayAccount): + # set "gateway" in keys.dat to this + gatewayName = "mailchuck" registrationAddress = "BM-2cVYYrhaY5Gbi3KqrX9Eae2NRNrkfrhCSA" unregistrationAddress = "BM-2cVMAHTRjZHCTPMue75XBK5Tco175DtJ9J" relayAddress = "BM-2cWim8aZwUNqxzjMxstnUMtVEUQJeezstf" @@ -53,11 +83,11 @@ class MailchuckAccount(GatewayAccount): if not matches.group(3) is None: self.subject += matches.group(3) if not matches.group(2) is None: - self.fromAddress = matches.group(2) + self.fromLabel = matches.group(2) if toAddress == self.relayAddress: matches = self.regExpOutgoing.search(subject) if not matches is None: if not matches.group(2) is None: self.subject = matches.group(2) if not matches.group(1) is None: - self.toAddress = matches.group(1) \ No newline at end of file + self.toLabel = matches.group(1) \ No newline at end of file diff --git a/src/bitmessageqt/foldertree.py b/src/bitmessageqt/foldertree.py index 78524187..41824b91 100644 --- a/src/bitmessageqt/foldertree.py +++ b/src/bitmessageqt/foldertree.py @@ -49,11 +49,13 @@ class Ui_FolderWidget(QtGui.QTreeWidgetItem): y = self.folderWeight[other.folderName] else: y = 4 - + reverse = False + if self.treeWidget().header().sortIndicatorOrder() == QtCore.Qt.DescendingOrder: + reverse = True if x == y: - return self.folderName > other.folderName + return self.folderName < other.folderName else: - return x > y + return (x >= y if reverse else x < y) return super(QtGui.QTreeWidgetItem, self).__lt__(other) @@ -117,6 +119,9 @@ class Ui_AddressWidget(QtGui.QTreeWidgetItem): # label (or address) alphabetically, disabled at the end def __lt__(self, other): if (isinstance(other, Ui_AddressWidget)): + reverse = False + if self.treeWidget().header().sortIndicatorOrder() == QtCore.Qt.DescendingOrder: + reverse = True if shared.config.getboolean(self.address, 'enabled') == shared.config.getboolean(other.address, 'enabled'): if shared.config.get(self.address, 'label'): x = shared.config.get(self.address, 'label').decode('utf-8').lower() @@ -126,8 +131,8 @@ class Ui_AddressWidget(QtGui.QTreeWidgetItem): y = shared.config.get(other.address, 'label').decode('utf-8').lower() else: y = other.address.decode('utf-8').lower() - return y < x + return x < y # else: - return (False if shared.config.getboolean(self.address, 'enabled') else True) + return (not reverse if shared.config.getboolean(self.address, 'enabled') else reverse) return super(QtGui.QTreeWidgetItem, self).__lt__(other) From 77a843b4449417b96e73331a832fcf226fbadb6c Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sat, 3 Oct 2015 17:24:21 +0200 Subject: [PATCH 032/210] Email gateway integration Sending/receiving from the send tab, reply from inbox and registration/unregistration context menu. --- src/bitmessageqt/__init__.py | 89 +++++++++++++++--- src/bitmessageqt/account.py | 52 +++++++++- src/bitmessageqt/emailgateway.py | 64 +++++++++++++ src/bitmessageqt/emailgateway.ui | 157 +++++++++++++++++++++++++++++++ 4 files changed, 347 insertions(+), 15 deletions(-) create mode 100644 src/bitmessageqt/emailgateway.py create mode 100644 src/bitmessageqt/emailgateway.ui diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index fee80891..8c64ae8e 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -34,6 +34,7 @@ from newsubscriptiondialog import * from regenerateaddresses import * from newchandialog import * from specialaddressbehavior import * +from emailgateway import * from settings import * from about import * from help import * @@ -213,6 +214,10 @@ class MyForm(QtGui.QMainWindow): _translate( "MainWindow", "Special address behavior..."), self.on_action_SpecialAddressBehaviorDialog) + self.actionEmailGateway = self.ui.addressContextMenuToolbarYourIdentities.addAction( + _translate( + "MainWindow", "Email gateway"), + self.on_action_EmailGatewayDialog) self.ui.treeWidgetYourIdentities.setContextMenuPolicy( QtCore.Qt.CustomContextMenu) @@ -230,6 +235,7 @@ class MyForm(QtGui.QMainWindow): self.popMenuYourIdentities.addAction(self.actionDisableYourIdentities) self.popMenuYourIdentities.addAction(self.actionSetAvatarYourIdentities) self.popMenuYourIdentities.addAction(self.actionSpecialAddressBehaviorYourIdentities) + self.popMenuYourIdentities.addAction(self.actionEmailGateway) def init_chan_popup_menu(self, connectSignal=True): # Popup menu for the Channels tab @@ -902,6 +908,7 @@ class MyForm(QtGui.QMainWindow): subjectItem = QtGui.QTableWidgetItem(unicode(acct.subject, 'utf-8')) subjectItem.setToolTip(unicode(acct.subject, 'utf-8')) + subjectItem.setData(Qt.UserRole, str(subject)) subjectItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) tableWidget.setItem(0, 2, subjectItem) @@ -1038,6 +1045,7 @@ class MyForm(QtGui.QMainWindow): # subject subject_item = QtGui.QTableWidgetItem(unicode(acct.subject, 'utf-8')) subject_item.setToolTip(unicode(acct.subject, 'utf-8')) + subject_item.setData(Qt.UserRole, str(subject)) subject_item.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) if not read: @@ -1966,6 +1974,8 @@ more work your computer must do to send the message. A Time-To-Live of four or f QMessageBox.about(self, _translate("MainWindow", "Message too long"), _translate( "MainWindow", "The message that you are trying to send is too long by %1 bytes. (The maximum is 261644 bytes). Please cut it down before sending.").arg(len(message) - (2 ** 18 - 500))) return + + acct = accountClass(fromAddress) if sendMessageToPeople: # To send a message to specific people (rather than broadcast) toAddressesList = [s.strip() @@ -1974,6 +1984,12 @@ more work your computer must do to send the message. A Time-To-Live of four or f toAddressesList)) # remove duplicate addresses. If the user has one address with a BM- and the same address without the BM-, this will not catch it. They'll send the message to the person twice. for toAddress in toAddressesList: if toAddress != '': + if toAddress.find("@") >= 0 and isinstance(acct, GatewayAccount): + acct.createMessage(toAddress, fromAddress, subject, message) + subject = acct.subject + toAddress = acct.toAddress + print "Subject: %s" % (subject) + print "address: %s" % (toAddress) status, addressVersionNumber, streamNumber, ripe = decodeAddress( toAddress) if status != 'success': @@ -2195,6 +2211,8 @@ more work your computer must do to send the message. A Time-To-Live of four or f self.ui.tableWidgetInbox.setItem(0, 1, newItem) newItem = QtGui.QTableWidgetItem(unicode(acct.subject, 'utf-8)')) newItem.setToolTip(unicode(acct.subject, 'utf-8)')) + newItem.setData(Qt.UserRole, str(subject)) + #newItem.setData(Qt.UserRole, unicode(message, 'utf-8)')) # No longer hold the message in the table; we'll use a SQL query to display it as needed. self.ui.tableWidgetInbox.setItem(0, 2, newItem) # newItem = QtGui.QTableWidgetItem('Doing work necessary to send @@ -2240,6 +2258,8 @@ more work your computer must do to send the message. A Time-To-Live of four or f self.ui.tableWidgetInbox.setItem(0, 1, newItem) newItem = QtGui.QTableWidgetItem(unicode(acct.subject, 'utf-8)')) newItem.setToolTip(unicode(acct.subject, 'utf-8)')) + newItem.setData(Qt.UserRole, str(subject)) + #newItem.setData(Qt.UserRole, unicode(message, 'utf-8)')) # No longer hold the message in the table; we'll use a SQL query to display it as needed. newItem.setFont(font) self.ui.tableWidgetInbox.setItem(0, 2, newItem) @@ -2629,6 +2649,30 @@ more work your computer must do to send the message. A Time-To-Live of four or f shared.writeKeysFile() self.rerenderInboxToLabels() + def on_action_EmailGatewayDialog(self): + self.dialog = EmailGatewayDialog(self) + # For Modal dialogs + if self.dialog.exec_(): + addressAtCurrentRow = self.getCurrentAccount() + acct = accountClass(addressAtCurrentRow) + if isinstance(acct, GatewayAccount) and self.dialog.ui.radioButtonUnregister.isChecked(): + print "unregister" + acct.unregister() + shared.config.remove_option(addressAtCurrentRow, 'gateway') + shared.writeKeysFile() + elif (not isinstance(acct, GatewayAccount)) and self.dialog.ui.radioButtonRegister.isChecked(): + print "register" + email = str(self.dialog.ui.lineEditEmail.text().toUtf8()) + acct = MailchuckAccount(addressAtCurrentRow) + acct.register(email) + shared.config.set(addressAtCurrentRow, 'label', email) + shared.config.set(addressAtCurrentRow, 'gateway', 'mailchuck') + shared.writeKeysFile() + else: + print "well nothing" +# shared.writeKeysFile() +# self.rerenderInboxToLabels() + def click_NewAddressDialog(self): addresses = [] configSections = shared.config.sections() @@ -2636,11 +2680,11 @@ more work your computer must do to send the message. A Time-To-Live of four or f if addressInKeysFile == 'bitmessagesettings': continue addresses.append(addressInKeysFile) - self.dialog = Ui_NewAddressWizard(addresses) - self.dialog.exec_() +# self.dialog = Ui_NewAddressWizard(addresses) +# self.dialog.exec_() # print "Name: " + self.dialog.field("name").toString() # print "Email: " + self.dialog.field("email").toString() - return +# return self.dialog = NewAddressDialog(self) # For Modal dialogs if self.dialog.exec_(): @@ -2787,6 +2831,7 @@ more work your computer must do to send the message. A Time-To-Live of four or f currentInboxRow = tableWidget.currentRow() toAddressAtCurrentInboxRow = str(tableWidget.item( currentInboxRow, 0).data(Qt.UserRole).toPyObject()) + acct = accountClass(toAddressAtCurrentInboxRow) fromAddressAtCurrentInboxRow = str(tableWidget.item( currentInboxRow, 1).data(Qt.UserRole).toPyObject()) msgid = str(tableWidget.item( @@ -2796,6 +2841,7 @@ more work your computer must do to send the message. A Time-To-Live of four or f if queryreturn != []: for row in queryreturn: messageAtCurrentInboxRow, = row + acct.parseMessage(toAddressAtCurrentInboxRow, fromAddressAtCurrentInboxRow, str(tableWidget.item(currentInboxRow, 2).data(Qt.UserRole).toPyObject()), messageAtCurrentInboxRow) if toAddressAtCurrentInboxRow == self.str_broadcast_subscribers: #TODO what does this if?.. a = a @@ -2808,7 +2854,7 @@ more work your computer must do to send the message. A Time-To-Live of four or f else: self.setBroadcastEnablementDependingOnWhetherThisIsAChanAddress(toAddressAtCurrentInboxRow) - self.ui.lineEditTo.setText(str(fromAddressAtCurrentInboxRow)) + self.ui.lineEditTo.setText(str(acct.fromLabel)) # If the previous message was to a chan then we should send our reply to the chan rather than to the particular person who sent the message. if shared.config.has_section(toAddressAtCurrentInboxRow): @@ -2825,12 +2871,10 @@ more work your computer must do to send the message. A Time-To-Live of four or f quotedText = self.quoted_text(unicode(messageAtCurrentInboxRow, 'utf-8')) self.ui.textEditMessage.setText(quotedText) - if tableWidget.item(currentInboxRow, 2).text()[0:3] in ['Re:', 'RE:']: - self.ui.lineEditSubject.setText( - tableWidget.item(currentInboxRow, 2).text()) + if acct.subject[0:3] in ['Re:', 'RE:']: + self.ui.lineEditSubject.setText(acct.subject) else: - self.ui.lineEditSubject.setText( - 'Re: ' + tableWidget.item(currentInboxRow, 2).text()) + self.ui.lineEditSubject.setText('Re: ' + acct.subject) self.ui.tabWidgetSend.setCurrentIndex(0) self.ui.tabWidget.setCurrentIndex(1) @@ -3772,6 +3816,23 @@ class SpecialAddressBehaviorDialog(QtGui.QDialog): QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) +class EmailGatewayDialog(QtGui.QDialog): + + def __init__(self, parent): + QtGui.QWidget.__init__(self, parent) + self.ui = Ui_EmailGatewayDialog() + self.ui.setupUi(self) + self.parent = parent + addressAtCurrentRow = parent.getCurrentAccount() + acct = accountClass(addressAtCurrentRow) +# if isinstance(acct, GatewayAccount): + label = shared.config.get(addressAtCurrentRow, 'label') + if label.find("@mailchuck.com") > -1: + self.ui.lineEditEmail.setText(label) + + QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) + + class AddAddressDialog(QtGui.QDialog): def __init__(self, parent): @@ -3999,11 +4060,11 @@ def run(): if shared.safeConfigGetBoolean('bitmessagesettings', 'dontconnect'): myapp.showConnectDialog() # ask the user if we may connect - try: - if shared.config.get('bitmessagesettings', 'mailchuck') < 1: - myapp.showMigrationWizard(shared.config.get('bitmessagesettings', 'mailchuck')) - except: - myapp.showMigrationWizard(0) +# try: +# if shared.config.get('bitmessagesettings', 'mailchuck') < 1: +# myapp.showMigrationWizard(shared.config.get('bitmessagesettings', 'mailchuck')) +# except: +# myapp.showMigrationWizard(0) # only show after wizards and connect dialogs have completed if not shared.config.getboolean('bitmessagesettings', 'startintray'): diff --git a/src/bitmessageqt/account.py b/src/bitmessageqt/account.py index 0ff0fa23..05613e47 100644 --- a/src/bitmessageqt/account.py +++ b/src/bitmessageqt/account.py @@ -5,6 +5,9 @@ import re import sys import inspect from helper_sql import * +from addresses import decodeAddress +from pyelliptic.openssl import OpenSSL +import time def accountClass(address): if not shared.config.has_section(address): @@ -57,6 +60,31 @@ class GatewayAccount(BMAccount): gatewayName = None def __init__(self, address): super(BMAccount, self).__init__(address) + + def send(self): + status, addressVersionNumber, streamNumber, ripe = decodeAddress(self.toAddress) + ackdata = OpenSSL.rand(32) + t = () + sqlExecute( + '''INSERT INTO sent VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)''', + '', + self.toAddress, + ripe, + self.fromAddress, + self.subject, + self.message, + 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. + 'msgqueued', + 0, # retryNumber + 'sent', # folder + 2, # encodingtype + shared.config.getint('bitmessagesettings', 'ttl') + ) + + shared.workerQueue.put(('sendmessage', self.toAddress)) def parseMessage(self, toAddress, fromAddress, subject, message): super(BMAccount, self).parseMessage(toAddress, fromAddress, subject, message) @@ -71,6 +99,26 @@ class MailchuckAccount(GatewayAccount): regExpOutgoing = re.compile("(\S+) (.*)") def __init__(self, address): super(GatewayAccount, self).__init__(address) + + def createMessage(self, toAddress, fromAddress, subject, message): + self.subject = toAddress + " " + subject + self.toAddress = self.relayAddress + self.fromAddress = fromAddress + self.message = message + + def register(self, email): + self.toAddress = self.registrationAddress + self.subject = email + self.message = "" + self.fromAddress = self.address + self.send() + + def unregister(self): + self.toAddress = self.unregistrationAddress + self.subject = "" + self.message = "" + self.fromAddress = self.address + self.send() def parseMessage(self, toAddress, fromAddress, subject, message): super(GatewayAccount, self).parseMessage(toAddress, fromAddress, subject, message) @@ -84,10 +132,12 @@ class MailchuckAccount(GatewayAccount): self.subject += matches.group(3) if not matches.group(2) is None: self.fromLabel = matches.group(2) + self.fromAddress = matches.group(2) if toAddress == self.relayAddress: matches = self.regExpOutgoing.search(subject) if not matches is None: if not matches.group(2) is None: self.subject = matches.group(2) if not matches.group(1) is None: - self.toLabel = matches.group(1) \ No newline at end of file + self.toLabel = matches.group(1) + self.toAddress = matches.group(1) \ No newline at end of file diff --git a/src/bitmessageqt/emailgateway.py b/src/bitmessageqt/emailgateway.py new file mode 100644 index 00000000..0df4dd01 --- /dev/null +++ b/src/bitmessageqt/emailgateway.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'emailgateway.ui' +# +# Created: Fri Apr 26 17:43:31 2013 +# by: PyQt4 UI code generator 4.9.4 +# +# WARNING! All changes made in this file will be lost! + +from PyQt4 import QtCore, QtGui + +try: + _fromUtf8 = QtCore.QString.fromUtf8 +except AttributeError: + _fromUtf8 = lambda s: s + +class Ui_EmailGatewayDialog(object): + def setupUi(self, EmailGatewayDialog): + EmailGatewayDialog.setObjectName(_fromUtf8("EmailGatewayDialog")) + EmailGatewayDialog.resize(386, 172) + self.gridLayout = QtGui.QGridLayout(EmailGatewayDialog) + self.gridLayout.setObjectName(_fromUtf8("gridLayout")) + self.radioButtonRegister = QtGui.QRadioButton(EmailGatewayDialog) + self.radioButtonRegister.setChecked(True) + self.radioButtonRegister.setObjectName(_fromUtf8("radioButtonRegister")) + self.gridLayout.addWidget(self.radioButtonRegister, 1, 0, 1, 1) + self.radioButtonUnregister = QtGui.QRadioButton(EmailGatewayDialog) + self.radioButtonUnregister.setObjectName(_fromUtf8("radioButtonUnregister")) + self.gridLayout.addWidget(self.radioButtonUnregister, 4, 0, 1, 1) + self.label = QtGui.QLabel(EmailGatewayDialog) + self.label.setWordWrap(True) + self.label.setObjectName(_fromUtf8("label")) + self.gridLayout.addWidget(self.label, 0, 0, 1, 1) + self.label_2 = QtGui.QLabel(EmailGatewayDialog) + self.label_2.setObjectName(_fromUtf8("label_2")) + self.gridLayout.addWidget(self.label_2, 2, 0, 1, 1) + self.lineEditEmail = QtGui.QLineEdit(EmailGatewayDialog) + self.lineEditEmail.setEnabled(True) + self.lineEditEmail.setObjectName(_fromUtf8("lineEditEmail")) + self.gridLayout.addWidget(self.lineEditEmail, 3, 0, 1, 1) + self.buttonBox = QtGui.QDialogButtonBox(EmailGatewayDialog) + self.buttonBox.setMinimumSize(QtCore.QSize(368, 0)) + 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, 5, 0, 1, 1) + + self.retranslateUi(EmailGatewayDialog) + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("accepted()")), EmailGatewayDialog.accept) + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("rejected()")), EmailGatewayDialog.reject) + QtCore.QObject.connect(self.radioButtonRegister, QtCore.SIGNAL(_fromUtf8("clicked(bool)")), self.lineEditEmail.setEnabled) + QtCore.QObject.connect(self.radioButtonUnregister, QtCore.SIGNAL(_fromUtf8("clicked(bool)")), self.lineEditEmail.setDisabled) + QtCore.QMetaObject.connectSlotsByName(EmailGatewayDialog) + EmailGatewayDialog.setTabOrder(self.radioButtonRegister, self.lineEditEmail) + EmailGatewayDialog.setTabOrder(self.lineEditEmail, self.radioButtonUnregister) + EmailGatewayDialog.setTabOrder(self.radioButtonUnregister, self.buttonBox) + + def retranslateUi(self, EmailGatewayDialog): + EmailGatewayDialog.setWindowTitle(QtGui.QApplication.translate("EmailGatewayDialog", "Email gateway", None, QtGui.QApplication.UnicodeUTF8)) + self.radioButtonRegister.setText(QtGui.QApplication.translate("EmailGatewayDialog", "Register on email gateway", None, QtGui.QApplication.UnicodeUTF8)) + self.radioButtonUnregister.setText(QtGui.QApplication.translate("EmailGatewayDialog", "Unregister from email gateway", None, QtGui.QApplication.UnicodeUTF8)) + self.label.setText(QtGui.QApplication.translate("EmailGatewayDialog", "Email gateway allows you to communicate with email users. Currently, only the Mailchuck email gateway (@mailchuck.com) is available.", None, QtGui.QApplication.UnicodeUTF8)) + self.label_2.setText(QtGui.QApplication.translate("EmailGatewayDialog", "Desired email address (including @mailchuck.com):", None, QtGui.QApplication.UnicodeUTF8)) + diff --git a/src/bitmessageqt/emailgateway.ui b/src/bitmessageqt/emailgateway.ui new file mode 100644 index 00000000..927df46a --- /dev/null +++ b/src/bitmessageqt/emailgateway.ui @@ -0,0 +1,157 @@ + + + EmailGatewayDialog + + + + 0 + 0 + 386 + 172 + + + + Email gateway + + + + + + true + + + Desired email address (including @mailchuck.com) + + + + + + + Register on email gateway + + + true + + + + + + + + 368 + 0 + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + true + + + @mailchuck.com + + + + + + + Email gateway alows you to communicate with email users. Currently, only the Mailchuck email gateway (@mailchuck.com) is available. + + + true + + + + + + + Unregister from email gateway + + + false + + + + + + + radioButtonRegister + lineEditEmailAddress + buttonBox + + + + + buttonBox + accepted() + EmailGatewayDialog + accept() + + + 227 + 152 + + + 157 + 171 + + + + + buttonBox + rejected() + EmailGatewayDialog + reject() + + + 295 + 158 + + + 286 + 171 + + + + + radioButtonRegister + clicked(bool) + lineEditEmailAddress + setEnabled(bool) + + + 95 + 40 + + + 94 + 123 + + + + + radioButtonUnregister + clicked(bool) + lineEditEmailAddress + setDisabled(bool) + + + 139 + 19 + + + 187 + 123 + + + + + From 8c8795cfa48718f82623ed179239a466390741ed Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sun, 4 Oct 2015 10:47:51 +0200 Subject: [PATCH 033/210] Editable address labels Address labels can be edited now (F2 or double click) --- src/bitmessageqt/__init__.py | 22 +++++++++++++++++++++- src/bitmessageqt/foldertree.py | 8 +++++--- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 8c64ae8e..5d2202a0 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -534,8 +534,11 @@ class MyForm(QtGui.QMainWindow): unread += db[toAddress][folder] j += 1 widget.setUnreadCount(unread) + if (tab == 'messages'): + print "setting %s editable" % (toAddress) + widget.setFlags (widget.flags() | QtCore.Qt.ItemIsEditable) i += 1 - + treeWidget.setSortingEnabled(True) def __init__(self, parent=None): @@ -645,6 +648,8 @@ class MyForm(QtGui.QMainWindow): # tree address lists QtCore.QObject.connect(self.ui.treeWidgetYourIdentities, QtCore.SIGNAL( "itemSelectionChanged ()"), self.treeWidgetItemClicked) + QtCore.QObject.connect(self.ui.treeWidgetYourIdentities, QtCore.SIGNAL( + "itemChanged (QTreeWidgetItem *, int)"), self.treeWidgetItemChanged) QtCore.QObject.connect(self.ui.treeWidgetSubscriptions, QtCore.SIGNAL( "itemSelectionChanged ()"), self.treeWidgetItemClicked) QtCore.QObject.connect(self.ui.treeWidgetChans, QtCore.SIGNAL( @@ -3461,6 +3466,21 @@ more work your computer must do to send the message. A Time-To-Live of four or f folder = self.getCurrentFolder() self.loadMessagelist(messagelist, account, folder) + def treeWidgetItemChanged(self, item, column): + widget = self.getCurrentTreeWidget() + if item.address == widget.currentItem().address: + newLabel = str(item.text(0)) + newLabel = newLabel.replace("(" + str(item.address) + ")", '') + newLabel = newLabel.rstrip() + oldLabel = shared.config.get(str(item.address), 'label') + oldLabel = oldLabel.replace("(" + str(item.address) + ")", '') + oldLabel = oldLabel.rstrip() + if newLabel == oldLabel: + return + shared.config.set(str(item.address), 'label', newLabel) + item.updateText() + shared.writeKeysFile() + def tableWidgetInboxItemClicked(self): folder = self.getCurrentFolder() messageTextedit = self.getCurrentMessageTextedit() diff --git a/src/bitmessageqt/foldertree.py b/src/bitmessageqt/foldertree.py index 41824b91..2a907711 100644 --- a/src/bitmessageqt/foldertree.py +++ b/src/bitmessageqt/foldertree.py @@ -80,9 +80,7 @@ class Ui_AddressWidget(QtGui.QTreeWidgetItem): self.updateText() def updateText(self): - text = QtGui.QApplication.translate("MainWindow", - unicode(shared.config.get(self.address, 'label'), 'utf-8)') - + ' (' + self.address + ')') + text = unicode(shared.config.get(self.address, 'label'), 'utf-8)') + ' (' + self.address + ')' font = QtGui.QFont() if self.unreadCount > 0: @@ -116,6 +114,10 @@ class Ui_AddressWidget(QtGui.QTreeWidgetItem): super(Ui_AddressWidget, self).setExpanded(expand) self.updateText() + def edit(self): + self.setText(0, shared.config.get(self.address, 'label')) + super(QtGui.QAbstractItemView, self).edit() + # label (or address) alphabetically, disabled at the end def __lt__(self, other): if (isinstance(other, Ui_AddressWidget)): From 8ff790396457603ea0631dde95cd74622f787587 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sun, 4 Oct 2015 10:57:14 +0200 Subject: [PATCH 034/210] Fix: do not update account name from folder name Last commit caused changes in unread count of folder to change the account name. This is now fixed. --- src/bitmessageqt/__init__.py | 37 ++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 5d2202a0..5aa6c7da 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -535,7 +535,6 @@ class MyForm(QtGui.QMainWindow): j += 1 widget.setUnreadCount(unread) if (tab == 'messages'): - print "setting %s editable" % (toAddress) widget.setFlags (widget.flags() | QtCore.Qt.ItemIsEditable) i += 1 @@ -3467,19 +3466,29 @@ more work your computer must do to send the message. A Time-To-Live of four or f self.loadMessagelist(messagelist, account, folder) def treeWidgetItemChanged(self, item, column): - widget = self.getCurrentTreeWidget() - if item.address == widget.currentItem().address: - newLabel = str(item.text(0)) - newLabel = newLabel.replace("(" + str(item.address) + ")", '') - newLabel = newLabel.rstrip() - oldLabel = shared.config.get(str(item.address), 'label') - oldLabel = oldLabel.replace("(" + str(item.address) + ")", '') - oldLabel = oldLabel.rstrip() - if newLabel == oldLabel: - return - shared.config.set(str(item.address), 'label', newLabel) - item.updateText() - shared.writeKeysFile() + # only for manual edits. automatic edits (setText) are ignored + if column != 0: + return + # only account names + if not isinstance(item, Ui_AddressWidget): + return + # only currently selected item + if item.address != self.getCurrentTreeWidget().currentItem().address: + return + + newLabel = str(item.text(0)) + newLabel = newLabel.replace("(" + str(item.address) + ")", '') + newLabel = newLabel.rstrip() + oldLabel = shared.config.get(str(item.address), 'label') + oldLabel = oldLabel.replace("(" + str(item.address) + ")", '') + oldLabel = oldLabel.rstrip() + # unchanged, do not do anything either + if newLabel == oldLabel: + return + + shared.config.set(str(item.address), 'label', newLabel) + item.updateText() + shared.writeKeysFile() def tableWidgetInboxItemClicked(self): folder = self.getCurrentFolder() From 0f2a9d6e8ff87549bfbfa9900c2013f921490b1e Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sun, 4 Oct 2015 11:47:06 +0200 Subject: [PATCH 035/210] Fix: gateway registration updates label When registering on a gateway, the label change will be displayed immediately. --- src/bitmessageqt/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 5aa6c7da..d846de5b 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2671,6 +2671,7 @@ more work your computer must do to send the message. A Time-To-Live of four or f acct.register(email) shared.config.set(addressAtCurrentRow, 'label', email) shared.config.set(addressAtCurrentRow, 'gateway', 'mailchuck') + self.getCurrentTreeWidget().currentItem().updateText() shared.writeKeysFile() else: print "well nothing" From aa29c49829caf1506d1a38e48857ca9d6bebaff1 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sun, 4 Oct 2015 20:32:17 +0200 Subject: [PATCH 036/210] Fix: recurse prevention In case something screws up, prevent recursive changes through treeWidgetItemChagned --- src/bitmessageqt/__init__.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index d846de5b..b62ff8a4 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -587,6 +587,8 @@ class MyForm(QtGui.QMainWindow): self.timer.start(2000) # milliseconds QtCore.QObject.connect(self.timer, QtCore.SIGNAL("timeout()"), self.runEveryTwoSeconds) + self.recurDepth = 0 + self.init_file_menu() self.init_inbox_popup_menu() self.init_identities_popup_menu() @@ -595,7 +597,7 @@ class MyForm(QtGui.QMainWindow): self.init_chan_popup_menu() self.init_sent_popup_menu() self.init_blacklist_popup_menu() - + # Initialize the user's list of addresses on the 'Chan' tab. self.rerenderTabTreeChans() @@ -3486,10 +3488,16 @@ more work your computer must do to send the message. A Time-To-Live of four or f # unchanged, do not do anything either if newLabel == oldLabel: return - + + # recursion prevention + if self.recurDepth > 0: + return + + self.recurDepth += 1 shared.config.set(str(item.address), 'label', newLabel) item.updateText() shared.writeKeysFile() + self.recurDepth -= 1 def tableWidgetInboxItemClicked(self): folder = self.getCurrentFolder() From f4f2425b035dc4ab66d01ebdc18752033267e926 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Mon, 5 Oct 2015 10:32:56 +0200 Subject: [PATCH 037/210] Chan updates - chans now work with the new interface, still some bugs present - chans do not have a sent folder anymore (you'll see sent messages in the sent folder of the account you're sending it from) --- src/bitmessageqt/__init__.py | 16 +++++++++------- src/bitmessageqt/foldertree.py | 14 +++++++++++--- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index b62ff8a4..50fccf59 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -447,11 +447,12 @@ class MyForm(QtGui.QMainWindow): self.rerenderTabTree('chan') def rerenderTabTree(self, tab): - folders = ['inbox', 'sent', 'trash'] if tab == 'messages': treeWidget = self.ui.treeWidgetYourIdentities + folders = ['inbox', 'sent', 'trash'] elif tab == 'chan': treeWidget = self.ui.treeWidgetChans + folders = ['inbox', 'trash'] # sort ascending when creating if treeWidget.topLevelItemCount() == 0: @@ -874,6 +875,7 @@ class MyForm(QtGui.QMainWindow): else: where = "toaddress || fromaddress || subject || message" + tableWidget.setColumnHidden(0, False) tableWidget.setColumnHidden(1, True) tableWidget.setSortingEnabled(False) @@ -2246,9 +2248,9 @@ more work your computer must do to send the message. A Time-To-Live of four or f newItem.setToolTip(unicode(acct.toLabel, 'utf-8')) newItem.setFont(font) newItem.setData(Qt.UserRole, str(toAddress)) - if shared.safeConfigGetBoolean(str(toAddress), 'mailinglist'): + if acct.type == 'mailinglist': newItem.setTextColor(QtGui.QColor(137, 04, 177)) # magenta - if shared.safeConfigGetBoolean(str(toAddress), 'chan'): + if acct.type == 'chan': newItem.setTextColor(QtGui.QColor(216, 119, 0)) # orange self.ui.tableWidgetInbox.insertRow(0) newItem.setIcon(avatarize(toAddress)) @@ -3259,8 +3261,8 @@ more work your computer must do to send the message. A Time-To-Live of four or f # Group of functions for the Your Identities dialog box def getCurrentAccount(self): - #treeWidget = self.getCurrentTreeWidget() - treeWidget = self.ui.treeWidgetYourIdentities + treeWidget = self.getCurrentTreeWidget() + #treeWidget = self.ui.treeWidgetYourIdentities if treeWidget: currentItem = treeWidget.currentItem() if currentItem: @@ -3472,8 +3474,8 @@ more work your computer must do to send the message. A Time-To-Live of four or f # only for manual edits. automatic edits (setText) are ignored if column != 0: return - # only account names - if not isinstance(item, Ui_AddressWidget): + # only account names of normal addresses (no chans/mailinglists) + if (not isinstance(item, Ui_AddressWidget)) or item.type != 'normal': return # only currently selected item if item.address != self.getCurrentTreeWidget().currentItem().address: diff --git a/src/bitmessageqt/foldertree.py b/src/bitmessageqt/foldertree.py index 2a907711..cb06763b 100644 --- a/src/bitmessageqt/foldertree.py +++ b/src/bitmessageqt/foldertree.py @@ -63,18 +63,26 @@ class Ui_FolderWidget(QtGui.QTreeWidgetItem): class Ui_AddressWidget(QtGui.QTreeWidgetItem): def __init__(self, parent, pos = 0, address = "", unreadCount = 0): super(QtGui.QTreeWidgetItem, self).__init__() - self.address = address self.unreadCount = unreadCount parent.insertTopLevelItem(pos, self) # only set default when creating #super(QtGui.QTreeWidgetItem, self).setExpanded(shared.config.getboolean(self.address, 'enabled')) - self.setExpanded(shared.safeConfigGetBoolean(self.address, 'enabled')) - self.updateText() + self.setAddress(address) def setAddress(self, address): self.address = str(address) + self.setType() + self.setExpanded(shared.safeConfigGetBoolean(self.address, 'enabled')) self.updateText() + def setType(self): + if shared.safeConfigGetBoolean(self.address, 'chan'): + self.type = "chan" + elif shared.safeConfigGetBoolean(self.address, 'mailinglist'): + self.type = "mailinglist" + else: + self.type = "normal" + def setUnreadCount(self, cnt): self.unreadCount = int(cnt) self.updateText() From 0bbd853341cbf71dc92e60fa0f4e5135ac20c746 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Mon, 5 Oct 2015 10:36:04 +0200 Subject: [PATCH 038/210] Gateway register/unregister changes - do not register/unregister channels or mailing lists - send registration/unregistration request irrespective of whether the client thinks it's registered or not. We don't know for sure it knows it correctly so don't assume that, it may confuse users --- src/bitmessageqt/__init__.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 50fccf59..9fe593ad 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2663,12 +2663,15 @@ more work your computer must do to send the message. A Time-To-Live of four or f if self.dialog.exec_(): addressAtCurrentRow = self.getCurrentAccount() acct = accountClass(addressAtCurrentRow) - if isinstance(acct, GatewayAccount) and self.dialog.ui.radioButtonUnregister.isChecked(): + # no chans / mailinglists + if acct.type != 'normal': + continue + if self.dialog.ui.radioButtonUnregister.isChecked(): print "unregister" acct.unregister() shared.config.remove_option(addressAtCurrentRow, 'gateway') shared.writeKeysFile() - elif (not isinstance(acct, GatewayAccount)) and self.dialog.ui.radioButtonRegister.isChecked(): + elif self.dialog.ui.radioButtonRegister.isChecked(): print "register" email = str(self.dialog.ui.lineEditEmail.text().toUtf8()) acct = MailchuckAccount(addressAtCurrentRow) @@ -2678,7 +2681,8 @@ more work your computer must do to send the message. A Time-To-Live of four or f self.getCurrentTreeWidget().currentItem().updateText() shared.writeKeysFile() else: - print "well nothing" + pass + #print "well nothing" # shared.writeKeysFile() # self.rerenderInboxToLabels() From e3ffc560cb5c72da87aa9f0135ebddeed6c7c455 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Mon, 5 Oct 2015 17:07:23 +0200 Subject: [PATCH 039/210] Chan UI fix Minor chan UI fix. Still not completely smooth but mostly working. --- src/bitmessageqt/__init__.py | 7 +++++-- src/bitmessageqt/account.py | 6 ++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 9fe593ad..600cb78b 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2665,7 +2665,7 @@ more work your computer must do to send the message. A Time-To-Live of four or f acct = accountClass(addressAtCurrentRow) # no chans / mailinglists if acct.type != 'normal': - continue + return if self.dialog.ui.radioButtonUnregister.isChecked(): print "unregister" acct.unregister() @@ -3479,7 +3479,10 @@ more work your computer must do to send the message. A Time-To-Live of four or f if column != 0: return # only account names of normal addresses (no chans/mailinglists) - if (not isinstance(item, Ui_AddressWidget)) or item.type != 'normal': + if (not isinstance(item, Ui_AddressWidget)) or item.type != 'normal' or self.getCurrentTreeWidget().currentItem() is None: + return + # not visible + if (not self.getCurrentAccount()) or (not isinstance (self.getCurrentAccount(), Ui_AddressWidget)): return # only currently selected item if item.address != self.getCurrentTreeWidget().currentItem().address: diff --git a/src/bitmessageqt/account.py b/src/bitmessageqt/account.py index 05613e47..f140235c 100644 --- a/src/bitmessageqt/account.py +++ b/src/bitmessageqt/account.py @@ -28,6 +28,12 @@ def accountClass(address): class BMAccount(object): def __init__(self, address = None): self.address = address + self.type = 'normal' + if shared.config.has_section(address): + if shared.safeConfigGetBoolean(self.address, 'chan'): + self.type = "chan" + elif shared.safeConfigGetBoolean(self.address, 'mailinglist'): + self.type = "mailinglist" def getLabel(self, address = None): if address is None: From 14d733066a3aee0cc492de313cad4e799377892c Mon Sep 17 00:00:00 2001 From: mailchuck Date: Tue, 6 Oct 2015 18:58:23 +0200 Subject: [PATCH 040/210] Resizable fields Inbox folder/message now resizable --- src/bitmessageqt/bitmessageui.py | 33 +++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/bitmessageqt/bitmessageui.py b/src/bitmessageqt/bitmessageui.py index 36ccbbb0..7012d0d7 100644 --- a/src/bitmessageqt/bitmessageui.py +++ b/src/bitmessageqt/bitmessageui.py @@ -69,14 +69,15 @@ class Ui_MainWindow(object): self.pushButtonNewAddress.setObjectName(_fromUtf8("pushButtonNewAddress")) self.verticalLayout_12.addWidget(self.pushButtonNewAddress) self.horizontalLayout_3.addLayout(self.verticalLayout_12) - self.verticalLayout_7 = QtGui.QVBoxLayout() - self.verticalLayout_7.setObjectName(_fromUtf8("verticalLayout_7")) - self.horizontalLayoutSearch = QtGui.QHBoxLayout() - self.horizontalLayoutSearch.setContentsMargins(-1, 0, -1, -1) - self.horizontalLayoutSearch.setObjectName(_fromUtf8("horizontalLayoutSearch")) + self.verticalSplitter_7 = QtGui.QSplitter() + self.verticalSplitter_7.setObjectName(_fromUtf8("verticalSplitter_7")) + self.verticalSplitter_7.setOrientation(QtCore.Qt.Vertical) + self.horizontalSplitterSearch = QtGui.QSplitter() + self.horizontalSplitterSearch.setContentsMargins(0, 0, 0, 0) + self.horizontalSplitterSearch.setObjectName(_fromUtf8("horizontalSplitterSearch")) self.inboxSearchLineEdit = QtGui.QLineEdit(self.inbox) self.inboxSearchLineEdit.setObjectName(_fromUtf8("inboxSearchLineEdit")) - self.horizontalLayoutSearch.addWidget(self.inboxSearchLineEdit) + self.horizontalSplitterSearch.addWidget(self.inboxSearchLineEdit) self.inboxSearchOption = QtGui.QComboBox(self.inbox) self.inboxSearchOption.setObjectName(_fromUtf8("inboxSearchOption")) self.inboxSearchOption.addItem(_fromUtf8("")) @@ -84,8 +85,11 @@ class Ui_MainWindow(object): self.inboxSearchOption.addItem(_fromUtf8("")) self.inboxSearchOption.addItem(_fromUtf8("")) self.inboxSearchOption.addItem(_fromUtf8("")) - self.horizontalLayoutSearch.addWidget(self.inboxSearchOption) - self.verticalLayout_7.addLayout(self.horizontalLayoutSearch) + self.horizontalSplitterSearch.addWidget(self.inboxSearchOption) + self.horizontalSplitterSearch.handle(1).setEnabled(False) + self.horizontalSplitterSearch.setStretchFactor(0, 1) + self.horizontalSplitterSearch.setStretchFactor(1, 0) + self.verticalSplitter_7.addWidget(self.horizontalSplitterSearch) self.tableWidgetInbox = QtGui.QTableWidget(self.inbox) self.tableWidgetInbox.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers) self.tableWidgetInbox.setAlternatingRowColors(True) @@ -111,13 +115,20 @@ class Ui_MainWindow(object): self.tableWidgetInbox.horizontalHeader().setStretchLastSection(True) self.tableWidgetInbox.verticalHeader().setVisible(False) self.tableWidgetInbox.verticalHeader().setDefaultSectionSize(26) - self.verticalLayout_7.addWidget(self.tableWidgetInbox) + self.verticalSplitter_7.addWidget(self.tableWidgetInbox) self.textEditInboxMessage = QtGui.QTextEdit(self.inbox) self.textEditInboxMessage.setBaseSize(QtCore.QSize(0, 500)) self.textEditInboxMessage.setReadOnly(True) self.textEditInboxMessage.setObjectName(_fromUtf8("textEditInboxMessage")) - self.verticalLayout_7.addWidget(self.textEditInboxMessage) - self.horizontalLayout_3.addLayout(self.verticalLayout_7) + self.verticalSplitter_7.addWidget(self.textEditInboxMessage) + self.verticalSplitter_7.setStretchFactor(0, 0) + self.verticalSplitter_7.setStretchFactor(1, 1) + self.verticalSplitter_7.setStretchFactor(2, 2) + self.verticalSplitter_7.setCollapsible(0, False) + self.verticalSplitter_7.setCollapsible(1, False) + self.verticalSplitter_7.setCollapsible(2, False) + self.verticalSplitter_7.handle(1).setEnabled(False) + self.horizontalLayout_3.addWidget(self.verticalSplitter_7) self.gridLayout.addLayout(self.horizontalLayout_3, 0, 0, 1, 1) icon2 = QtGui.QIcon() icon2.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/inbox.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) From 229c3c99657631e5898246cce1a19b0f330817f0 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Tue, 6 Oct 2015 18:58:44 +0200 Subject: [PATCH 041/210] Fix Typo --- src/bitmessageqt/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 600cb78b..f1b08e01 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2278,7 +2278,7 @@ more work your computer must do to send the message. A Time-To-Live of four or f newItem.setFont(font) self.ui.tableWidgetInbox.setItem(0, 3, newItem) self.ui.tableWidgetInbox.setSortingEnabled(True) - self.ubuntuMessagingMenuUpdate(True, newItem, self.toLabel) + self.ubuntuMessagingMenuUpdate(True, newItem, acct.toLabel) def click_pushButtonAddAddressBook(self): self.AddAddressDialogInstance = AddAddressDialog(self) From 17913cc586907db1842db3aad5bdb98d0b2f58fc Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sat, 10 Oct 2015 19:58:01 +0200 Subject: [PATCH 042/210] Subscriptions Initial subscription fix. Still does not always display the list from the correct folder, and it currently does not update unread count on subscriptions. --- src/bitmessageqt/__init__.py | 70 +++++++++++++-------------- src/bitmessageqt/account.py | 18 +++++-- src/bitmessageqt/foldertree.py | 88 ++++++++++++++++++++++++++++++++++ 3 files changed, 138 insertions(+), 38 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index f1b08e01..bf5dabc5 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -419,26 +419,16 @@ class MyForm(QtGui.QMainWindow): treeWidget = self.ui.treeWidgetSubscriptions folders = ['inbox', 'trash'] treeWidget.clear() + treeWidget.setSortingEnabled(False) + treeWidget.header().setSortIndicator(0, Qt.AscendingOrder) queryreturn = sqlQuery('SELECT label, address, enabled FROM subscriptions') for row in queryreturn: label, address, enabled = row - newItem = QtGui.QTreeWidgetItem(treeWidget) - newItem.setExpanded(True) - newItem.setIcon(0, avatarize(address)) - newItem.setText(0, label + ' (' + address + ')') - newItem.setData(0, Qt.UserRole, [str(address), "inbox"]) - #set text color - if enabled: - brush = QtGui.QBrush(QApplication.palette().text().color()) - else: - brush = QtGui.QBrush(QtGui.QColor(128, 128, 128)) - brush.setStyle(QtCore.Qt.NoBrush) - newItem.setForeground(0, brush) + newItem = Ui_SubscriptionWidget(treeWidget, 0, address, 0, label, enabled) for folder in folders: - newSubItem = QtGui.QTreeWidgetItem(newItem) - newSubItem.setText(0, _translate("MainWindow", folder)) - newSubItem.setData(0, Qt.UserRole, [str(address), folder]) + newSubItem = Ui_FolderWidget(newItem, 0, address, folder, 0) + treeWidget.setSortingEnabled(True) def rerenderTabTreeMessages(self): self.rerenderTabTree('messages') @@ -457,7 +447,6 @@ class MyForm(QtGui.QMainWindow): # sort ascending when creating if treeWidget.topLevelItemCount() == 0: treeWidget.header().setSortIndicator(0, Qt.AscendingOrder) - # init dictionary db = {} enabled = {} @@ -989,18 +978,22 @@ class MyForm(QtGui.QMainWindow): where = "message" else: where = "toaddress || fromaddress || subject || message" - + + if tableWidget == self.ui.tableWidgetInboxSubscriptions: + xAddress = "fromaddress" + else: + xAddress = "toaddress" if folder != False: sqlStatement = ''' SELECT folder, msgid, toaddress, fromaddress, subject, received, read - FROM inbox WHERE toaddress=? AND folder=? AND %s LIKE ? + FROM inbox WHERE ''' + xAddress + '''=? AND folder=? AND %s LIKE ? ORDER BY received ''' % (where) queryreturn = sqlQuery(sqlStatement, account, folder, what) else: sqlStatement = ''' SELECT folder, msgid, toaddress, fromaddress, subject, received, read - FROM inbox WHERE toaddress=? AND folder != "trash" AND %s LIKE ? + FROM inbox WHERE ''' + xAddress + '''=? AND folder != "trash" AND %s LIKE ? ORDER BY received ''' % (where) queryreturn = sqlQuery(sqlStatement, account, what) @@ -1018,7 +1011,10 @@ class MyForm(QtGui.QMainWindow): for row in queryreturn: msgfolder, msgid, toAddress, fromAddress, subject, received, read = row if acct is None: - acct = accountClass(toAddress) + if tableWidget == self.ui.tableWidgetInboxSubscriptions: + acct = accountClass(fromAddress) + else: + acct = accountClass(toAddress) subject = shared.fixPotentiallyInvalidUTF8Data(subject) acct.parseMessage(toAddress, fromAddress, subject, "") @@ -1792,11 +1788,12 @@ class MyForm(QtGui.QMainWindow): self.ui.tableWidgetInbox.item(i, 3).setText(textToDisplay) def removeInboxRowByMsgid(self, msgid): # msgid and inventoryHash are the same thing - for i in range(self.ui.tableWidgetInbox.rowCount()): - if msgid == str(self.ui.tableWidgetInbox.item(i, 3).data(Qt.UserRole).toPyObject()): + inbox = self.getCurrentMessagelist + for i in range(inbox.rowCount()): + if msgid == str(inbox.item(i, 3).data(Qt.UserRole).toPyObject()): self.statusBar().showMessage(_translate( "MainWindow", "Message trashed")) - self.ui.tableWidgetInbox.removeRow(i) + inbox.removeRow(i) break self.changedInboxUnread() @@ -2082,7 +2079,7 @@ more work your computer must do to send the message. A Time-To-Live of four or f self.ui.lineEditTo.setText('') self.ui.lineEditSubject.setText('') self.ui.textEditMessage.setText('') - self.ui.tabWidget.setCurrentIndex(2) + self.ui.tabWidget.setCurrentIndex(0) self.ui.tableWidgetInbox.setCurrentCell(0, 0) else: self.statusBar().showMessage(_translate( @@ -2129,7 +2126,7 @@ more work your computer must do to send the message. A Time-To-Live of four or f self.ui.lineEditSubjectBroadcast.setText('') self.ui.textEditMessageBroadcast.setText('') self.ui.tabWidget.setCurrentIndex(1) - self.ui.tableWidgetInbox.setCurrentCell(0, 0) + self.ui.tableWidgetInboxSubscriptions.setCurrentCell(0, 0) def click_pushButtonLoadFromAddressBook(self): self.ui.tabWidget.setCurrentIndex(5) @@ -2240,10 +2237,12 @@ more work your computer must do to send the message. A Time-To-Live of four or f subject = shared.fixPotentiallyInvalidUTF8Data(subject) acct = accountClass(toAddress) acct.parseMessage(toAddress, fromAddress, subject, message) + + inbox = self.getCurrentMessagelist() font = QFont() font.setBold(True) - self.ui.tableWidgetInbox.setSortingEnabled(False) + inbox.setSortingEnabled(False) newItem = QtGui.QTableWidgetItem(unicode(acct.toLabel, 'utf-8')) newItem.setToolTip(unicode(acct.toLabel, 'utf-8')) newItem.setFont(font) @@ -2252,9 +2251,9 @@ more work your computer must do to send the message. A Time-To-Live of four or f newItem.setTextColor(QtGui.QColor(137, 04, 177)) # magenta if acct.type == 'chan': newItem.setTextColor(QtGui.QColor(216, 119, 0)) # orange - self.ui.tableWidgetInbox.insertRow(0) + inbox.insertRow(0) newItem.setIcon(avatarize(toAddress)) - self.ui.tableWidgetInbox.setItem(0, 0, newItem) + inbox.setItem(0, 0, newItem) newItem = QtGui.QTableWidgetItem(unicode(acct.fromLabel, 'utf-8')) newItem.setToolTip(unicode(acct.fromLabel, 'utf-8')) @@ -2263,21 +2262,21 @@ more work your computer must do to send the message. A Time-To-Live of four or f newItem.setData(Qt.UserRole, str(fromAddress)) newItem.setFont(font) newItem.setIcon(avatarize(fromAddress)) - self.ui.tableWidgetInbox.setItem(0, 1, newItem) + inbox.setItem(0, 1, newItem) newItem = QtGui.QTableWidgetItem(unicode(acct.subject, 'utf-8)')) newItem.setToolTip(unicode(acct.subject, 'utf-8)')) newItem.setData(Qt.UserRole, str(subject)) #newItem.setData(Qt.UserRole, unicode(message, 'utf-8)')) # No longer hold the message in the table; we'll use a SQL query to display it as needed. newItem.setFont(font) - self.ui.tableWidgetInbox.setItem(0, 2, newItem) + inbox.setItem(0, 2, newItem) newItem = myTableWidgetItem(l10n.formatTimestamp()) newItem.setToolTip(l10n.formatTimestamp()) newItem.setData(Qt.UserRole, QByteArray(inventoryHash)) newItem.setData(33, int(time.time())) newItem.setFont(font) - self.ui.tableWidgetInbox.setItem(0, 3, newItem) - self.ui.tableWidgetInbox.setSortingEnabled(True) + inbox.setItem(0, 3, newItem) + inbox.setSortingEnabled(True) self.ubuntuMessagingMenuUpdate(True, newItem, acct.toLabel) def click_pushButtonAddAddressBook(self): @@ -2929,7 +2928,8 @@ more work your computer must do to send the message. A Time-To-Live of four or f if not tableWidget: return unread = False - while tableWidget.selectedIndexes() != []: + currentRow = 0 + while tableWidget.selectedIndexes(): currentRow = tableWidget.selectedIndexes()[0].row() inventoryHashToTrash = str(tableWidget.item( currentRow, 3).data(Qt.UserRole).toPyObject()) @@ -2945,7 +2945,7 @@ more work your computer must do to send the message. A Time-To-Live of four or f else: tableWidget.selectRow(currentRow - 1) if unread: - changedInboxUnread() + self.changedInboxUnread() def on_action_InboxSaveMessageAs(self): tableWidget = self.getCurrentMessagelist() @@ -3479,7 +3479,7 @@ more work your computer must do to send the message. A Time-To-Live of four or f if column != 0: return # only account names of normal addresses (no chans/mailinglists) - if (not isinstance(item, Ui_AddressWidget)) or item.type != 'normal' or self.getCurrentTreeWidget().currentItem() is None: + if (not isinstance(item, Ui_AddressWidget)) or item.type != 'normal' or not self.getCurrentTreeWidget() or self.getCurrentTreeWidget().currentItem() is None: return # not visible if (not self.getCurrentAccount()) or (not isinstance (self.getCurrentAccount(), Ui_AddressWidget)): diff --git a/src/bitmessageqt/account.py b/src/bitmessageqt/account.py index f140235c..3b8badaa 100644 --- a/src/bitmessageqt/account.py +++ b/src/bitmessageqt/account.py @@ -11,7 +11,9 @@ import time def accountClass(address): if not shared.config.has_section(address): - return None + subscription = SubscriptionAccount(address) + if subscription.type != 'subscription': + return None try: gateway = shared.config.get(address, "gateway") for name, cls in inspect.getmembers(sys.modules[__name__], inspect.isclass): @@ -24,7 +26,7 @@ def accountClass(address): pass # no gateway return BMAccount(address) - + class BMAccount(object): def __init__(self, address = None): self.address = address @@ -34,7 +36,12 @@ class BMAccount(object): self.type = "chan" elif shared.safeConfigGetBoolean(self.address, 'mailinglist'): self.type = "mailinglist" - + else: + queryreturn = sqlQuery( + '''select label from subscriptions where address=?''', address) + if queryreturn: + self.type = 'subscription' + def getLabel(self, address = None): if address is None: address = self.address @@ -62,6 +69,11 @@ class BMAccount(object): self.fromLabel = self.getLabel(fromAddress) self.toLabel = self.getLabel(toAddress) + +class SubscriptionAccount(BMAccount): + pass + + class GatewayAccount(BMAccount): gatewayName = None def __init__(self, address): diff --git a/src/bitmessageqt/foldertree.py b/src/bitmessageqt/foldertree.py index cb06763b..59e2b963 100644 --- a/src/bitmessageqt/foldertree.py +++ b/src/bitmessageqt/foldertree.py @@ -146,3 +146,91 @@ class Ui_AddressWidget(QtGui.QTreeWidgetItem): return (not reverse if shared.config.getboolean(self.address, 'enabled') else reverse) return super(QtGui.QTreeWidgetItem, self).__lt__(other) + + +class Ui_SubscriptionWidget(Ui_AddressWidget): + def __init__(self, parent, pos = 0, address = "", unreadCount = 0, label = "", enabled = ""): + super(QtGui.QTreeWidgetItem, self).__init__() + self.unreadCount = unreadCount + parent.insertTopLevelItem(pos, self) + # only set default when creating + #super(QtGui.QTreeWidgetItem, self).setExpanded(shared.config.getboolean(self.address, 'enabled')) + self.setEnabled(enabled) + self.setLabel(label) + self.setAddress(address) + + def setLabel(self, label): + self.label = label + + def setAddress(self, address): + self.address = str(address) + self.setType() + self.setExpanded(self.isEnabled) + self.updateText() + + def setEnabled(self, enabled): + self.isEnabled = enabled + + def setType(self): + self.type = "subscription" + + def setUnreadCount(self, cnt): + self.unreadCount = int(cnt) + self.updateText() + + def updateText(self): + text = unicode(self.label, 'utf-8)') + ' (' + self.address + ')' + + font = QtGui.QFont() + if self.unreadCount > 0: + # only show message count if the child doesn't show + if not self.isExpanded(): + text += " (" + str(self.unreadCount) + ")" + font.setBold(True) + else: + font.setBold(False) + self.setFont(0, font) + + #set text color + if self.isEnabled: + brush = QtGui.QBrush(QtGui.QColor(137, 04, 177)) + #self.setExpanded(True) + else: + brush = QtGui.QBrush(QtGui.QColor(128, 128, 128)) + #self.setExpanded(False) + brush.setStyle(QtCore.Qt.NoBrush) + self.setForeground(0, brush) + + self.setIcon(0, avatarize(self.address)) + self.setText(0, text) + self.setToolTip(0, text) +# self.setData(0, QtCore.Qt.UserRole, [self.address, "inbox"]) + + def setExpanded(self, expand): + super(Ui_SubscriptionWidget, self).setExpanded(expand) + self.updateText() + + def edit(self): + self.setText(0, self.label) + super(QtGui.QAbstractItemView, self).edit() + + # label (or address) alphabetically, disabled at the end + def __lt__(self, other): + if (isinstance(other, Ui_SubscriptionWidget)): + reverse = False + if self.treeWidget().header().sortIndicatorOrder() == QtCore.Qt.DescendingOrder: + reverse = True + if self.isEnabled == other.isEnabled: + if self.label: + x = self.label.decode('utf-8').lower() + else: + x = self.address.decode('utf-8').lower() + if other.label: + y = other.label.decode('utf-8').lower() + else: + y = other.address.decode('utf-8').lower() + return x < y +# else: + return (not reverse if self.isEnabled else reverse) + + return super(QtGui.QTreeWidgetItem, self).__lt__(other) \ No newline at end of file From 32cda02d5cd830c12b1bf0edd31125d1a35e07c9 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sat, 10 Oct 2015 19:59:44 +0200 Subject: [PATCH 043/210] Messages tab resizable - the separator between folder tree and message list is now resizable - the separator between list of messages and message content is resizable - only for message tab, chans, subscriptions and others are still not resizable --- src/bitmessageqt/bitmessageui.py | 34 +++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/src/bitmessageqt/bitmessageui.py b/src/bitmessageqt/bitmessageui.py index 7012d0d7..26493241 100644 --- a/src/bitmessageqt/bitmessageui.py +++ b/src/bitmessageqt/bitmessageui.py @@ -53,27 +53,32 @@ class Ui_MainWindow(object): self.inbox.setObjectName(_fromUtf8("inbox")) self.gridLayout = QtGui.QGridLayout(self.inbox) self.gridLayout.setObjectName(_fromUtf8("gridLayout")) - self.horizontalLayout_3 = QtGui.QHBoxLayout() - self.horizontalLayout_3.setObjectName(_fromUtf8("horizontalLayout_3")) - self.verticalLayout_12 = QtGui.QVBoxLayout() - self.verticalLayout_12.setObjectName(_fromUtf8("verticalLayout_12")) + self.horizontalSplitter_3 = QtGui.QSplitter() + self.horizontalSplitter_3.setObjectName(_fromUtf8("horizontalSplitter_3")) + self.verticalSplitter_12 = QtGui.QSplitter() + self.verticalSplitter_12.setObjectName(_fromUtf8("verticalSplitter_12")) + self.verticalSplitter_12.setOrientation(QtCore.Qt.Vertical) self.treeWidgetYourIdentities = QtGui.QTreeWidget(self.inbox) - self.treeWidgetYourIdentities.setMaximumSize(QtCore.QSize(200, 16777215)) self.treeWidgetYourIdentities.setObjectName(_fromUtf8("treeWidgetYourIdentities")) + self.treeWidgetYourIdentities.resize(200, self.treeWidgetYourIdentities.height()) icon1 = QtGui.QIcon() icon1.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/identities.png")), QtGui.QIcon.Selected, QtGui.QIcon.Off) self.treeWidgetYourIdentities.headerItem().setIcon(0, icon1) - self.verticalLayout_12.addWidget(self.treeWidgetYourIdentities) + self.verticalSplitter_12.addWidget(self.treeWidgetYourIdentities) self.pushButtonNewAddress = QtGui.QPushButton(self.inbox) - self.pushButtonNewAddress.setMaximumSize(QtCore.QSize(200, 16777215)) self.pushButtonNewAddress.setObjectName(_fromUtf8("pushButtonNewAddress")) - self.verticalLayout_12.addWidget(self.pushButtonNewAddress) - self.horizontalLayout_3.addLayout(self.verticalLayout_12) + self.pushButtonNewAddress.resize(200, self.pushButtonNewAddress.height()) + self.verticalSplitter_12.addWidget(self.pushButtonNewAddress) + self.verticalSplitter_12.setStretchFactor(0, 1) + self.verticalSplitter_12.setStretchFactor(1, 0) + self.verticalSplitter_12.setCollapsible(0, False) + self.verticalSplitter_12.setCollapsible(1, False) + self.verticalSplitter_12.handle(1).setEnabled(False) + self.horizontalSplitter_3.addWidget(self.verticalSplitter_12) self.verticalSplitter_7 = QtGui.QSplitter() self.verticalSplitter_7.setObjectName(_fromUtf8("verticalSplitter_7")) self.verticalSplitter_7.setOrientation(QtCore.Qt.Vertical) self.horizontalSplitterSearch = QtGui.QSplitter() - self.horizontalSplitterSearch.setContentsMargins(0, 0, 0, 0) self.horizontalSplitterSearch.setObjectName(_fromUtf8("horizontalSplitterSearch")) self.inboxSearchLineEdit = QtGui.QLineEdit(self.inbox) self.inboxSearchLineEdit.setObjectName(_fromUtf8("inboxSearchLineEdit")) @@ -85,6 +90,7 @@ class Ui_MainWindow(object): self.inboxSearchOption.addItem(_fromUtf8("")) self.inboxSearchOption.addItem(_fromUtf8("")) self.inboxSearchOption.addItem(_fromUtf8("")) + self.inboxSearchOption.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToContents) self.horizontalSplitterSearch.addWidget(self.inboxSearchOption) self.horizontalSplitterSearch.handle(1).setEnabled(False) self.horizontalSplitterSearch.setStretchFactor(0, 1) @@ -128,8 +134,12 @@ class Ui_MainWindow(object): self.verticalSplitter_7.setCollapsible(1, False) self.verticalSplitter_7.setCollapsible(2, False) self.verticalSplitter_7.handle(1).setEnabled(False) - self.horizontalLayout_3.addWidget(self.verticalSplitter_7) - self.gridLayout.addLayout(self.horizontalLayout_3, 0, 0, 1, 1) + self.horizontalSplitter_3.addWidget(self.verticalSplitter_7) + self.horizontalSplitter_3.setStretchFactor(0, 0) + self.horizontalSplitter_3.setStretchFactor(1, 1) + self.horizontalSplitter_3.setCollapsible(0, False) + self.horizontalSplitter_3.setCollapsible(1, False) + self.gridLayout.addWidget(self.horizontalSplitter_3) icon2 = QtGui.QIcon() icon2.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/inbox.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.tabWidget.addTab(self.inbox, icon2, _fromUtf8("")) From 509992d77e49e61928e96d24ff3f98d94e9bdca4 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sun, 11 Oct 2015 11:18:25 +0200 Subject: [PATCH 044/210] Formatting change Was not folding properly in editor, made it more python-like --- src/bitmessageqt/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index bf5dabc5..805bf9d3 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -1941,10 +1941,10 @@ class MyForm(QtGui.QMainWindow): def click_pushButtonTTL(self): QtGui.QMessageBox.information(self, 'Time To Live', _translate( - "MainWindow", "The TTL, or Time-To-Live is the length of time that the network will hold the message. \ -The recipient must get it during this time. If your Bitmessage client does not hear an acknowledgement, it \ -will resend the message automatically. The longer the Time-To-Live, the \ -more work your computer must do to send the message. A Time-To-Live of four or five days is often appropriate."), QMessageBox.Ok) + "MainWindow", """The TTL, or Time-To-Live is the length of time that the network will hold the message. + The recipient must get it during this time. If your Bitmessage client does not hear an acknowledgement, it + will resend the message automatically. The longer the Time-To-Live, the + more work your computer must do to send the message. A Time-To-Live of four or five days is often appropriate."""), QMessageBox.Ok) def click_pushButtonSend(self): self.statusBar().showMessage('') From 7a0e6488222b4c479c2fa1aca829193007f305db Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sun, 11 Oct 2015 11:57:58 +0200 Subject: [PATCH 045/210] Remember main window geometry and state on exit Main window geometry and state is saved on exit and restored on load. Uses default QSettings in python. --- src/bitmessageqt/__init__.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 805bf9d3..bf1a52f0 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -724,6 +724,8 @@ class MyForm(QtGui.QMainWindow): QtCore.QObject.connect(self.ui.horizontalSliderTTL, QtCore.SIGNAL( "valueChanged(int)"), self.updateTTL) + self.readSettings() + # Check to see whether we can connect to namecoin. Hide the 'Fetch Namecoin ID' button if we can't. try: options = {} @@ -2739,6 +2741,11 @@ class MyForm(QtGui.QMainWindow): # unregister the messaging system if self.mmapp is not None: self.mmapp.unregister() + + settings = QSettings("Mailchuck Ltd.", "PyBitmessage") + settings.setValue("geometry", self.saveGeometry()) + settings.setValue("state", self.saveState()) + self.statusBar().showMessage(_translate( "MainWindow", "All done. Closing user interface...")) os._exit(0) @@ -3582,6 +3589,20 @@ class MyForm(QtGui.QMainWindow): self.statusBar().showMessage(data) + def readSettings(self): + settings = QSettings("Mailchuck Ltd.", "PyBitmessage") + try: + geom = settings.value("geometry") + self.restoreGeometry(geom.toByteArray() if hasattr(geom, 'toByteArray') else geom) + except Exception as e: + pass + + try: + state = settings.value("state") + self.restoreState(state.toByteArray() if hasattr(state, 'toByteArray') else state) + except Exception as e: + pass + class helpDialog(QtGui.QDialog): From 61482cc4188a567b6e0fdc644b70e35551ae480e Mon Sep 17 00:00:00 2001 From: mailchuck Date: Tue, 13 Oct 2015 23:32:36 +0200 Subject: [PATCH 046/210] Add BroadcastAccount class This will help to distinguish between general broadcasts and subscriptions. --- src/bitmessageqt/account.py | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/bitmessageqt/account.py b/src/bitmessageqt/account.py index 3b8badaa..19212302 100644 --- a/src/bitmessageqt/account.py +++ b/src/bitmessageqt/account.py @@ -7,13 +7,20 @@ import inspect from helper_sql import * from addresses import decodeAddress from pyelliptic.openssl import OpenSSL +from utils import str_broadcast_subscribers import time def accountClass(address): if not shared.config.has_section(address): - subscription = SubscriptionAccount(address) - if subscription.type != 'subscription': - return None + if address == str_broadcast_subscribers: + subscription = BroadcastAccount(address) + if subscription.type != 'broadcast': + return None + else: + subscription = SubscriptionAccount(address) + if subscription.type != 'subscription': + return None + return subscription try: gateway = shared.config.get(address, "gateway") for name, cls in inspect.getmembers(sys.modules[__name__], inspect.isclass): @@ -36,9 +43,11 @@ class BMAccount(object): self.type = "chan" elif shared.safeConfigGetBoolean(self.address, 'mailinglist'): self.type = "mailinglist" + elif self.address == str_broadcast_subscribers: + self.type = 'broadcast' else: queryreturn = sqlQuery( - '''select label from subscriptions where address=?''', address) + '''select label from subscriptions where address=?''', self.address) if queryreturn: self.type = 'subscription' @@ -72,6 +81,10 @@ class BMAccount(object): class SubscriptionAccount(BMAccount): pass + + +class BroadcastAccount(BMAccount): + pass class GatewayAccount(BMAccount): From 8688e72b555b8a16ecedc9820ce5ab9db4c98437 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Tue, 13 Oct 2015 23:33:36 +0200 Subject: [PATCH 047/210] Constant in a separate file str_broadcast_subscribers should not be specific to the main UI class --- src/bitmessageqt/utils.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/bitmessageqt/utils.py b/src/bitmessageqt/utils.py index af58fd37..79b0e2a9 100644 --- a/src/bitmessageqt/utils.py +++ b/src/bitmessageqt/utils.py @@ -4,6 +4,8 @@ import os import shared from addresses import addBMIfNotPresent +str_broadcast_subscribers = '[Broadcast subscribers]' + def identiconize(address): size = 48 From 2a561c0995e616f718536b78fdfe9fe75fc03b8e Mon Sep 17 00:00:00 2001 From: mailchuck Date: Tue, 13 Oct 2015 23:36:09 +0200 Subject: [PATCH 048/210] Fix reply-to subscriptions and labels Closes #1 Also attempts to solve #49 but needs testing. --- src/bitmessageqt/__init__.py | 65 ++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 29 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index bf1a52f0..cf25b430 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -89,7 +89,6 @@ class MyForm(QtGui.QMainWindow): # the maximum frequency of message sounds in seconds maxSoundFrequencySec = 60 - str_broadcast_subscribers = '[Broadcast subscribers]' str_chan = '[chan]' def init_file_menu(self): @@ -1161,7 +1160,7 @@ class MyForm(QtGui.QMainWindow): for row in queryreturn: toAddress, read = row if not read: - if toAddress == self.str_broadcast_subscribers: + if toAddress == str_broadcast_subscribers: if self.mmapp.has_source("Subscriptions"): self.mmapp.remove_source("Subscriptions") else: @@ -1179,8 +1178,8 @@ class MyForm(QtGui.QMainWindow): msgid, toAddress, read = row try: - if toAddress == self.str_broadcast_subscribers: - toLabel = self.str_broadcast_subscribers + if toAddress == str_broadcast_subscribers: + toLabel = str_broadcast_subscribers else: toLabel = shared.config.get(toAddress, 'label') except: @@ -1189,7 +1188,7 @@ class MyForm(QtGui.QMainWindow): toLabel = toAddress if not read: - if toLabel == self.str_broadcast_subscribers: + if toLabel == str_broadcast_subscribers: # increment the unread subscriptions unreadSubscriptions = unreadSubscriptions + 1 else: @@ -1254,7 +1253,7 @@ class MyForm(QtGui.QMainWindow): return # remember this item to that the messaging menu can find it - if toLabel == self.str_broadcast_subscribers: + if toLabel == str_broadcast_subscribers: self.newBroadcastItem = newItem else: self.newMessageItem = newItem @@ -2096,7 +2095,7 @@ class MyForm(QtGui.QMainWindow): # this is a broadcast message, but we can use it to update the # user interface when the POW is done generating. ackdata = OpenSSL.rand(32) - toAddress = self.str_broadcast_subscribers + toAddress = str_broadcast_subscribers ripe = '' t = ('', # msgid. We don't know what this will be until the POW is done. toAddress, @@ -2117,7 +2116,7 @@ class MyForm(QtGui.QMainWindow): sqlExecute( '''INSERT INTO sent VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)''', *t) - toLabel = self.str_broadcast_subscribers + toLabel = str_broadcast_subscribers self.displayNewSentMessage( toAddress, toLabel, fromAddress, subject, message, ackdata) @@ -2177,15 +2176,13 @@ class MyForm(QtGui.QMainWindow): def rerenderComboBoxSendFromBroadcast(self): self.ui.comboBoxSendFromBroadcast.clear() - configSections = shared.config.sections() - for addressInKeysFile in configSections: - if addressInKeysFile != 'bitmessagesettings': - isEnabled = shared.config.getboolean( - addressInKeysFile, 'enabled') # I realize that this is poor programming practice but I don't care. It's easier for others to read. - isMaillinglist = shared.safeConfigGetBoolean(addressInKeysFile, 'mailinglist') - if isEnabled and isMaillinglist: - self.ui.comboBoxSendFromBroadcast.insertItem(0, avatarize(addressInKeysFile), unicode(shared.config.get( - addressInKeysFile, 'label'), 'utf-8'), addressInKeysFile) + queryreturn = sqlQuery( + '''select label, address from subscriptions where enabled = 1''' + ) + + for row in queryreturn: + label, address = row + self.ui.comboBoxSendFromBroadcast.insertItem(0, avatarize(address), unicode(label, 'utf-8'), address) self.ui.comboBoxSendFromBroadcast.insertItem(0, '', '') if(self.ui.comboBoxSendFromBroadcast.count() == 2): self.ui.comboBoxSendFromBroadcast.setCurrentIndex(1) @@ -2861,9 +2858,19 @@ class MyForm(QtGui.QMainWindow): for row in queryreturn: messageAtCurrentInboxRow, = row acct.parseMessage(toAddressAtCurrentInboxRow, fromAddressAtCurrentInboxRow, str(tableWidget.item(currentInboxRow, 2).data(Qt.UserRole).toPyObject()), messageAtCurrentInboxRow) - if toAddressAtCurrentInboxRow == self.str_broadcast_subscribers: - #TODO what does this if?.. - a = a + widget = { + 'subject': self.ui.lineEditSubject, + 'from': self.ui.comboBoxSendFrom, + 'message': self.ui.textEditMessage + } + if toAddressAtCurrentInboxRow == str_broadcast_subscribers: + widget = { + 'subject': self.ui.lineEditSubjectBroadcast, + 'from': self.ui.comboBoxSendFromBroadcast, + 'message': self.ui.textEditMessageBroadcast + } + self.ui.tabWidgetSend.setCurrentIndex(1) + toAddressAtCurrentInboxRow = fromAddressAtCurrentInboxRow elif not shared.config.has_section(toAddressAtCurrentInboxRow): QtGui.QMessageBox.information(self, _translate("MainWindow", "Address is gone"), _translate( "MainWindow", "Bitmessage cannot find your address %1. Perhaps you removed it?").arg(toAddressAtCurrentInboxRow), QMessageBox.Ok) @@ -2871,9 +2878,10 @@ class MyForm(QtGui.QMainWindow): QtGui.QMessageBox.information(self, _translate("MainWindow", "Address disabled"), _translate( "MainWindow", "Error: The address from which you are trying to send is disabled. You\'ll have to enable it on the \'Your Identities\' tab before using it."), QMessageBox.Ok) else: - self.setBroadcastEnablementDependingOnWhetherThisIsAChanAddress(toAddressAtCurrentInboxRow) + #self.setBroadcastEnablementDependingOnWhetherThisIsAChanAddress(toAddressAtCurrentInboxRow) + self.ui.tabWidgetSend.setCurrentIndex(0) - self.ui.lineEditTo.setText(str(acct.fromLabel)) + self.ui.lineEditTo.setText(str(acct.fromAddress)) # If the previous message was to a chan then we should send our reply to the chan rather than to the particular person who sent the message. if shared.config.has_section(toAddressAtCurrentInboxRow): @@ -2881,20 +2889,19 @@ class MyForm(QtGui.QMainWindow): print 'original sent to a chan. Setting the to address in the reply to the chan address.' self.ui.lineEditTo.setText(str(toAddressAtCurrentInboxRow)) - listOfAddressesInComboBoxSendFrom = [str(self.ui.comboBoxSendFrom.itemData(i).toPyObject()) for i in range(self.ui.comboBoxSendFrom.count())] + listOfAddressesInComboBoxSendFrom = [str(widget['from'].itemData(i).toPyObject()) for i in range(widget['from'].count())] if toAddressAtCurrentInboxRow in listOfAddressesInComboBoxSendFrom: currentIndex = listOfAddressesInComboBoxSendFrom.index(toAddressAtCurrentInboxRow) - self.ui.comboBoxSendFrom.setCurrentIndex(currentIndex) + widget['from'].setCurrentIndex(currentIndex) else: - self.ui.comboBoxSendFrom.setCurrentIndex(0) + widget['from'].setCurrentIndex(0) quotedText = self.quoted_text(unicode(messageAtCurrentInboxRow, 'utf-8')) - self.ui.textEditMessage.setText(quotedText) + widget['message'].setText(quotedText) if acct.subject[0:3] in ['Re:', 'RE:']: - self.ui.lineEditSubject.setText(acct.subject) + widget['subject'].setText(acct.subject) else: - self.ui.lineEditSubject.setText('Re: ' + acct.subject) - self.ui.tabWidgetSend.setCurrentIndex(0) + widget['subject'].setText('Re: ' + acct.subject) self.ui.tabWidget.setCurrentIndex(1) def on_action_InboxAddSenderToAddressBook(self): From ceeea1da732eb02f15bdd4287a3cfc804227693f Mon Sep 17 00:00:00 2001 From: mailchuck Date: Wed, 14 Oct 2015 23:38:34 +0200 Subject: [PATCH 049/210] Replies to mailing lists Attempt to fix #49. Will close after positive feedback. --- src/bitmessageqt/__init__.py | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index cf25b430..7a9e0e30 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2149,13 +2149,13 @@ class MyForm(QtGui.QMainWindow): self.statusBar().showMessage(_translate( "MainWindow", "Fetched address from namecoin identity.")) - def setBroadcastEnablementDependingOnWhetherThisIsAChanAddress(self, address): + def setBroadcastEnablementDependingOnWhetherThisIsAMailingListAddress(self, address): # If this is a chan then don't let people broadcast because no one # should subscribe to chan addresses. - if shared.safeConfigGetBoolean(str(address), 'chan'): - self.ui.tabWidgetSend.setCurrentIndex(0) - else: + if shared.safeConfigGetBoolean(str(address), 'mailinglist'): self.ui.tabWidgetSend.setCurrentIndex(1) + else: + self.ui.tabWidgetSend.setCurrentIndex(0) def rerenderComboBoxSendFrom(self): self.ui.comboBoxSendFrom.clear() @@ -2864,13 +2864,8 @@ class MyForm(QtGui.QMainWindow): 'message': self.ui.textEditMessage } if toAddressAtCurrentInboxRow == str_broadcast_subscribers: - widget = { - 'subject': self.ui.lineEditSubjectBroadcast, - 'from': self.ui.comboBoxSendFromBroadcast, - 'message': self.ui.textEditMessageBroadcast - } - self.ui.tabWidgetSend.setCurrentIndex(1) - toAddressAtCurrentInboxRow = fromAddressAtCurrentInboxRow + self.ui.tabWidgetSend.setCurrentIndex(0) +# toAddressAtCurrentInboxRow = fromAddressAtCurrentInboxRow elif not shared.config.has_section(toAddressAtCurrentInboxRow): QtGui.QMessageBox.information(self, _translate("MainWindow", "Address is gone"), _translate( "MainWindow", "Bitmessage cannot find your address %1. Perhaps you removed it?").arg(toAddressAtCurrentInboxRow), QMessageBox.Ok) @@ -2878,8 +2873,15 @@ class MyForm(QtGui.QMainWindow): QtGui.QMessageBox.information(self, _translate("MainWindow", "Address disabled"), _translate( "MainWindow", "Error: The address from which you are trying to send is disabled. You\'ll have to enable it on the \'Your Identities\' tab before using it."), QMessageBox.Ok) else: - #self.setBroadcastEnablementDependingOnWhetherThisIsAChanAddress(toAddressAtCurrentInboxRow) - self.ui.tabWidgetSend.setCurrentIndex(0) + self.setBroadcastEnablementDependingOnWhetherThisIsAMailingListAddress(toAddressAtCurrentInboxRow) + if self.ui.tabWidgetSend.currentIndex() == 1: + widget = { + 'subject': self.ui.lineEditSubjectBroadcast, + 'from': self.ui.comboBoxSendFromBroadcast, + 'message': self.ui.textEditMessageBroadcast + } + self.ui.tabWidgetSend.setCurrentIndex(1) + toAddressAtCurrentInboxRow = fromAddressAtCurrentInboxRow self.ui.lineEditTo.setText(str(acct.fromAddress)) From 872243c8200fa17651f5b8e3b058b0c4b05c445d Mon Sep 17 00:00:00 2001 From: mailchuck Date: Wed, 14 Oct 2015 23:43:50 +0200 Subject: [PATCH 050/210] Chan and subscription trash Fixes #6 --- src/bitmessageqt/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 7a9e0e30..a4e654bc 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -3293,8 +3293,8 @@ class MyForm(QtGui.QMainWindow): return False def getCurrentFolder(self): - #treeWidget = self.getCurrentTreeWidget() - treeWidget = self.ui.treeWidgetYourIdentities + treeWidget = self.getCurrentTreeWidget() + #treeWidget = self.ui.treeWidgetYourIdentities if treeWidget: currentItem = treeWidget.currentItem() if currentItem and hasattr(currentItem, 'folderName'): From b448b020724b815e6e357de96a3786474a675790 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Fri, 16 Oct 2015 20:34:15 +0200 Subject: [PATCH 051/210] Broadcast sending from was wrong Fixes #51 --- src/bitmessageqt/__init__.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index a4e654bc..904225a7 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2176,13 +2176,15 @@ class MyForm(QtGui.QMainWindow): def rerenderComboBoxSendFromBroadcast(self): self.ui.comboBoxSendFromBroadcast.clear() - queryreturn = sqlQuery( - '''select label, address from subscriptions where enabled = 1''' - ) - - for row in queryreturn: - label, address = row - self.ui.comboBoxSendFromBroadcast.insertItem(0, avatarize(address), unicode(label, 'utf-8'), address) + configSections = shared.config.sections() + for addressInKeysFile in configSections: + if addressInKeysFile != 'bitmessagesettings': + isEnabled = shared.config.getboolean( + addressInKeysFile, 'enabled') # I realize that this is poor programming practice but I don't care. It's easier for others to read. + isMaillinglist = shared.safeConfigGetBoolean(addressInKeysFile, 'mailinglist') + if isEnabled and isMaillinglist: + self.ui.comboBoxSendFromBroadcast.insertItem(0, avatarize(addressInKeysFile), unicode(shared.config.get( + addressInKeysFile, 'label'), 'utf-8'), addressInKeysFile) self.ui.comboBoxSendFromBroadcast.insertItem(0, '', '') if(self.ui.comboBoxSendFromBroadcast.count() == 2): self.ui.comboBoxSendFromBroadcast.setCurrentIndex(1) From dad14b0cb8dc61537db69de17257775d7830c70e Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sat, 17 Oct 2015 17:11:40 +0200 Subject: [PATCH 052/210] Bump up version --- src/shared.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared.py b/src/shared.py index d82f00a5..0ae2d40d 100644 --- a/src/shared.py +++ b/src/shared.py @@ -1,6 +1,6 @@ from __future__ import division -softwareVersion = '0.4.4' +softwareVersion = '0.4.5.3' verbose = 1 maximumAgeOfAnObjectThatIAmWillingToAccept = 216000 # This is obsolete with the change to protocol v3 but the singleCleaner thread still hasn't been updated so we need this a little longer. lengthOfTimeToHoldOnToAllPubkeys = 2419200 # Equals 4 weeks. You could make this longer if you want but making it shorter would not be advisable because there is a very small possibility that it could keep you from obtaining a needed pubkey for a period of time. From 8b527f81eecb5c24bfa762a27b6e2eac3bf2d634 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sun, 18 Oct 2015 21:11:10 +0200 Subject: [PATCH 053/210] Show subscription unread count Subscriptions were not showing unread count. This commit implements it. --- src/bitmessageqt/__init__.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 904225a7..07ed038a 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -420,13 +420,30 @@ class MyForm(QtGui.QMainWindow): treeWidget.clear() treeWidget.setSortingEnabled(False) treeWidget.header().setSortIndicator(0, Qt.AscendingOrder) + db = {} + queryreturn = sqlQuery('''SELECT fromaddress, folder, count(msgid) as cnt + FROM inbox, subscriptions + WHERE read = 0 AND subscriptions.address = inbox.fromaddress + GROUP BY inbox.fromaddress, folder''') + for row in queryreturn: + fromaddress, folder, cnt = row + if fromaddress not in db: + db[fromaddress] = {} + db[fromaddress][folder] = cnt queryreturn = sqlQuery('SELECT label, address, enabled FROM subscriptions') for row in queryreturn: label, address, enabled = row newItem = Ui_SubscriptionWidget(treeWidget, 0, address, 0, label, enabled) + unread = 0 for folder in folders: - newSubItem = Ui_FolderWidget(newItem, 0, address, folder, 0) + try: + newSubItem = Ui_FolderWidget(newItem, 0, address, folder, db[address][folder]) + unread += db[address][folder] + except KeyError: + newSubItem = Ui_FolderWidget(newItem, 0, address, folder, 0) + + newItem.setUnreadCount(unread) treeWidget.setSortingEnabled(True) def rerenderTabTreeMessages(self): From 6639a1912626653f93b2bb3f99db7b9a1f1c9e32 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sun, 18 Oct 2015 22:54:52 +0200 Subject: [PATCH 054/210] Fix reply unicode subject Fixes #62 --- src/bitmessageqt/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 07ed038a..e4137e40 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2876,7 +2876,7 @@ class MyForm(QtGui.QMainWindow): if queryreturn != []: for row in queryreturn: messageAtCurrentInboxRow, = row - acct.parseMessage(toAddressAtCurrentInboxRow, fromAddressAtCurrentInboxRow, str(tableWidget.item(currentInboxRow, 2).data(Qt.UserRole).toPyObject()), messageAtCurrentInboxRow) + acct.parseMessage(toAddressAtCurrentInboxRow, fromAddressAtCurrentInboxRow, unicode(tableWidget.item(currentInboxRow, 2).data(Qt.UserRole).toPyObject(), 'utf-8'), messageAtCurrentInboxRow) widget = { 'subject': self.ui.lineEditSubject, 'from': self.ui.comboBoxSendFrom, From 08dff8e1189e1c990fca8d7d6721511a9da1d34a Mon Sep 17 00:00:00 2001 From: mailchuck Date: Mon, 19 Oct 2015 17:22:37 +0200 Subject: [PATCH 055/210] Account labels become editable again Fixes #60 --- src/bitmessageqt/__init__.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index e4137e40..90aaa36a 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -3299,17 +3299,22 @@ class MyForm(QtGui.QMainWindow): return False # Group of functions for the Your Identities dialog box - def getCurrentAccount(self): + def getCurrentItem(self): treeWidget = self.getCurrentTreeWidget() - #treeWidget = self.ui.treeWidgetYourIdentities if treeWidget: currentItem = treeWidget.currentItem() if currentItem: - account = currentItem.address - return account - else: - # TODO need debug msg? - return False + return currentItem + return False + + def getCurrentAccount(self): + currentItem = self.getCurrentItem() + if currentItem: + account = currentItem.address + return account + else: + # TODO need debug msg? + return False def getCurrentFolder(self): treeWidget = self.getCurrentTreeWidget() @@ -3517,10 +3522,10 @@ class MyForm(QtGui.QMainWindow): if (not isinstance(item, Ui_AddressWidget)) or item.type != 'normal' or not self.getCurrentTreeWidget() or self.getCurrentTreeWidget().currentItem() is None: return # not visible - if (not self.getCurrentAccount()) or (not isinstance (self.getCurrentAccount(), Ui_AddressWidget)): + if (not self.getCurrentItem()) or (not isinstance (self.getCurrentItem(), Ui_AddressWidget)): return # only currently selected item - if item.address != self.getCurrentTreeWidget().currentItem().address: + if item.address != self.getCurrentAccount(): return newLabel = str(item.text(0)) From bc6db7f0e5ec1a7cd8c9dfd190a2dde5c21b796d Mon Sep 17 00:00:00 2001 From: mailchuck Date: Mon, 19 Oct 2015 17:37:43 +0200 Subject: [PATCH 056/210] Switch to tab you're replying from Fixes #64 --- src/bitmessageqt/__init__.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 90aaa36a..794de654 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -593,7 +593,11 @@ class MyForm(QtGui.QMainWindow): self.timer.start(2000) # milliseconds QtCore.QObject.connect(self.timer, QtCore.SIGNAL("timeout()"), self.runEveryTwoSeconds) + # e.g. for editing labels self.recurDepth = 0 + + # switch back to this when replying + self.replyFromTab = None self.init_file_menu() self.init_inbox_popup_menu() @@ -2097,8 +2101,10 @@ class MyForm(QtGui.QMainWindow): self.ui.lineEditTo.setText('') self.ui.lineEditSubject.setText('') self.ui.textEditMessage.setText('') - self.ui.tabWidget.setCurrentIndex(0) - self.ui.tableWidgetInbox.setCurrentCell(0, 0) + if self.replyFromTab is not None: + self.ui.tabWidget.setCurrentIndex(self.replyFromTab) + self.replyFromTab = None + #self.ui.tableWidgetInbox.setCurrentCell(0, 0) else: self.statusBar().showMessage(_translate( "MainWindow", "Your \'To\' field is empty.")) @@ -2863,6 +2869,10 @@ class MyForm(QtGui.QMainWindow): tableWidget = self.getCurrentMessagelist() if not tableWidget: return + + # save this to return back after reply is done + self.replyFromTab = self.ui.tabWidget.currentIndex() + currentInboxRow = tableWidget.currentRow() toAddressAtCurrentInboxRow = str(tableWidget.item( currentInboxRow, 0).data(Qt.UserRole).toPyObject()) From 245ed6d78907e23dd95a363cb5de3c18fa70f01c Mon Sep 17 00:00:00 2001 From: mailchuck Date: Mon, 19 Oct 2015 20:03:06 +0200 Subject: [PATCH 057/210] Account listing and sorting - account listing and sorting has now a common function to reuse - combobox send from is now sorted, Fixes #59 --- src/bitmessageqt/__init__.py | 73 +++++++++++++++--------------------- src/bitmessageqt/account.py | 7 ++++ 2 files changed, 37 insertions(+), 43 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 794de654..c11029ce 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -467,9 +467,7 @@ class MyForm(QtGui.QMainWindow): db = {} enabled = {} - for toAddress in shared.config.sections(): - if toAddress == 'bitmessagesettings': - continue + for toAddress in getSortedAccounts(): isEnabled = shared.config.getboolean( toAddress, 'enabled') isChan = shared.safeConfigGetBoolean( @@ -553,20 +551,18 @@ class MyForm(QtGui.QMainWindow): # Ask the user if we may delete their old version 1 addresses if they # have any. - configSections = shared.config.sections() - for addressInKeysFile in configSections: - if addressInKeysFile != 'bitmessagesettings': - status, addressVersionNumber, streamNumber, hash = decodeAddress( - addressInKeysFile) - if addressVersionNumber == 1: - displayMsg = _translate( - "MainWindow", "One of your addresses, %1, is an old version 1 address. Version 1 addresses are no longer supported. " - + "May we delete it now?").arg(addressInKeysFile) - reply = QtGui.QMessageBox.question( - self, 'Message', displayMsg, QtGui.QMessageBox.Yes, QtGui.QMessageBox.No) - if reply == QtGui.QMessageBox.Yes: - shared.config.remove_section(addressInKeysFile) - shared.writeKeysFile() + for addressInKeysFile in getSortedAccounts(): + status, addressVersionNumber, streamNumber, hash = decodeAddress( + addressInKeysFile) + if addressVersionNumber == 1: + displayMsg = _translate( + "MainWindow", "One of your addresses, %1, is an old version 1 address. Version 1 addresses are no longer supported. " + + "May we delete it now?").arg(addressInKeysFile) + reply = QtGui.QMessageBox.question( + self, 'Message', displayMsg, QtGui.QMessageBox.Yes, QtGui.QMessageBox.No) + if reply == QtGui.QMessageBox.Yes: + shared.config.remove_section(addressInKeysFile) + shared.writeKeysFile() # Configure Bitmessage to start on startup (or remove the # configuration) based on the setting in the keys.dat file @@ -2182,15 +2178,14 @@ class MyForm(QtGui.QMainWindow): def rerenderComboBoxSendFrom(self): self.ui.comboBoxSendFrom.clear() - configSections = shared.config.sections() - for addressInKeysFile in configSections: - if addressInKeysFile != 'bitmessagesettings': - isEnabled = shared.config.getboolean( - addressInKeysFile, 'enabled') # I realize that this is poor programming practice but I don't care. It's easier for others to read. - isMaillinglist = shared.safeConfigGetBoolean(addressInKeysFile, 'mailinglist') - if isEnabled and not isMaillinglist: - self.ui.comboBoxSendFrom.insertItem(0, avatarize(addressInKeysFile), unicode(shared.config.get( - addressInKeysFile, 'label'), 'utf-8'), addressInKeysFile) + for addressInKeysFile in getSortedAccounts(): + isEnabled = shared.config.getboolean( + addressInKeysFile, 'enabled') # I realize that this is poor programming practice but I don't care. It's easier for others to read. + isMaillinglist = shared.safeConfigGetBoolean(addressInKeysFile, 'mailinglist') + if isEnabled and not isMaillinglist: + self.ui.comboBoxSendFrom.addItem(avatarize(addressInKeysFile), unicode(shared.config.get( + addressInKeysFile, 'label'), 'utf-8'), addressInKeysFile) +# self.ui.comboBoxSendFrom.model().sort(1, Qt.AscendingOrder) self.ui.comboBoxSendFrom.insertItem(0, '', '') if(self.ui.comboBoxSendFrom.count() == 2): self.ui.comboBoxSendFrom.setCurrentIndex(1) @@ -2199,15 +2194,13 @@ class MyForm(QtGui.QMainWindow): def rerenderComboBoxSendFromBroadcast(self): self.ui.comboBoxSendFromBroadcast.clear() - configSections = shared.config.sections() - for addressInKeysFile in configSections: - if addressInKeysFile != 'bitmessagesettings': - isEnabled = shared.config.getboolean( - addressInKeysFile, 'enabled') # I realize that this is poor programming practice but I don't care. It's easier for others to read. - isMaillinglist = shared.safeConfigGetBoolean(addressInKeysFile, 'mailinglist') - if isEnabled and isMaillinglist: - self.ui.comboBoxSendFromBroadcast.insertItem(0, avatarize(addressInKeysFile), unicode(shared.config.get( - addressInKeysFile, 'label'), 'utf-8'), addressInKeysFile) + for addressInKeysFile in getSortedAccounts(): + isEnabled = shared.config.getboolean( + addressInKeysFile, 'enabled') # I realize that this is poor programming practice but I don't care. It's easier for others to read. + isMaillinglist = shared.safeConfigGetBoolean(addressInKeysFile, 'mailinglist') + if isEnabled and isMaillinglist: + self.ui.comboBoxSendFromBroadcast.addItem(avatarize(addressInKeysFile), unicode(shared.config.get( + addressInKeysFile, 'label'), 'utf-8'), addressInKeysFile) self.ui.comboBoxSendFromBroadcast.insertItem(0, '', '') if(self.ui.comboBoxSendFromBroadcast.count() == 2): self.ui.comboBoxSendFromBroadcast.setCurrentIndex(1) @@ -2711,10 +2704,7 @@ class MyForm(QtGui.QMainWindow): def click_NewAddressDialog(self): addresses = [] - configSections = shared.config.sections() - for addressInKeysFile in configSections: - if addressInKeysFile == 'bitmessagesettings': - continue + for addressInKeysFile in getSortedAccounts(): addresses.append(addressInKeysFile) # self.dialog = Ui_NewAddressWizard(addresses) # self.dialog.exec_() @@ -4054,10 +4044,7 @@ class NewAddressDialog(QtGui.QDialog): row = 1 # Let's fill out the 'existing address' combo box with addresses from # the 'Your Identities' tab. - configSections = shared.config.sections() - for addressInKeysFile in configSections: - if addressInKeysFile == 'bitmessagesettings': - continue + for addressInKeysFile in getSortedAccounts(): self.ui.radioButtonExisting.click() self.ui.comboBoxExisting.addItem( addressInKeysFile) diff --git a/src/bitmessageqt/account.py b/src/bitmessageqt/account.py index 19212302..2342bcc5 100644 --- a/src/bitmessageqt/account.py +++ b/src/bitmessageqt/account.py @@ -10,6 +10,13 @@ from pyelliptic.openssl import OpenSSL from utils import str_broadcast_subscribers import time +def getSortedAccounts(): + configSections = filter(lambda x: x != 'bitmessagesettings', shared.config.sections()) + configSections.sort(cmp = + lambda x,y: cmp(unicode(shared.config.get(x, 'label'), 'utf-8').lower(), unicode(shared.config.get(y, 'label'), 'utf-8').lower()) + ) + return configSections + def accountClass(address): if not shared.config.has_section(address): if address == str_broadcast_subscribers: From f05407afa30d4f2feefd3c310c20dc703325a450 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Mon, 19 Oct 2015 20:17:28 +0200 Subject: [PATCH 058/210] Changing label updates SendFrom combobox Fixes #2 --- src/bitmessageqt/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index c11029ce..442346be 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -3546,6 +3546,10 @@ class MyForm(QtGui.QMainWindow): shared.config.set(str(item.address), 'label', newLabel) item.updateText() shared.writeKeysFile() + if item.type == 'mailinglist': + self.rerenderComboBoxSendFromBroadcast() + else: + self.rerenderComboBoxSendFrom() self.recurDepth -= 1 def tableWidgetInboxItemClicked(self): From 44ecf909366671792ae8ac789e890836b9d92c8f Mon Sep 17 00:00:00 2001 From: mailchuck Date: Mon, 19 Oct 2015 21:12:10 +0200 Subject: [PATCH 059/210] Special address behaviour updates send comboboxes Fixes #57 --- src/bitmessageqt/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 442346be..aa4ea4a2 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2670,6 +2670,8 @@ class MyForm(QtGui.QMainWindow): shared.config.set(str(addressAtCurrentRow), 'mailinglistname', str( self.dialog.ui.lineEditMailingListName.text().toUtf8())) self.setCurrentItemColor(QtGui.QColor(137, 04, 177)) #magenta + self.rerenderComboBoxSendFrom() + self.rerenderComboBoxSendFromBroadcast() shared.writeKeysFile() self.rerenderInboxToLabels() From 386094f1ade0b37edfb833ad5f710cbaafb27a3c Mon Sep 17 00:00:00 2001 From: mailchuck Date: Mon, 19 Oct 2015 21:26:46 +0200 Subject: [PATCH 060/210] Version bump Just for development --- src/shared.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared.py b/src/shared.py index 0ae2d40d..54093f51 100644 --- a/src/shared.py +++ b/src/shared.py @@ -1,6 +1,6 @@ from __future__ import division -softwareVersion = '0.4.5.3' +softwareVersion = '0.5.0' verbose = 1 maximumAgeOfAnObjectThatIAmWillingToAccept = 216000 # This is obsolete with the change to protocol v3 but the singleCleaner thread still hasn't been updated so we need this a little longer. lengthOfTimeToHoldOnToAllPubkeys = 2419200 # Equals 4 weeks. You could make this longer if you want but making it shorter would not be advisable because there is a very small possibility that it could keep you from obtaining a needed pubkey for a period of time. From 611574c3424ab41098752a0a79ba396c78228910 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Mon, 19 Oct 2015 21:30:38 +0200 Subject: [PATCH 061/210] There will not be a Mailchuck PyBitmessage anymore Since I'm merging my fork into normal pybitmessage, there won't be separate configs. --- src/bitmessageqt/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index aa4ea4a2..8823000e 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -3629,7 +3629,7 @@ class MyForm(QtGui.QMainWindow): self.statusBar().showMessage(data) def readSettings(self): - settings = QSettings("Mailchuck Ltd.", "PyBitmessage") + settings = QSettings("Bitmessage", "PyBitmessage") try: geom = settings.value("geometry") self.restoreGeometry(geom.toByteArray() if hasattr(geom, 'toByteArray') else geom) From dd47b9ab652d44901c8f14f6eae1445fd7cf404a Mon Sep 17 00:00:00 2001 From: mailchuck Date: Mon, 19 Oct 2015 22:02:09 +0200 Subject: [PATCH 062/210] No more Mailchuck Pybitmessage 2nd Last commit only changed loading, this also saving. --- src/bitmessageqt/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 8823000e..4daaf8a7 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2756,7 +2756,7 @@ class MyForm(QtGui.QMainWindow): if self.mmapp is not None: self.mmapp.unregister() - settings = QSettings("Mailchuck Ltd.", "PyBitmessage") + settings = QSettings("Bitmessage", "PyBitmessage") settings.setValue("geometry", self.saveGeometry()) settings.setValue("state", self.saveState()) From 5660f039a680dfeb673dab30634fd16ff45ad38b Mon Sep 17 00:00:00 2001 From: mailchuck Date: Mon, 19 Oct 2015 22:33:18 +0200 Subject: [PATCH 063/210] Passive version check Bitmessage will now notify you if it encounters someone with a newer version. Takes into account that it should not recommend switching from stable to unstable and vice versa. Also, temporarily treats 0.5 as a mailchuck fork. Fixes #43 --- src/bitmessageqt/__init__.py | 29 +++++++++++++++++++++++++++++ src/class_receiveDataThread.py | 11 +++++++++++ 2 files changed, 40 insertions(+) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 4daaf8a7..e6f42cd7 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -718,6 +718,8 @@ class MyForm(QtGui.QMainWindow): "rerenderBlackWhiteList()"), self.rerenderBlackWhiteList) QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL( "removeInboxRowByMsgid(PyQt_PyObject)"), self.removeInboxRowByMsgid) + QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL( + "newVersionAvailable(PyQt_PyObject)"), self.newVersionAvailable) QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL( "displayAlert(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"), self.displayAlert) self.UISignalThread.start() @@ -1814,6 +1816,31 @@ class MyForm(QtGui.QMainWindow): inbox.removeRow(i) break self.changedInboxUnread() + + def newVersionAvailable(self, version): +# if (not (self.windowState() & QtCore.Qt.WindowActive)) or (self.windowState() & QtCore.Qt.WindowMinimized): +# print "SHIIIIIIIIIIIIIIIIT" +# return + # only notify once until next restart + try: + if self.notifiedNewVersion: + return + except AttributeError: + pass + + self.notifiedNewVersion = ".".join(str(n) for n in version) + message = "New " + if version[1] % 2: + message += "UNSTABLE" + else: + message += "stable" + message += " version of PyBitmessage is available: " + self.notifiedNewVersion + ". Download it from https://github.com/" + if version[0] == 0 and version[1] == 5: + message += "mailchuck" + else: + message += "Bitmessage" + message += "/PyBitmessage/releases/latest" + self.displayAlert("New release of PyBitmessage available", message, False) def displayAlert(self, title, text, exitAfterUserClicksOk): self.statusBar().showMessage(text) @@ -4145,6 +4172,8 @@ class UISignaler(QThread): self.emit(SIGNAL("rerenderBlackWhiteList()")) elif command == 'removeInboxRowByMsgid': self.emit(SIGNAL("removeInboxRowByMsgid(PyQt_PyObject)"), data) + elif command == 'newVersionAvailable': + self.emit(SIGNAL("newVersionAvailable(PyQt_PyObject)"), data) elif command == 'alert': title, text, exitAfterUserClicksOk = data self.emit(SIGNAL("displayAlert(PyQt_PyObject, PyQt_PyObject, PyQt_PyObject)"), title, text, exitAfterUserClicksOk) diff --git a/src/class_receiveDataThread.py b/src/class_receiveDataThread.py index ff5371ad..07569b7b 100644 --- a/src/class_receiveDataThread.py +++ b/src/class_receiveDataThread.py @@ -698,6 +698,17 @@ class receiveDataThread(threading.Thread): data[80:84]) readPosition = 80 + lengthOfUseragentVarint useragent = data[readPosition:readPosition + useragentLength] + + # version check + userAgentName, userAgentVersion = useragent[1:-1].split(":") + if userAgentName == "PyBitmessage": + myVersion = [int(n) for n in shared.softwareVersion.split(".")] + remoteVersion = [int(n) for n in userAgentVersion.split(".")] + # remote is newer, but do not cross between stable and unstable + if cmp(remoteVersion, myVersion) > 0 and \ + (myVersion[1] % 2 == remoteVersion[1] % 2): + shared.UISignalQueue.put(('newVersionAvailable', remoteVersion)) + readPosition += useragentLength numberOfStreamsInVersionMessage, lengthOfNumberOfStreamsInVersionMessage = decodeVarint( data[readPosition:]) From 6ba8b553d24900b7d8fb4da50fe42b8765ec6c90 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Mon, 19 Oct 2015 22:36:30 +0200 Subject: [PATCH 064/210] Remove expletive That wasn't necessary. --- src/bitmessageqt/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index e6f42cd7..bc2862fe 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -1819,7 +1819,6 @@ class MyForm(QtGui.QMainWindow): def newVersionAvailable(self, version): # if (not (self.windowState() & QtCore.Qt.WindowActive)) or (self.windowState() & QtCore.Qt.WindowMinimized): -# print "SHIIIIIIIIIIIIIIIIT" # return # only notify once until next restart try: From 71553ae188ffa1d584b43c93869ea4b6626306e9 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Thu, 22 Oct 2015 23:16:44 +0200 Subject: [PATCH 065/210] Fix keypress in chan and subscription messagelists Fixes #65 --- src/bitmessageqt/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index bc2862fe..cc2cefad 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -1414,7 +1414,7 @@ class MyForm(QtGui.QMainWindow): def tableWidgetInboxKeyPressEvent(self, event): if event.key() == QtCore.Qt.Key_Delete: self.on_action_InboxTrash() - return QtGui.QTableWidget.keyPressEvent(self.ui.tableWidgetInbox, event) + return QtGui.QTableWidget.keyPressEvent(self.getCurrentMessagelist(), event) # menu button 'manage keys' def click_actionManageKeys(self): From 01df34680d749cd2ef864fa8cedd47295aca8457 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Thu, 22 Oct 2015 23:56:20 +0200 Subject: [PATCH 066/210] New message in correct tab Newly arriving messages now appear in the correct tab. Previously it assumed it was always "Messages" tab. Partially addresses #3. --- src/bitmessageqt/__init__.py | 77 ++++++++++++++++++++++++++---------- 1 file changed, 57 insertions(+), 20 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index cc2cefad..7dc0ed77 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2238,31 +2238,33 @@ class MyForm(QtGui.QMainWindow): # pseudo-mailing-list. The message will be broadcast out. This function # puts the message on the 'Sent' tab. def displayNewSentMessage(self, toAddress, toLabel, fromAddress, subject, message, ackdata): - if self.getCurrentFolder() != "sent" or self.getCurrentAccount() != fromAddress: - return subject = shared.fixPotentiallyInvalidUTF8Data(subject) message = shared.fixPotentiallyInvalidUTF8Data(message) acct = accountClass(fromAddress) acct.parseMessage(toAddress, fromAddress, subject, message) + sent = self.getAccountMessagelist(acct) + treeWidget = self.getAccountTreeWidget(acct) + if self.getCurrentFolder(treeWidget) != "sent" or self.getCurrentAccount(treeWidget) != fromAddress: + return - self.ui.tableWidgetInbox.setSortingEnabled(False) - self.ui.tableWidgetInbox.insertRow(0) + sent.setSortingEnabled(False) + sent.insertRow(0) newItem = QtGui.QTableWidgetItem(unicode(acct.toLabel, 'utf-8')) newItem.setToolTip(unicode(acct.toLabel, 'utf-8')) newItem.setData(Qt.UserRole, str(toAddress)) newItem.setIcon(avatarize(toAddress)) - self.ui.tableWidgetInbox.setItem(0, 0, newItem) + sent.setItem(0, 0, newItem) newItem = QtGui.QTableWidgetItem(unicode(acct.fromLabel, 'utf-8')) newItem.setToolTip(unicode(acct.fromLabel, 'utf-8')) newItem.setData(Qt.UserRole, str(fromAddress)) newItem.setIcon(avatarize(fromAddress)) - self.ui.tableWidgetInbox.setItem(0, 1, newItem) + sent.setItem(0, 1, newItem) newItem = QtGui.QTableWidgetItem(unicode(acct.subject, 'utf-8)')) newItem.setToolTip(unicode(acct.subject, 'utf-8)')) newItem.setData(Qt.UserRole, str(subject)) #newItem.setData(Qt.UserRole, unicode(message, 'utf-8)')) # No longer hold the message in the table; we'll use a SQL query to display it as needed. - self.ui.tableWidgetInbox.setItem(0, 2, newItem) + sent.setItem(0, 2, newItem) # newItem = QtGui.QTableWidgetItem('Doing work necessary to send # broadcast...'+ # l10n.formatTimestamp()) @@ -2270,18 +2272,18 @@ class MyForm(QtGui.QMainWindow): newItem.setToolTip(_translate("MainWindow", "Work is queued. %1").arg(l10n.formatTimestamp())) newItem.setData(Qt.UserRole, QByteArray(ackdata)) newItem.setData(33, int(time.time())) - self.ui.tableWidgetInbox.setItem(0, 3, newItem) - self.ui.textEditInboxMessage.setPlainText(unicode(message, 'utf-8)')) - self.ui.tableWidgetInbox.setSortingEnabled(True) + sent.setItem(0, 3, newItem) + self.getAccountTextedit(acct).setPlainText(unicode(message, 'utf-8)')) + sent.setSortingEnabled(True) def displayNewInboxMessage(self, inventoryHash, toAddress, fromAddress, subject, message): - if (self.getCurrentFolder() != "inbox" and self.getCurrentFolder() != False) or self.getCurrentAccount() != toAddress: - return subject = shared.fixPotentiallyInvalidUTF8Data(subject) acct = accountClass(toAddress) acct.parseMessage(toAddress, fromAddress, subject, message) - - inbox = self.getCurrentMessagelist() + inbox = self.getAccountMessagelist(acct) + treeWidget = self.getAccountTreeWidget(acct) + if (self.getCurrentFolder(treeWidget) != "inbox" and self.getCurrentFolder(treeWidget) != False) or self.getCurrentAccount(treeWidget) != toAddress: + return font = QFont() font.setBold(True) @@ -3264,6 +3266,17 @@ class MyForm(QtGui.QMainWindow): else: return False + def getAccountTreeWidget(self, account): + try: + if account.type == 'chan': + return self.ui.treeWidgetChans + elif account.type == 'subscription': + return self.ui.treeWidgetSubscriptions + else: + return self.ui.treeWidgetYourIdentities + except: + return self.ui.treeWidgetYourIdentities + def getCurrentMessagelist(self): currentIndex = self.ui.tabWidget.currentIndex(); messagelistList = [ @@ -3276,6 +3289,17 @@ class MyForm(QtGui.QMainWindow): return messagelistList[currentIndex] else: return False + + def getAccountMessagelist(self, account): + try: + if account.type == 'chan': + return self.ui.tableWidgetInboxChans + elif account.type == 'subscription': + return self.ui.tableWidgetInboxSubscriptions + else: + return self.ui.tableWidgetInbox + except: + return self.ui.tableWidgetInbox def getCurrentMessageId(self): messagelist = self.getCurrentMessagelist() @@ -3300,6 +3324,17 @@ class MyForm(QtGui.QMainWindow): else: return False + def getAccountTextedit(self, account): + try: + if account.type == 'chan': + return self.ui.textEditInboxMessageChans + elif account.type == 'subscription': + return self.ui.textEditInboxSubscriptions + else: + return self.ui.textEditInboxMessage + except: + return self.ui.textEditInboxMessage + def getCurrentSearchLine(self): currentIndex = self.ui.tabWidget.currentIndex(); messagelistList = [ @@ -3327,16 +3362,17 @@ class MyForm(QtGui.QMainWindow): return False # Group of functions for the Your Identities dialog box - def getCurrentItem(self): - treeWidget = self.getCurrentTreeWidget() + def getCurrentItem(self, treeWidget = None): + if treeWidget is None: + treeWidget = self.getCurrentTreeWidget() if treeWidget: currentItem = treeWidget.currentItem() if currentItem: return currentItem return False - def getCurrentAccount(self): - currentItem = self.getCurrentItem() + def getCurrentAccount(self, treeWidget = None): + currentItem = self.getCurrentItem(treeWidget) if currentItem: account = currentItem.address return account @@ -3344,8 +3380,9 @@ class MyForm(QtGui.QMainWindow): # TODO need debug msg? return False - def getCurrentFolder(self): - treeWidget = self.getCurrentTreeWidget() + def getCurrentFolder(self, treeWidget = None): + if treeWidget is None: + treeWidget = self.getCurrentTreeWidget() #treeWidget = self.ui.treeWidgetYourIdentities if treeWidget: currentItem = treeWidget.currentItem() From 77232226e68546395242e549b84f14670b20d985 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Fri, 23 Oct 2015 07:07:38 +0200 Subject: [PATCH 067/210] Put mailing lists behind normal accounts Fixes #58 --- src/bitmessageqt/foldertree.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/bitmessageqt/foldertree.py b/src/bitmessageqt/foldertree.py index 59e2b963..71caae66 100644 --- a/src/bitmessageqt/foldertree.py +++ b/src/bitmessageqt/foldertree.py @@ -132,16 +132,21 @@ class Ui_AddressWidget(QtGui.QTreeWidgetItem): reverse = False if self.treeWidget().header().sortIndicatorOrder() == QtCore.Qt.DescendingOrder: reverse = True - if shared.config.getboolean(self.address, 'enabled') == shared.config.getboolean(other.address, 'enabled'): - if shared.config.get(self.address, 'label'): - x = shared.config.get(self.address, 'label').decode('utf-8').lower() + if shared.config.getboolean(self.address, 'enabled') == \ + shared.config.getboolean(other.address, 'enabled'): + if shared.safeConfigGetBoolean(self.address, 'mailinglist') == \ + shared.safeConfigGetBoolean(other.address, 'mailinglist'): + if shared.config.get(self.address, 'label'): + x = shared.config.get(self.address, 'label').decode('utf-8').lower() + else: + x = self.address.decode('utf-8').lower() + if shared.config.get(other.address, 'label'): + y = shared.config.get(other.address, 'label').decode('utf-8').lower() + else: + y = other.address.decode('utf-8').lower() + return x < y else: - x = self.address.decode('utf-8').lower() - if shared.config.get(other.address, 'label'): - y = shared.config.get(other.address, 'label').decode('utf-8').lower() - else: - y = other.address.decode('utf-8').lower() - return x < y + return (reverse if shared.safeConfigGetBoolean(self.address, 'mailinglist') else not reverse) # else: return (not reverse if shared.config.getboolean(self.address, 'enabled') else reverse) From c9e805793942926af51d3fc89e10a7bdd9d92df0 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Fri, 23 Oct 2015 12:38:59 +0200 Subject: [PATCH 068/210] Sent folder for chans and subscriptions Fixes #53 --- src/bitmessageqt/__init__.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 7dc0ed77..8f78ef41 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -416,7 +416,7 @@ class MyForm(QtGui.QMainWindow): def rerenderTabTreeSubscriptions(self): treeWidget = self.ui.treeWidgetSubscriptions - folders = ['inbox', 'trash'] + folders = ['inbox', 'sent', 'trash'] treeWidget.clear() treeWidget.setSortingEnabled(False) treeWidget.header().setSortIndicator(0, Qt.AscendingOrder) @@ -455,10 +455,9 @@ class MyForm(QtGui.QMainWindow): def rerenderTabTree(self, tab): if tab == 'messages': treeWidget = self.ui.treeWidgetYourIdentities - folders = ['inbox', 'sent', 'trash'] elif tab == 'chan': treeWidget = self.ui.treeWidgetChans - folders = ['inbox', 'trash'] + folders = ['inbox', 'sent', 'trash'] # sort ascending when creating if treeWidget.topLevelItemCount() == 0: @@ -888,10 +887,15 @@ class MyForm(QtGui.QMainWindow): tableWidget.setColumnHidden(0, False) tableWidget.setColumnHidden(1, True) tableWidget.setSortingEnabled(False) - + + if tableWidget == self.ui.tableWidgetInboxChans or tableWidget == self.ui.tableWidgetInboxSubscriptions: + xAddress = 'toaddress' + else: + xAddress = 'fromaddress' + sqlStatement = ''' SELECT toaddress, fromaddress, subject, status, ackdata, lastactiontime - FROM sent WHERE fromaddress=? AND folder="sent" AND %s LIKE ? + FROM sent WHERE ''' + xAddress + '''=? AND folder="sent" AND %s LIKE ? ORDER BY lastactiontime ''' % (where,) From 676ea5544ee418a895a7fdc288d6f6880f09dbe3 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Fri, 23 Oct 2015 19:14:01 +0200 Subject: [PATCH 069/210] Addressbook contains chans, subscriptions and is sorted Fixes #5 --- src/bitmessageqt/__init__.py | 36 ++++++++++++++++++++------ src/bitmessageqt/foldertree.py | 46 +++++++++++++++++++++++++++++++++- 2 files changed, 73 insertions(+), 9 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 8f78ef41..ba280ef9 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -1949,18 +1949,38 @@ class MyForm(QtGui.QMainWindow): i, 0).setText(unicode(toLabel, 'utf-8')) def rerenderAddressBook(self): + def addRow (address, label, type): + self.ui.tableWidgetAddressBook.insertRow(0) + newItem = Ui_AddressBookWidgetItemLabel(address, unicode(label, 'utf-8'), type) + self.ui.tableWidgetAddressBook.setItem(0, 0, newItem) + newItem = Ui_AddressBookWidgetItemAddress(address, unicode(label, 'utf-8'), type) + self.ui.tableWidgetAddressBook.setItem(0, 1, newItem) + + self.ui.tableWidgetAddressBook.setSortingEnabled(False) self.ui.tableWidgetAddressBook.setRowCount(0) + + # subscriptions + queryreturn = sqlQuery('SELECT label, address FROM subscriptions WHERE enabled = 1') + for row in queryreturn: + label, address = row + addRow(address, label, 'subscription') + + # chans + addresses = getSortedAccounts() + for address in addresses: + account = accountClass(address) + if (account.type == 'chan'): + addRow(address, account.getLabel(), 'chan') + + # normal accounts queryreturn = sqlQuery('SELECT * FROM addressbook') for row in queryreturn: label, address = row - self.ui.tableWidgetAddressBook.insertRow(0) - newItem = QtGui.QTableWidgetItem(unicode(label, 'utf-8')) - newItem.setIcon(avatarize(address)) - self.ui.tableWidgetAddressBook.setItem(0, 0, newItem) - newItem = QtGui.QTableWidgetItem(address) - newItem.setFlags( - QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) - self.ui.tableWidgetAddressBook.setItem(0, 1, newItem) + addRow(address, label, 'normal') + + # sort + self.ui.tableWidgetAddressBook.sortItems(0, QtCore.Qt.AscendingOrder) + self.ui.tableWidgetAddressBook.setSortingEnabled(True) def rerenderSubscriptions(self): self.rerenderTabTreeSubscriptions() diff --git a/src/bitmessageqt/foldertree.py b/src/bitmessageqt/foldertree.py index 71caae66..08cd7ff2 100644 --- a/src/bitmessageqt/foldertree.py +++ b/src/bitmessageqt/foldertree.py @@ -238,4 +238,48 @@ class Ui_SubscriptionWidget(Ui_AddressWidget): # else: return (not reverse if self.isEnabled else reverse) - return super(QtGui.QTreeWidgetItem, self).__lt__(other) \ No newline at end of file + return super(QtGui.QTreeWidgetItem, self).__lt__(other) + +class Ui_AddressBookWidgetItem(QtGui.QTableWidgetItem): + _types = {'normal': 0, 'chan': 1, 'subscription': 2} + + def __init__ (self, text, type = 'normal'): + super(QtGui.QTableWidgetItem, self).__init__(text) + self.label = text + try: + self.type = self._types[type] + except: + self.type = 0 + if self.type == 2: + brush = QtGui.QBrush(QtGui.QColor(137, 04, 177)) + elif self.type == 1: + brush = QtGui.QBrush(QtGui.QColor(216, 119, 0)) + else: + return + brush.setStyle(QtCore.Qt.NoBrush) + self.setForeground(brush) + + def __lt__ (self, other): + if (isinstance(other, Ui_AddressBookWidgetItem)): + reverse = False + if self.tableWidget().horizontalHeader().sortIndicatorOrder() == QtCore.Qt.DescendingOrder: + reverse = True + if self.type == other.type: + return self.label.decode('utf-8').lower() < other.label.decode('utf-8').lower() + else: + return (not reverse if self.type < other.type else reverse) + return super(QtGui.QTableWidgetItem, self).__lt__(other) + + +class Ui_AddressBookWidgetItemLabel(Ui_AddressBookWidgetItem): + def __init__ (self, address, label, type): + Ui_AddressBookWidgetItem.__init__(self, label, type) + self.label = label + self.setIcon(avatarize(address)) + + +class Ui_AddressBookWidgetItemAddress(Ui_AddressBookWidgetItem): + def __init__ (self, address, label, type): + Ui_AddressBookWidgetItem.__init__(self, address, type) + self.label = label + self.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) \ No newline at end of file From 17b43df474b51add97dda606e950e9e2f00f1f42 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Fri, 23 Oct 2015 19:23:16 +0200 Subject: [PATCH 070/210] Sent in chans and subscriptions columns Now it shows "from" instead of "to" column when selecting sent folder in subscriptions or chans. Fixes #53 --- src/bitmessageqt/__init__.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index ba280ef9..bfe20e82 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -883,14 +883,15 @@ class MyForm(QtGui.QMainWindow): else: where = "toaddress || fromaddress || subject || message" - - tableWidget.setColumnHidden(0, False) - tableWidget.setColumnHidden(1, True) tableWidget.setSortingEnabled(False) if tableWidget == self.ui.tableWidgetInboxChans or tableWidget == self.ui.tableWidgetInboxSubscriptions: + tableWidget.setColumnHidden(0, True) + tableWidget.setColumnHidden(1, False) xAddress = 'toaddress' else: + tableWidget.setColumnHidden(0, False) + tableWidget.setColumnHidden(1, True) xAddress = 'fromaddress' sqlStatement = ''' From f4f0ce6a97d6421bc9dff87217125d9bcf736ca0 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Tue, 27 Oct 2015 17:41:04 +0100 Subject: [PATCH 071/210] Spanish wrongly spelled Fixes #74 --- src/bitmessageqt/settings.py | 2 +- src/bitmessageqt/settings.ui | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bitmessageqt/settings.py b/src/bitmessageqt/settings.py index 6d854773..b9131315 100644 --- a/src/bitmessageqt/settings.py +++ b/src/bitmessageqt/settings.py @@ -92,7 +92,7 @@ class Ui_settingsDialog(object): self.languageComboBox.addItem(_fromUtf8("")) self.languageComboBox.setItemText(4, _fromUtf8("Deutsch")) self.languageComboBox.addItem(_fromUtf8("")) - self.languageComboBox.setItemText(5, _fromUtf8("Españl")) + self.languageComboBox.setItemText(5, _fromUtf8("Español")) self.languageComboBox.addItem(_fromUtf8("")) self.languageComboBox.setItemText(6, _fromUtf8("русский")) self.languageComboBox.addItem(_fromUtf8("")) diff --git a/src/bitmessageqt/settings.ui b/src/bitmessageqt/settings.ui index a41d7e4e..475821f8 100644 --- a/src/bitmessageqt/settings.ui +++ b/src/bitmessageqt/settings.ui @@ -153,7 +153,7 @@ - Españl + Español From 97b38f9daff383c663812c492f147918b8aba0f8 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Tue, 27 Oct 2015 19:24:29 +0100 Subject: [PATCH 072/210] Refactoring foldertree Colors consistent Sorting more consistent Deduplication of methods Context menu enable/disable affects tree Fixes #73 --- src/bitmessageqt/__init__.py | 14 +-- src/bitmessageqt/foldertree.py | 159 +++++++++++++++------------------ 2 files changed, 82 insertions(+), 91 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index bfe20e82..6668e6a6 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -529,7 +529,7 @@ class MyForm(QtGui.QMainWindow): i = 0 for toAddress in db: - widget = Ui_AddressWidget(treeWidget, i, toAddress, db[toAddress]["inbox"]) + widget = Ui_AddressWidget(treeWidget, i, toAddress, db[toAddress]["inbox"], enabled[toAddress]) j = 0 unread = 0 for folder in folders: @@ -3200,7 +3200,8 @@ class MyForm(QtGui.QMainWindow): sqlExecute( '''update subscriptions set enabled=1 WHERE address=?''', address) - self.setCurrentItemColor(QApplication.palette().text().color()) + account = self.getCurrentItem() + account.setEnabled(True) shared.reloadBroadcastSendersForWhichImWatching() def on_action_SubscriptionsDisable(self): @@ -3208,7 +3209,8 @@ class MyForm(QtGui.QMainWindow): sqlExecute( '''update subscriptions set enabled=0 WHERE address=?''', address) - self.setCurrentItemColor(QtGui.QColor(128, 128, 128)) + account = self.getCurrentItem() + account.setEnabled(False) shared.reloadBroadcastSendersForWhichImWatching() def on_context_menuSubscriptions(self, point): @@ -3432,7 +3434,8 @@ class MyForm(QtGui.QMainWindow): def on_action_Enable(self): addressAtCurrentRow = self.getCurrentAccount() self.enableIdentity(addressAtCurrentRow) - self.setCurrentItemColor(QApplication.palette().text().color()) + account = self.getCurrentItem() + account.setEnabled(True) def enableIdentity(self, address): shared.config.set(address, 'enabled', 'true') @@ -3442,7 +3445,8 @@ class MyForm(QtGui.QMainWindow): def on_action_Disable(self): address = self.getCurrentAccount() self.disableIdentity(address) - self.setCurrentItemColor(QtGui.QColor(128, 128, 128)) + account = self.getCurrentItem() + account.setEnabled(False) def disableIdentity(self, address): shared.config.set(str(address), 'enabled', 'false') diff --git a/src/bitmessageqt/foldertree.py b/src/bitmessageqt/foldertree.py index 08cd7ff2..fd4bb9e8 100644 --- a/src/bitmessageqt/foldertree.py +++ b/src/bitmessageqt/foldertree.py @@ -3,15 +3,16 @@ from PyQt4 import QtCore, QtGui from utils import * import shared -class Ui_FolderWidget(QtGui.QTreeWidgetItem): - folderWeight = {"inbox": 1, "sent": 2, "trash": 3} - def __init__(self, parent, pos = 0, address = "", folderName = "", unreadCount = 0): - super(QtGui.QTreeWidgetItem, self).__init__() - self.address = address - self.folderName = folderName - self.unreadCount = unreadCount - parent.insertChild(pos, self) - self.updateText() +class AccountMixin (object): + def accountColor (self): + if not self.isEnabled: + return QtGui.QBrush(QtGui.QColor(128, 128, 128)) + elif self.type == "chan": + return QtGui.QBrush(QtGui.QColor(216, 119, 0)) + elif self.type == "mailinglist" or self.type == "subscription": + return QtGui.QBrush(QtGui.QColor(137, 04, 177)) + else: + return QtGui.QBrush(QtGui.QApplication.palette().text().color()) def setAddress(self, address): self.address = str(address) @@ -21,11 +22,41 @@ class Ui_FolderWidget(QtGui.QTreeWidgetItem): self.unreadCount = int(cnt) self.updateText() + def setEnabled(self, enabled): + self.isEnabled = enabled + self.updateText() + + def setType(self): + if shared.safeConfigGetBoolean(self.address, 'chan'): + self.type = "chan" + elif shared.safeConfigGetBoolean(self.address, 'mailinglist'): + self.type = "mailinglist" + else: + self.type = "normal" + + def updateText(self): + pass + + +class Ui_FolderWidget(QtGui.QTreeWidgetItem, AccountMixin): + folderWeight = {"inbox": 1, "sent": 2, "trash": 3} + def __init__(self, parent, pos = 0, address = "", folderName = "", unreadCount = 0): + super(QtGui.QTreeWidgetItem, self).__init__() + self.initialised = False + self.setAddress(address) + self.setFolderName(folderName) + self.setUnreadCount(unreadCount) + self.initialised = True + self.updateText() + parent.insertChild(pos, self) + def setFolderName(self, fname): self.folderName = str(fname) self.updateText() def updateText(self): + if not self.initialised: + return text = QtGui.QApplication.translate("MainWindow", self.folderName) font = QtGui.QFont() if self.unreadCount > 0: @@ -60,34 +91,23 @@ class Ui_FolderWidget(QtGui.QTreeWidgetItem): return super(QtGui.QTreeWidgetItem, self).__lt__(other) -class Ui_AddressWidget(QtGui.QTreeWidgetItem): - def __init__(self, parent, pos = 0, address = "", unreadCount = 0): +class Ui_AddressWidget(QtGui.QTreeWidgetItem, AccountMixin): + def __init__(self, parent, pos = 0, address = "", unreadCount = 0, enabled = True): super(QtGui.QTreeWidgetItem, self).__init__() - self.unreadCount = unreadCount parent.insertTopLevelItem(pos, self) # only set default when creating #super(QtGui.QTreeWidgetItem, self).setExpanded(shared.config.getboolean(self.address, 'enabled')) + self.initialised = False self.setAddress(address) - - def setAddress(self, address): - self.address = str(address) + self.setEnabled(enabled) + self.setUnreadCount(unreadCount) self.setType() - self.setExpanded(shared.safeConfigGetBoolean(self.address, 'enabled')) - self.updateText() - - def setType(self): - if shared.safeConfigGetBoolean(self.address, 'chan'): - self.type = "chan" - elif shared.safeConfigGetBoolean(self.address, 'mailinglist'): - self.type = "mailinglist" - else: - self.type = "normal" + self.initialised = True + self.setExpanded(enabled) # does updateText - def setUnreadCount(self, cnt): - self.unreadCount = int(cnt) - self.updateText() - def updateText(self): + if not self.initialised: + return text = unicode(shared.config.get(self.address, 'label'), 'utf-8)') + ' (' + self.address + ')' font = QtGui.QFont() @@ -101,15 +121,7 @@ class Ui_AddressWidget(QtGui.QTreeWidgetItem): self.setFont(0, font) #set text color - if shared.safeConfigGetBoolean(self.address, 'enabled'): - if shared.safeConfigGetBoolean(self.address, 'mailinglist'): - brush = QtGui.QBrush(QtGui.QColor(137, 04, 177)) - else: - brush = QtGui.QBrush(QtGui.QApplication.palette().text().color()) - #self.setExpanded(True) - else: - brush = QtGui.QBrush(QtGui.QColor(128, 128, 128)) - #self.setExpanded(False) + brush = self.accountColor() brush.setStyle(QtCore.Qt.NoBrush) self.setForeground(0, brush) @@ -132,10 +144,8 @@ class Ui_AddressWidget(QtGui.QTreeWidgetItem): reverse = False if self.treeWidget().header().sortIndicatorOrder() == QtCore.Qt.DescendingOrder: reverse = True - if shared.config.getboolean(self.address, 'enabled') == \ - shared.config.getboolean(other.address, 'enabled'): - if shared.safeConfigGetBoolean(self.address, 'mailinglist') == \ - shared.safeConfigGetBoolean(other.address, 'mailinglist'): + if self.isEnabled == other.isEnabled: + if self.type == other.type: if shared.config.get(self.address, 'label'): x = shared.config.get(self.address, 'label').decode('utf-8').lower() else: @@ -146,44 +156,37 @@ class Ui_AddressWidget(QtGui.QTreeWidgetItem): y = other.address.decode('utf-8').lower() return x < y else: - return (reverse if shared.safeConfigGetBoolean(self.address, 'mailinglist') else not reverse) + return (reverse if self.type == "mailinglist" else not reverse) # else: - return (not reverse if shared.config.getboolean(self.address, 'enabled') else reverse) + return (not reverse if self.isEnabled else reverse) return super(QtGui.QTreeWidgetItem, self).__lt__(other) -class Ui_SubscriptionWidget(Ui_AddressWidget): - def __init__(self, parent, pos = 0, address = "", unreadCount = 0, label = "", enabled = ""): +class Ui_SubscriptionWidget(Ui_AddressWidget, AccountMixin): + def __init__(self, parent, pos = 0, address = "", unreadCount = 0, label = "", enabled = True): super(QtGui.QTreeWidgetItem, self).__init__() - self.unreadCount = unreadCount parent.insertTopLevelItem(pos, self) # only set default when creating #super(QtGui.QTreeWidgetItem, self).setExpanded(shared.config.getboolean(self.address, 'enabled')) - self.setEnabled(enabled) - self.setLabel(label) + self.initialised = False self.setAddress(address) + self.setEnabled(enabled) + self.setType() + self.setLabel(label) + self.setUnreadCount (unreadCount) + self.initialised = True + self.setExpanded(enabled) # does updateText def setLabel(self, label): self.label = label - def setAddress(self, address): - self.address = str(address) - self.setType() - self.setExpanded(self.isEnabled) - self.updateText() - - def setEnabled(self, enabled): - self.isEnabled = enabled - def setType(self): self.type = "subscription" - def setUnreadCount(self, cnt): - self.unreadCount = int(cnt) - self.updateText() - def updateText(self): + if not self.initialised: + return text = unicode(self.label, 'utf-8)') + ' (' + self.address + ')' font = QtGui.QFont() @@ -197,12 +200,7 @@ class Ui_SubscriptionWidget(Ui_AddressWidget): self.setFont(0, font) #set text color - if self.isEnabled: - brush = QtGui.QBrush(QtGui.QColor(137, 04, 177)) - #self.setExpanded(True) - else: - brush = QtGui.QBrush(QtGui.QColor(128, 128, 128)) - #self.setExpanded(False) + brush = self.accountColor() brush.setStyle(QtCore.Qt.NoBrush) self.setForeground(0, brush) @@ -211,14 +209,6 @@ class Ui_SubscriptionWidget(Ui_AddressWidget): self.setToolTip(0, text) # self.setData(0, QtCore.Qt.UserRole, [self.address, "inbox"]) - def setExpanded(self, expand): - super(Ui_SubscriptionWidget, self).setExpanded(expand) - self.updateText() - - def edit(self): - self.setText(0, self.label) - super(QtGui.QAbstractItemView, self).edit() - # label (or address) alphabetically, disabled at the end def __lt__(self, other): if (isinstance(other, Ui_SubscriptionWidget)): @@ -240,22 +230,19 @@ class Ui_SubscriptionWidget(Ui_AddressWidget): return super(QtGui.QTreeWidgetItem, self).__lt__(other) -class Ui_AddressBookWidgetItem(QtGui.QTableWidgetItem): +class Ui_AddressBookWidgetItem(QtGui.QTableWidgetItem, AccountMixin): _types = {'normal': 0, 'chan': 1, 'subscription': 2} def __init__ (self, text, type = 'normal'): super(QtGui.QTableWidgetItem, self).__init__(text) self.label = text + self.type = type try: - self.type = self._types[type] + self.typeNum = self._types[self.type] except: self.type = 0 - if self.type == 2: - brush = QtGui.QBrush(QtGui.QColor(137, 04, 177)) - elif self.type == 1: - brush = QtGui.QBrush(QtGui.QColor(216, 119, 0)) - else: - return + self.setEnabled(True) + brush = self.accountColor() brush.setStyle(QtCore.Qt.NoBrush) self.setForeground(brush) @@ -264,10 +251,10 @@ class Ui_AddressBookWidgetItem(QtGui.QTableWidgetItem): reverse = False if self.tableWidget().horizontalHeader().sortIndicatorOrder() == QtCore.Qt.DescendingOrder: reverse = True - if self.type == other.type: + if self.typeNum == other.typeNum: return self.label.decode('utf-8').lower() < other.label.decode('utf-8').lower() else: - return (not reverse if self.type < other.type else reverse) + return (not reverse if self.typeNum < other.typeNum else reverse) return super(QtGui.QTableWidgetItem, self).__lt__(other) From dd2f83ff4dc7e0bab85841487de1727cae287275 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Tue, 27 Oct 2015 19:50:52 +0100 Subject: [PATCH 073/210] Make subscription/chan labels editable Fixes #67 --- src/bitmessageqt/__init__.py | 28 +++++++++++++++++++++------- src/bitmessageqt/foldertree.py | 4 ++++ 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 6668e6a6..73c4c687 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -444,6 +444,8 @@ class MyForm(QtGui.QMainWindow): newSubItem = Ui_FolderWidget(newItem, 0, address, folder, 0) newItem.setUnreadCount(unread) + newItem.setFlags (newItem.flags() | QtCore.Qt.ItemIsEditable) + treeWidget.setSortingEnabled(True) def rerenderTabTreeMessages(self): @@ -537,8 +539,7 @@ class MyForm(QtGui.QMainWindow): unread += db[toAddress][folder] j += 1 widget.setUnreadCount(unread) - if (tab == 'messages'): - widget.setFlags (widget.flags() | QtCore.Qt.ItemIsEditable) + widget.setFlags (widget.flags() | QtCore.Qt.ItemIsEditable) i += 1 treeWidget.setSortingEnabled(True) @@ -658,8 +659,12 @@ class MyForm(QtGui.QMainWindow): "itemChanged (QTreeWidgetItem *, int)"), self.treeWidgetItemChanged) QtCore.QObject.connect(self.ui.treeWidgetSubscriptions, QtCore.SIGNAL( "itemSelectionChanged ()"), self.treeWidgetItemClicked) + QtCore.QObject.connect(self.ui.treeWidgetSubscriptions, QtCore.SIGNAL( + "itemChanged (QTreeWidgetItem *, int)"), self.treeWidgetItemChanged) QtCore.QObject.connect(self.ui.treeWidgetChans, QtCore.SIGNAL( "itemSelectionChanged ()"), self.treeWidgetItemClicked) + QtCore.QObject.connect(self.ui.treeWidgetChans, QtCore.SIGNAL( + "itemChanged (QTreeWidgetItem *, int)"), self.treeWidgetItemChanged) # Put the colored icon on the status bar # self.ui.pushButtonStatusIcon.setIcon(QIcon(":/newPrefix/images/yellowicon.png")) @@ -3613,7 +3618,7 @@ class MyForm(QtGui.QMainWindow): if column != 0: return # only account names of normal addresses (no chans/mailinglists) - if (not isinstance(item, Ui_AddressWidget)) or item.type != 'normal' or not self.getCurrentTreeWidget() or self.getCurrentTreeWidget().currentItem() is None: + if (not isinstance(item, Ui_AddressWidget)) or (not self.getCurrentTreeWidget()) or self.getCurrentTreeWidget().currentItem() is None: return # not visible if (not self.getCurrentItem()) or (not isinstance (self.getCurrentItem(), Ui_AddressWidget)): @@ -3625,7 +3630,10 @@ class MyForm(QtGui.QMainWindow): newLabel = str(item.text(0)) newLabel = newLabel.replace("(" + str(item.address) + ")", '') newLabel = newLabel.rstrip() - oldLabel = shared.config.get(str(item.address), 'label') + if item.type == "subscription": + oldLabel = item.label + else: + oldLabel = shared.config.get(str(item.address), 'label') oldLabel = oldLabel.replace("(" + str(item.address) + ")", '') oldLabel = oldLabel.rstrip() # unchanged, do not do anything either @@ -3637,12 +3645,18 @@ class MyForm(QtGui.QMainWindow): return self.recurDepth += 1 - shared.config.set(str(item.address), 'label', newLabel) + if item.type == "subscription": + sqlExecute( + '''UPDATE subscriptions SET label=? WHERE address=?''', + newLabel, item.address) + item.setLabel(newLabel) + else: + shared.config.set(str(item.address), 'label', newLabel) + shared.writeKeysFile() item.updateText() - shared.writeKeysFile() if item.type == 'mailinglist': self.rerenderComboBoxSendFromBroadcast() - else: + elif item.type != "subscription": self.rerenderComboBoxSendFrom() self.recurDepth -= 1 diff --git a/src/bitmessageqt/foldertree.py b/src/bitmessageqt/foldertree.py index fd4bb9e8..cb8b2c81 100644 --- a/src/bitmessageqt/foldertree.py +++ b/src/bitmessageqt/foldertree.py @@ -183,6 +183,10 @@ class Ui_SubscriptionWidget(Ui_AddressWidget, AccountMixin): def setType(self): self.type = "subscription" + + def edit(self): + self.setText(0, self.label) + super(QtGui.QAbstractItemView, self).edit() def updateText(self): if not self.initialised: From 9d78ca5059a7aa548afec14e31660c41c21470b1 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Tue, 27 Oct 2015 20:03:46 +0100 Subject: [PATCH 074/210] Show tooltips on addressbook Fixes #68 --- src/bitmessageqt/foldertree.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bitmessageqt/foldertree.py b/src/bitmessageqt/foldertree.py index cb8b2c81..f49b1d6d 100644 --- a/src/bitmessageqt/foldertree.py +++ b/src/bitmessageqt/foldertree.py @@ -265,12 +265,12 @@ class Ui_AddressBookWidgetItem(QtGui.QTableWidgetItem, AccountMixin): class Ui_AddressBookWidgetItemLabel(Ui_AddressBookWidgetItem): def __init__ (self, address, label, type): Ui_AddressBookWidgetItem.__init__(self, label, type) - self.label = label self.setIcon(avatarize(address)) + self.setToolTip(label + " (" + address + ")") class Ui_AddressBookWidgetItemAddress(Ui_AddressBookWidgetItem): def __init__ (self, address, label, type): Ui_AddressBookWidgetItem.__init__(self, address, type) - self.label = label - self.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) \ No newline at end of file + self.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) + self.setToolTip(label + " (" + address + ")") \ No newline at end of file From f356d903978b6cf54a1b46824b10acd4a2acc391 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sat, 31 Oct 2015 10:12:12 +0100 Subject: [PATCH 075/210] More unified colors in foldertree and messagelists Some parts still not colored in a unified way. Fixes #84 --- src/bitmessageqt/__init__.py | 10 ++++------ src/bitmessageqt/account.py | 19 +++++++++++++++++++ src/bitmessageqt/foldertree.py | 17 ++++++++++------- 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 73c4c687..1fc784a1 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -921,6 +921,7 @@ class MyForm(QtGui.QMainWindow): toAddressItem.setToolTip(unicode(acct.toLabel, 'utf-8')) toAddressItem.setIcon(avatarize(toAddress)) toAddressItem.setData(Qt.UserRole, str(toAddress)) + toAddressItem.setTextColor(AccountColor(toAddress).accountColor()) toAddressItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) tableWidget.setItem(0, 0, toAddressItem) @@ -929,6 +930,7 @@ class MyForm(QtGui.QMainWindow): fromAddressItem.setToolTip(unicode(acct.fromLabel, 'utf-8')) fromAddressItem.setIcon(avatarize(fromAddress)) fromAddressItem.setData(Qt.UserRole, str(fromAddress)) + fromAddressItem.setTextColor(AccountColor(fromAddress).accountColor()) fromAddressItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) tableWidget.setItem(0, 1, fromAddressItem) @@ -1058,10 +1060,7 @@ class MyForm(QtGui.QMainWindow): if not read: to_item.setFont(font) to_item.setData(Qt.UserRole, str(toAddress)) - if shared.safeConfigGetBoolean(toAddress, 'mailinglist'): - to_item.setTextColor(QtGui.QColor(137, 04, 177)) # magenta - if shared.safeConfigGetBoolean(str(toAddress), 'chan'): - to_item.setTextColor(QtGui.QColor(216, 119, 0)) # orange + to_item.setTextColor(AccountColor(toAddress).accountColor()) to_item.setIcon(avatarize(toAddress)) tableWidget.setItem(0, 0, to_item) # from @@ -1072,8 +1071,7 @@ class MyForm(QtGui.QMainWindow): if not read: from_item.setFont(font) from_item.setData(Qt.UserRole, str(fromAddress)) - if shared.safeConfigGetBoolean(str(fromAddress), 'chan'): - from_item.setTextColor(QtGui.QColor(216, 119, 0)) # orange + from_item.setTextColor(AccountColor(fromAddress).accountColor()) from_item.setIcon(avatarize(fromAddress)) tableWidget.setItem(0, 1, from_item) # subject diff --git a/src/bitmessageqt/account.py b/src/bitmessageqt/account.py index 2342bcc5..6e1083f6 100644 --- a/src/bitmessageqt/account.py +++ b/src/bitmessageqt/account.py @@ -6,6 +6,7 @@ import sys import inspect from helper_sql import * from addresses import decodeAddress +from foldertree import AccountMixin from pyelliptic.openssl import OpenSSL from utils import str_broadcast_subscribers import time @@ -41,6 +42,24 @@ def accountClass(address): # no gateway return BMAccount(address) +class AccountColor(AccountMixin): + def __init__(self, address, type = None): + self.isEnabled = True + self.address = address + if type is None: + if shared.safeConfigGetBoolean(self.address, 'mailinglist'): + self.type = "mailinglist" + elif shared.safeConfigGetBoolean(self.address, 'chan'): + self.type = "chan" + elif sqlQuery( + '''select label from subscriptions where address=?''', self.address): + self.type = 'subscription' + else: + self.type = "normal" + else: + self.type = type + + class BMAccount(object): def __init__(self, address = None): self.address = address diff --git a/src/bitmessageqt/foldertree.py b/src/bitmessageqt/foldertree.py index f49b1d6d..e0ce72b8 100644 --- a/src/bitmessageqt/foldertree.py +++ b/src/bitmessageqt/foldertree.py @@ -6,13 +6,16 @@ import shared class AccountMixin (object): def accountColor (self): if not self.isEnabled: - return QtGui.QBrush(QtGui.QColor(128, 128, 128)) + return QtGui.QColor(128, 128, 128) elif self.type == "chan": - return QtGui.QBrush(QtGui.QColor(216, 119, 0)) + return QtGui.QColor(216, 119, 0) elif self.type == "mailinglist" or self.type == "subscription": - return QtGui.QBrush(QtGui.QColor(137, 04, 177)) + return QtGui.QColor(137, 04, 177) else: - return QtGui.QBrush(QtGui.QApplication.palette().text().color()) + return QtGui.QApplication.palette().text().color() + + def accountBrush(self): + return QtGui.QBrush(self.accountColor()) def setAddress(self, address): self.address = str(address) @@ -121,7 +124,7 @@ class Ui_AddressWidget(QtGui.QTreeWidgetItem, AccountMixin): self.setFont(0, font) #set text color - brush = self.accountColor() + brush = self.accountBrush() brush.setStyle(QtCore.Qt.NoBrush) self.setForeground(0, brush) @@ -204,7 +207,7 @@ class Ui_SubscriptionWidget(Ui_AddressWidget, AccountMixin): self.setFont(0, font) #set text color - brush = self.accountColor() + brush = self.accountBrush() brush.setStyle(QtCore.Qt.NoBrush) self.setForeground(0, brush) @@ -246,7 +249,7 @@ class Ui_AddressBookWidgetItem(QtGui.QTableWidgetItem, AccountMixin): except: self.type = 0 self.setEnabled(True) - brush = self.accountColor() + brush = self.accountBrush() brush.setStyle(QtCore.Qt.NoBrush) self.setForeground(brush) From eb98192a0cdccb160a833c550d61eca142f46922 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sat, 31 Oct 2015 15:27:07 +0100 Subject: [PATCH 076/210] Update unread count more efficiently Fixes #63. There are still some situations which can be improved but it appears good enough. --- src/bitmessageqt/__init__.py | 84 ++++++++++++++++++++++++++-------- src/bitmessageqt/foldertree.py | 7 ++- src/helper_inbox.py | 3 +- 3 files changed, 73 insertions(+), 21 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 1fc784a1..8f824d36 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -594,7 +594,7 @@ class MyForm(QtGui.QMainWindow): # switch back to this when replying self.replyFromTab = None - + self.init_file_menu() self.init_inbox_popup_menu() self.init_identities_popup_menu() @@ -675,6 +675,7 @@ class MyForm(QtGui.QMainWindow): self.numberOfMessagesProcessed = 0 self.numberOfBroadcastsProcessed = 0 self.numberOfPubkeysProcessed = 0 + self.unreadCount = 0 # Set the icon sizes for the identicons identicon_size = 3*7 @@ -873,6 +874,49 @@ class MyForm(QtGui.QMainWindow): def appIndicatorChannel(self): self.appIndicatorShow() self.ui.tabWidget.setCurrentIndex(3) + + def propagateUnreadCount(self, address = None, folder = "inbox", widget = None, type = 1): + def updateUnreadCount(item, type = 1): + if type == 1: + item.setUnreadCount(item.unreadCount + 1) + if isinstance(item, Ui_AddressWidget): + self.drawTrayIcon(self.currentTrayIconFileName, self.findInboxUnreadCount(self.unreadCount + 1)) + elif type == -1: + item.setUnreadCount(item.unreadCount - 1) + if isinstance(item, Ui_AddressWidget): + self.drawTrayIcon(self.currentTrayIconFileName, self.findInboxUnreadCount(self.unreadCount -1)) + else: + if address and folder: + queryreturn = sqlQuery("SELECT COUNT(*) FROM inbox WHERE toaddress = ? AND folder = ? AND read = 0", address, folder) + elif address: + queryreturn = sqlQuery("SELECT COUNT(*) FROM inbox WHERE toaddress = ? AND read = 0", address) + elif folder: + queryreturn = sqlQuery("SELECT COUNT(*) FROM inbox WHERE folder = ? AND read = 0", folder) + else: + queryreturn = sqlQuery("SELECT COUNT(*) FROM inbox WHERE read = 0") + for row in queryreturn: + item.setUnreadCount(int(row[0])) + if isinstance(item, Ui_AddressWidget): + self.drawTrayIcon(self.currentTrayIconFileName, self.findInboxUnreadCount()) + + if widget == None: + widgets = [self.ui.treeWidgetYourIdentities, self.ui.treeWidgetSubscriptions, self.ui.treeWidgetChans] + else: + widgets = [widget] + for treeWidget in widgets: + root = treeWidget.invisibleRootItem() + for i in range(root.childCount()): + addressItem = root.child(i) + if address is not None and addressItem.data(0, QtCore.Qt.UserRole) != address: + continue + updateUnreadCount(addressItem, type) + if addressItem.childCount == 0: + continue + for j in range(addressItem.childCount()): + folderItem = addressItem.child(j) + if folder is not None and folderItem.data(0, QtCore.Qt.UserRole) != folder: + continue + updateUnreadCount(folderItem, type) # Load Sent items from database def loadSent(self, tableWidget, account, where="", what=""): @@ -1765,19 +1809,19 @@ class MyForm(QtGui.QMainWindow): def changedInboxUnread(self, row = None): self.drawTrayIcon(self.currentTrayIconFileName, self.findInboxUnreadCount()) self.rerenderTabTreeMessages() -# if not row is None: -# row[1], row[6] - if self.ui.tabWidget.currentIndex() == 2: - self.rerenderTabTreeSubscriptions() - elif self.ui.tabWidget.currentIndex() == 3: - self.rerenderTabTreeChans() + self.rerenderTabTreeSubscriptions() + self.rerenderTabTreeChans() - def findInboxUnreadCount(self): - queryreturn = sqlQuery('''SELECT count(*) from inbox WHERE folder='inbox' and read=0''') - cnt = 0 - for row in queryreturn: - cnt, = row - return int(cnt) + def findInboxUnreadCount(self, count = None): + if count is None: + queryreturn = sqlQuery('''SELECT count(*) from inbox WHERE folder='inbox' and read=0''') + cnt = 0 + for row in queryreturn: + cnt, = row + self.unreadCount = int(cnt) + else: + self.unreadCount = count + return self.unreadCount def updateSentItemStatusByToAddress(self, toAddress, textToDisplay): for i in range(self.ui.tableWidgetSent.rowCount()): @@ -1816,14 +1860,15 @@ class MyForm(QtGui.QMainWindow): self.ui.tableWidgetInbox.item(i, 3).setText(textToDisplay) def removeInboxRowByMsgid(self, msgid): # msgid and inventoryHash are the same thing - inbox = self.getCurrentMessagelist + inbox = self.getCurrentMessagelist() for i in range(inbox.rowCount()): if msgid == str(inbox.item(i, 3).data(Qt.UserRole).toPyObject()): self.statusBar().showMessage(_translate( "MainWindow", "Message trashed")) inbox.removeRow(i) break - self.changedInboxUnread() + # this is a callback from core, not initiated by UI. We don't care about performance + self.propagateUnreadCount(None, None, None, 0) def newVersionAvailable(self, version): # if (not (self.windowState() & QtCore.Qt.WindowActive)) or (self.windowState() & QtCore.Qt.WindowMinimized): @@ -2310,6 +2355,8 @@ class MyForm(QtGui.QMainWindow): acct.parseMessage(toAddress, fromAddress, subject, message) inbox = self.getAccountMessagelist(acct) treeWidget = self.getAccountTreeWidget(acct) + self.propagateUnreadCount(toAddress) + self.ubuntuMessagingMenuUpdate(True, newItem, acct.toLabel) if (self.getCurrentFolder(treeWidget) != "inbox" and self.getCurrentFolder(treeWidget) != False) or self.getCurrentAccount(treeWidget) != toAddress: return @@ -2350,7 +2397,6 @@ class MyForm(QtGui.QMainWindow): newItem.setFont(font) inbox.setItem(0, 3, newItem) inbox.setSortingEnabled(True) - self.ubuntuMessagingMenuUpdate(True, newItem, acct.toLabel) def click_pushButtonAddAddressBook(self): self.AddAddressDialogInstance = AddAddressDialog(self) @@ -2885,7 +2931,7 @@ class MyForm(QtGui.QMainWindow): #sqlite requires the exact number of ?s to prevent injection sqlExecute('''UPDATE inbox SET read=0 WHERE msgid IN (%s)''' % ( "?," * len(inventoryHashesToMarkUnread))[:-1], *inventoryHashesToMarkUnread) - self.changedInboxUnread() + self.propagateUnreadCount(self.getCurrentAccount(), "inbox", self.getCurrentTreeWidget(), 0) # tableWidget.selectRow(currentRow + 1) # This doesn't de-select the last message if you try to mark it unread, but that doesn't interfere. Might not be necessary. # We could also select upwards, but then our problem would be with the topmost message. @@ -3038,7 +3084,7 @@ class MyForm(QtGui.QMainWindow): else: tableWidget.selectRow(currentRow - 1) if unread: - self.changedInboxUnread() + self.propagateUnreadCount(self.getCurrentAccount(), self.getCurrentFolder(), self.getCurrentTreeWidget(), 0) def on_action_InboxSaveMessageAs(self): tableWidget = self.getCurrentMessagelist() @@ -3698,7 +3744,7 @@ class MyForm(QtGui.QMainWindow): tableWidget.item(currentRow, 1).setFont(font) tableWidget.item(currentRow, 2).setFont(font) tableWidget.item(currentRow, 3).setFont(font) - self.changedInboxUnread() + self.propagateUnreadCount(self.getCurrentAccount(), folder, self.getCurrentTreeWidget(), -1) else: data = self.getCurrentMessageId() diff --git a/src/bitmessageqt/foldertree.py b/src/bitmessageqt/foldertree.py index e0ce72b8..30fdf82a 100644 --- a/src/bitmessageqt/foldertree.py +++ b/src/bitmessageqt/foldertree.py @@ -40,7 +40,7 @@ class AccountMixin (object): def updateText(self): pass - + class Ui_FolderWidget(QtGui.QTreeWidgetItem, AccountMixin): folderWeight = {"inbox": 1, "sent": 2, "trash": 3} def __init__(self, parent, pos = 0, address = "", folderName = "", unreadCount = 0): @@ -55,6 +55,7 @@ class Ui_FolderWidget(QtGui.QTreeWidgetItem, AccountMixin): def setFolderName(self, fname): self.folderName = str(fname) + self.setData(0, QtCore.Qt.UserRole, self.folderName) self.updateText() def updateText(self): @@ -107,6 +108,10 @@ class Ui_AddressWidget(QtGui.QTreeWidgetItem, AccountMixin): self.setType() self.initialised = True self.setExpanded(enabled) # does updateText + + def setAddress(self, address): + super(Ui_AddressWidget, self).setAddress(address) + self.setData(0, QtCore.Qt.UserRole, self.address) def updateText(self): if not self.initialised: diff --git a/src/helper_inbox.py b/src/helper_inbox.py index 09c7edbc..a3ad9755 100644 --- a/src/helper_inbox.py +++ b/src/helper_inbox.py @@ -3,7 +3,8 @@ import shared def insert(t): sqlExecute('''INSERT INTO inbox VALUES (?,?,?,?,?,?,?,?,?,?)''', *t) - shared.UISignalQueue.put(('changedInboxUnread', None)) + #shouldn't emit changedInboxUnread and displayNewInboxMessage at the same time + #shared.UISignalQueue.put(('changedInboxUnread', None)) def trash(msgid): sqlExecute('''UPDATE inbox SET folder='trash' WHERE msgid=?''', msgid) From b04cf2da7e3a9c42f5d8028c71ec64228b8ff796 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sat, 31 Oct 2015 18:42:44 +0100 Subject: [PATCH 077/210] removeInboxRowByMsgid fixes Now undertands messagelists and has better performance. Haven't tested it yet though because it's triggered through API. I think it fixes #85 --- src/bitmessageqt/__init__.py | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 8f824d36..6056416a 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -1860,15 +1860,28 @@ class MyForm(QtGui.QMainWindow): self.ui.tableWidgetInbox.item(i, 3).setText(textToDisplay) def removeInboxRowByMsgid(self, msgid): # msgid and inventoryHash are the same thing - inbox = self.getCurrentMessagelist() - for i in range(inbox.rowCount()): - if msgid == str(inbox.item(i, 3).data(Qt.UserRole).toPyObject()): - self.statusBar().showMessage(_translate( - "MainWindow", "Message trashed")) - inbox.removeRow(i) - break - # this is a callback from core, not initiated by UI. We don't care about performance - self.propagateUnreadCount(None, None, None, 0) + def widgetConvert (tableWidget): + if tableWidget == self.ui.tableWidgetInbox: + return self.ui.treeWidgetYourIdentities + elif tableWidget == self.ui.tableWidgetInboxSubscriptions: + return self.ui.treeWidgetSubscriptions + elif tableWidget == self.ui.tableWidgetInboxChans: + return self.ui.treeWidgetChans + else: + return None + + for inbox in ([ + self.ui.tableWidgetInbox, + self.ui.tableWidgetInboxSubscriptions, + self.ui.tableWidgetInboxChans]): + for i in range(inbox.rowCount()): + if msgid == str(inbox.item(i, 3).data(Qt.UserRole).toPyObject()): + self.statusBar().showMessage(_translate( + "MainWindow", "Message trashed")) + treeWidget = widgetConvert(inbox) + self.propagateUnreadCount(self.getCurrentAccount(treeWidget), self.getCurrentFolder(treeWidget), treeWidget, 0) + inbox.removeRow(i) + break def newVersionAvailable(self, version): # if (not (self.windowState() & QtCore.Qt.WindowActive)) or (self.windowState() & QtCore.Qt.WindowMinimized): From a3677e0328095c37ca20f8029b2b40ffb38a59c9 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sat, 31 Oct 2015 18:48:33 +0100 Subject: [PATCH 078/210] Indicate message sending Fixes #83 --- src/bitmessageqt/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 6056416a..d4fb4b45 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2212,6 +2212,8 @@ class MyForm(QtGui.QMainWindow): if self.replyFromTab is not None: self.ui.tabWidget.setCurrentIndex(self.replyFromTab) self.replyFromTab = None + self.statusBar().showMessage(_translate( + "MainWindow", "Message queued.")) #self.ui.tableWidgetInbox.setCurrentCell(0, 0) else: self.statusBar().showMessage(_translate( @@ -2259,6 +2261,8 @@ class MyForm(QtGui.QMainWindow): self.ui.textEditMessageBroadcast.setText('') self.ui.tabWidget.setCurrentIndex(1) self.ui.tableWidgetInboxSubscriptions.setCurrentCell(0, 0) + self.statusBar().showMessage(_translate( + "MainWindow", "Broadcast queued.")) def click_pushButtonLoadFromAddressBook(self): self.ui.tabWidget.setCurrentIndex(5) From 3a4b60bb2583ec3f28cd00610ab550889ea33a41 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sat, 31 Oct 2015 19:03:10 +0100 Subject: [PATCH 079/210] Ubuntu notification fixes Notification should work irrespective of whether the new message shows up in the messagelist. --- src/bitmessageqt/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index d4fb4b45..9d504e7d 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2373,8 +2373,9 @@ class MyForm(QtGui.QMainWindow): inbox = self.getAccountMessagelist(acct) treeWidget = self.getAccountTreeWidget(acct) self.propagateUnreadCount(toAddress) - self.ubuntuMessagingMenuUpdate(True, newItem, acct.toLabel) if (self.getCurrentFolder(treeWidget) != "inbox" and self.getCurrentFolder(treeWidget) != False) or self.getCurrentAccount(treeWidget) != toAddress: + # Ubuntu should notify of new message irespective of whether it's in current message list or not + self.ubuntuMessagingMenuUpdate(True, None, acct.toLabel) return font = QFont() @@ -2414,6 +2415,7 @@ class MyForm(QtGui.QMainWindow): newItem.setFont(font) inbox.setItem(0, 3, newItem) inbox.setSortingEnabled(True) + self.ubuntuMessagingMenuUpdate(True, newItem, acct.toLabel) def click_pushButtonAddAddressBook(self): self.AddAddressDialogInstance = AddAddressDialog(self) From 37b79bc446251d96f435fcd33fca823b3fceba1c Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sat, 31 Oct 2015 20:04:52 +0100 Subject: [PATCH 080/210] Unread count performance optimisation Continuation of #63 --- src/bitmessageqt/__init__.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 9d504e7d..87e40a9a 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2938,10 +2938,16 @@ class MyForm(QtGui.QMainWindow): font = QFont() font.setBold(True) inventoryHashesToMarkUnread = [] + modified = 0 for row in tableWidget.selectedIndexes(): currentRow = row.row() inventoryHashToMarkUnread = str(tableWidget.item( currentRow, 3).data(Qt.UserRole).toPyObject()) + if inventoryHashToMarkUnread in inventoryHashesToMarkUnread: + # it returns columns as separate items, so we skip dupes + continue + if not tableWidget.item(currentRow, 0).font().bold(): + modified += 1 inventoryHashesToMarkUnread.append(inventoryHashToMarkUnread) tableWidget.item(currentRow, 0).setFont(font) tableWidget.item(currentRow, 1).setFont(font) @@ -2950,7 +2956,11 @@ class MyForm(QtGui.QMainWindow): #sqlite requires the exact number of ?s to prevent injection sqlExecute('''UPDATE inbox SET read=0 WHERE msgid IN (%s)''' % ( "?," * len(inventoryHashesToMarkUnread))[:-1], *inventoryHashesToMarkUnread) - self.propagateUnreadCount(self.getCurrentAccount(), "inbox", self.getCurrentTreeWidget(), 0) + if modified == 1: + # performance optimisation + self.propagateUnreadCount(self.getCurrentAccount()) + else: + self.propagateUnreadCount(self.getCurrentAccount(), "inbox", self.getCurrentTreeWidget(), 0) # tableWidget.selectRow(currentRow + 1) # This doesn't de-select the last message if you try to mark it unread, but that doesn't interfere. Might not be necessary. # We could also select upwards, but then our problem would be with the topmost message. From 98372bd6ba29372f1288c9c8fe44991f6587fd1f Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sat, 31 Oct 2015 20:05:58 +0100 Subject: [PATCH 081/210] Subscription new messages display correctly Fixes #70 --- src/bitmessageqt/__init__.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 87e40a9a..cc3bf198 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -876,7 +876,7 @@ class MyForm(QtGui.QMainWindow): self.ui.tabWidget.setCurrentIndex(3) def propagateUnreadCount(self, address = None, folder = "inbox", widget = None, type = 1): - def updateUnreadCount(item, type = 1): + def updateUnreadCount(item): if type == 1: item.setUnreadCount(item.unreadCount + 1) if isinstance(item, Ui_AddressWidget): @@ -886,10 +886,14 @@ class MyForm(QtGui.QMainWindow): if isinstance(item, Ui_AddressWidget): self.drawTrayIcon(self.currentTrayIconFileName, self.findInboxUnreadCount(self.unreadCount -1)) else: + if addressItem.type == 'subscription' or addressItem.type == 'mailinglist': + xAddress = "fromaddress" + else: + xAddress = "toaddress" if address and folder: - queryreturn = sqlQuery("SELECT COUNT(*) FROM inbox WHERE toaddress = ? AND folder = ? AND read = 0", address, folder) + queryreturn = sqlQuery("SELECT COUNT(*) FROM inbox WHERE " + xAddress + " = ? AND folder = ? AND read = 0", address, folder) elif address: - queryreturn = sqlQuery("SELECT COUNT(*) FROM inbox WHERE toaddress = ? AND read = 0", address) + queryreturn = sqlQuery("SELECT COUNT(*) FROM inbox WHERE " + xAddress + " = ? AND read = 0", address) elif folder: queryreturn = sqlQuery("SELECT COUNT(*) FROM inbox WHERE folder = ? AND read = 0", folder) else: @@ -909,14 +913,14 @@ class MyForm(QtGui.QMainWindow): addressItem = root.child(i) if address is not None and addressItem.data(0, QtCore.Qt.UserRole) != address: continue - updateUnreadCount(addressItem, type) + updateUnreadCount(addressItem) if addressItem.childCount == 0: continue for j in range(addressItem.childCount()): folderItem = addressItem.child(j) if folder is not None and folderItem.data(0, QtCore.Qt.UserRole) != folder: continue - updateUnreadCount(folderItem, type) + updateUnreadCount(folderItem) # Load Sent items from database def loadSent(self, tableWidget, account, where="", what=""): @@ -2368,12 +2372,15 @@ class MyForm(QtGui.QMainWindow): def displayNewInboxMessage(self, inventoryHash, toAddress, fromAddress, subject, message): subject = shared.fixPotentiallyInvalidUTF8Data(subject) - acct = accountClass(toAddress) + if toAddress == str_broadcast_subscribers: + acct = accountClass(fromAddress) + else: + acct = accountClass(toAddress) acct.parseMessage(toAddress, fromAddress, subject, message) inbox = self.getAccountMessagelist(acct) treeWidget = self.getAccountTreeWidget(acct) - self.propagateUnreadCount(toAddress) - if (self.getCurrentFolder(treeWidget) != "inbox" and self.getCurrentFolder(treeWidget) != False) or self.getCurrentAccount(treeWidget) != toAddress: + self.propagateUnreadCount(acct.address) + if (self.getCurrentFolder(treeWidget) != "inbox" and self.getCurrentFolder(treeWidget) != False) or self.getCurrentAccount(treeWidget) != acct.address: # Ubuntu should notify of new message irespective of whether it's in current message list or not self.ubuntuMessagingMenuUpdate(True, None, acct.toLabel) return From eee26605e19f15984eff96183a3936f85fb1333e Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sat, 31 Oct 2015 20:27:28 +0100 Subject: [PATCH 082/210] Colors for From ComboBox Fixes #86 --- src/bitmessageqt/__init__.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index cc3bf198..6fb06012 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2306,6 +2306,9 @@ class MyForm(QtGui.QMainWindow): self.ui.comboBoxSendFrom.addItem(avatarize(addressInKeysFile), unicode(shared.config.get( addressInKeysFile, 'label'), 'utf-8'), addressInKeysFile) # self.ui.comboBoxSendFrom.model().sort(1, Qt.AscendingOrder) + for i in range(self.ui.comboBoxSendFrom.count()): + address = str(self.ui.comboBoxSendFrom.itemData(i, Qt.UserRole).toString()) + self.ui.comboBoxSendFrom.setItemData(i, AccountColor(address).accountColor(), Qt.ForegroundRole) self.ui.comboBoxSendFrom.insertItem(0, '', '') if(self.ui.comboBoxSendFrom.count() == 2): self.ui.comboBoxSendFrom.setCurrentIndex(1) @@ -2321,6 +2324,9 @@ class MyForm(QtGui.QMainWindow): if isEnabled and isMaillinglist: self.ui.comboBoxSendFromBroadcast.addItem(avatarize(addressInKeysFile), unicode(shared.config.get( addressInKeysFile, 'label'), 'utf-8'), addressInKeysFile) + for i in range(self.ui.comboBoxSendFromBroadcast.count()): + address = str(self.ui.comboBoxSendFromBroadcast.itemData(i, Qt.UserRole).toString()) + self.ui.comboBoxSendFromBroadcast.setItemData(i, AccountColor(address).accountColor(), Qt.ForegroundRole) self.ui.comboBoxSendFromBroadcast.insertItem(0, '', '') if(self.ui.comboBoxSendFromBroadcast.count() == 2): self.ui.comboBoxSendFromBroadcast.setCurrentIndex(1) From d42d77c56c7b93d36500854c47f4af875f9600fd Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sat, 31 Oct 2015 21:38:54 +0100 Subject: [PATCH 083/210] Address book better context menu Don't allow delete of chans/subscriptions. Fixes #82 --- src/bitmessageqt/__init__.py | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 6fb06012..a4292839 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -310,14 +310,6 @@ class MyForm(QtGui.QMainWindow): self.connect(self.ui.tableWidgetAddressBook, QtCore.SIGNAL( 'customContextMenuRequested(const QPoint&)'), self.on_context_menuAddressBook) - self.popMenuAddressBook = QtGui.QMenu(self) - self.popMenuAddressBook.addAction(self.actionAddressBookSend) - self.popMenuAddressBook.addAction(self.actionAddressBookClipboard) - self.popMenuAddressBook.addAction(self.actionAddressBookSubscribe) - self.popMenuAddressBook.addAction(self.actionAddressBookSetAvatar) - self.popMenuAddressBook.addSeparator() - self.popMenuAddressBook.addAction(self.actionAddressBookNew) - self.popMenuAddressBook.addAction(self.actionAddressBookDelete) def init_subscriptions_popup_menu(self, connectSignal=True): # Popup menu for the Subscriptions page @@ -2018,6 +2010,7 @@ class MyForm(QtGui.QMainWindow): def addRow (address, label, type): self.ui.tableWidgetAddressBook.insertRow(0) newItem = Ui_AddressBookWidgetItemLabel(address, unicode(label, 'utf-8'), type) + newItem.setData(Qt.UserRole, type) self.ui.tableWidgetAddressBook.setItem(0, 0, newItem) newItem = Ui_AddressBookWidgetItemAddress(address, unicode(label, 'utf-8'), type) self.ui.tableWidgetAddressBook.setItem(0, 1, newItem) @@ -3266,6 +3259,26 @@ class MyForm(QtGui.QMainWindow): self.ui.tabWidget.setCurrentIndex(4) def on_context_menuAddressBook(self, point): + if hasattr(self, "popMenuAddressBook"): + self.popMenuAddressBook.clear() + else: + self.popMenuAddressBook = QtGui.QMenu(self) + self.popMenuAddressBook.addAction(self.actionAddressBookSend) + self.popMenuAddressBook.addAction(self.actionAddressBookClipboard) + self.popMenuAddressBook.addAction(self.actionAddressBookSubscribe) + self.popMenuAddressBook.addAction(self.actionAddressBookSetAvatar) + self.popMenuAddressBook.addSeparator() + self.popMenuAddressBook.addAction(self.actionAddressBookNew) + normal = True + for row in self.ui.tableWidgetAddressBook.selectedIndexes(): + currentRow = row.row() + type = str(self.ui.tableWidgetAddressBook.item( + currentRow, 0).data(Qt.UserRole).toPyObject()) + if type != "normal": + normal = False + if normal: + # only if all selected addressbook items are normal, allow delete + self.popMenuAddressBook.addAction(self.actionAddressBookDelete) self.popMenuAddressBook.exec_( self.ui.tableWidgetAddressBook.mapToGlobal(point)) From a4c4b44afac690d61f0fdf769ff8a64e2e275472 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sun, 1 Nov 2015 08:29:13 +0100 Subject: [PATCH 084/210] accountBrush improvements Moved code into the method --- src/bitmessageqt/foldertree.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/bitmessageqt/foldertree.py b/src/bitmessageqt/foldertree.py index 30fdf82a..6f5ad40c 100644 --- a/src/bitmessageqt/foldertree.py +++ b/src/bitmessageqt/foldertree.py @@ -15,7 +15,9 @@ class AccountMixin (object): return QtGui.QApplication.palette().text().color() def accountBrush(self): - return QtGui.QBrush(self.accountColor()) + brush = QtGui.QBrush(self.accountColor()) + brush.setStyle(QtCore.Qt.NoBrush) + return brush def setAddress(self, address): self.address = str(address) @@ -129,9 +131,7 @@ class Ui_AddressWidget(QtGui.QTreeWidgetItem, AccountMixin): self.setFont(0, font) #set text color - brush = self.accountBrush() - brush.setStyle(QtCore.Qt.NoBrush) - self.setForeground(0, brush) + self.setForeground(0, self.accountBrush()) self.setIcon(0, avatarize(self.address)) self.setText(0, text) @@ -212,9 +212,7 @@ class Ui_SubscriptionWidget(Ui_AddressWidget, AccountMixin): self.setFont(0, font) #set text color - brush = self.accountBrush() - brush.setStyle(QtCore.Qt.NoBrush) - self.setForeground(0, brush) + self.setForeground(0, self.accountBrush()) self.setIcon(0, avatarize(self.address)) self.setText(0, text) @@ -254,9 +252,7 @@ class Ui_AddressBookWidgetItem(QtGui.QTableWidgetItem, AccountMixin): except: self.type = 0 self.setEnabled(True) - brush = self.accountBrush() - brush.setStyle(QtCore.Qt.NoBrush) - self.setForeground(brush) + self.setForeground(self.accountBrush()) def __lt__ (self, other): if (isinstance(other, Ui_AddressBookWidgetItem)): From eb6567c0d77dcf363d273be25483e0bde057bfea Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sun, 1 Nov 2015 08:30:06 +0100 Subject: [PATCH 085/210] Color fixes in messagelists Fixes #87 --- src/bitmessageqt/__init__.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index a4292839..3e501e89 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2391,10 +2391,7 @@ class MyForm(QtGui.QMainWindow): newItem.setToolTip(unicode(acct.toLabel, 'utf-8')) newItem.setFont(font) newItem.setData(Qt.UserRole, str(toAddress)) - if acct.type == 'mailinglist': - newItem.setTextColor(QtGui.QColor(137, 04, 177)) # magenta - if acct.type == 'chan': - newItem.setTextColor(QtGui.QColor(216, 119, 0)) # orange + newItem.setTextColor(AccountColor(toAddress).accountColor()) inbox.insertRow(0) newItem.setIcon(avatarize(toAddress)) inbox.setItem(0, 0, newItem) @@ -2405,6 +2402,7 @@ class MyForm(QtGui.QMainWindow): self.notifierShow(unicode(_translate("MainWindow",'New Message').toUtf8(),'utf-8'), unicode(_translate("MainWindow",'From ').toUtf8(),'utf-8') + unicode(acct.fromLabel, 'utf-8'), self.SOUND_UNKNOWN, None) newItem.setData(Qt.UserRole, str(fromAddress)) newItem.setFont(font) + newItem.setTextColor(AccountColor(fromAddress).accountColor()) newItem.setIcon(avatarize(fromAddress)) inbox.setItem(0, 1, newItem) newItem = QtGui.QTableWidgetItem(unicode(acct.subject, 'utf-8)')) @@ -3791,12 +3789,14 @@ class MyForm(QtGui.QMainWindow): font = QFont() font.setBold(False) # inventoryHashesToMarkRead = [] - currentRow = self.getCurrentMessagelist().currentRow() + currentRow = tableWidget.currentRow() # inventoryHashToMarkRead = str(tableWidget.item( # currentRow, 3).data(Qt.UserRole).toPyObject()) # inventoryHashesToMarkRead.append(inventoryHashToMarkRead) tableWidget.item(currentRow, 0).setFont(font) + tableWidget.item(currentRow, 0).setTextColor(AccountColor(str(tableWidget.item(currentRow, 0).data(Qt.UserRole).toPyObject())).accountColor()) tableWidget.item(currentRow, 1).setFont(font) + tableWidget.item(currentRow, 1).setTextColor(AccountColor(str(tableWidget.item(currentRow, 1).data(Qt.UserRole).toPyObject())).accountColor()) tableWidget.item(currentRow, 2).setFont(font) tableWidget.item(currentRow, 3).setFont(font) self.propagateUnreadCount(self.getCurrentAccount(), folder, self.getCurrentTreeWidget(), -1) From db7374a825d4115fb718aacf8347f0d396396f22 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sun, 1 Nov 2015 08:46:27 +0100 Subject: [PATCH 086/210] Enable/disable dynamic behaviour Fixes #88 --- src/bitmessageqt/__init__.py | 69 ++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 3e501e89..4a3fa711 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -225,17 +225,6 @@ class MyForm(QtGui.QMainWindow): 'customContextMenuRequested(const QPoint&)'), self.on_context_menuYourIdentities) - self.popMenuYourIdentities = QtGui.QMenu(self) - self.popMenuYourIdentities.addAction(self.actionNewYourIdentities) - self.popMenuYourIdentities.addSeparator() - self.popMenuYourIdentities.addAction(self.actionClipboardYourIdentities) - self.popMenuYourIdentities.addSeparator() - self.popMenuYourIdentities.addAction(self.actionEnableYourIdentities) - self.popMenuYourIdentities.addAction(self.actionDisableYourIdentities) - self.popMenuYourIdentities.addAction(self.actionSetAvatarYourIdentities) - self.popMenuYourIdentities.addAction(self.actionSpecialAddressBehaviorYourIdentities) - self.popMenuYourIdentities.addAction(self.actionEmailGateway) - def init_chan_popup_menu(self, connectSignal=True): # Popup menu for the Channels tab self.ui.addressContextMenuToolbar = QtGui.QToolBar() @@ -268,16 +257,6 @@ class MyForm(QtGui.QMainWindow): 'customContextMenuRequested(const QPoint&)'), self.on_context_menuChan) - self.popMenu = QtGui.QMenu(self) - self.popMenu.addAction(self.actionNew) - self.popMenu.addSeparator() - self.popMenu.addAction(self.actionClipboard) - self.popMenu.addSeparator() - self.popMenu.addAction(self.actionEnable) - self.popMenu.addAction(self.actionDisable) - self.popMenu.addAction(self.actionSetAvatar) - self.popMenu.addAction(self.actionSpecialAddressBehavior) - def init_addressbook_popup_menu(self, connectSignal=True): # Popup menu for the Address Book page self.ui.addressBookContextMenuToolbar = QtGui.QToolBar() @@ -338,15 +317,6 @@ class MyForm(QtGui.QMainWindow): self.connect(self.ui.treeWidgetSubscriptions, QtCore.SIGNAL( 'customContextMenuRequested(const QPoint&)'), self.on_context_menuSubscriptions) - self.popMenuSubscriptions = QtGui.QMenu(self) - self.popMenuSubscriptions.addAction(self.actionsubscriptionsNew) - self.popMenuSubscriptions.addAction(self.actionsubscriptionsDelete) - self.popMenuSubscriptions.addSeparator() - self.popMenuSubscriptions.addAction(self.actionsubscriptionsEnable) - self.popMenuSubscriptions.addAction(self.actionsubscriptionsDisable) - self.popMenuSubscriptions.addAction(self.actionsubscriptionsSetAvatar) - self.popMenuSubscriptions.addSeparator() - self.popMenuSubscriptions.addAction(self.actionsubscriptionsClipboard) def init_sent_popup_menu(self, connectSignal=True): # Popup menu for the Sent page @@ -3257,10 +3227,7 @@ class MyForm(QtGui.QMainWindow): self.ui.tabWidget.setCurrentIndex(4) def on_context_menuAddressBook(self, point): - if hasattr(self, "popMenuAddressBook"): - self.popMenuAddressBook.clear() - else: - self.popMenuAddressBook = QtGui.QMenu(self) + self.popMenuAddressBook = QtGui.QMenu(self) self.popMenuAddressBook.addAction(self.actionAddressBookSend) self.popMenuAddressBook.addAction(self.actionAddressBookClipboard) self.popMenuAddressBook.addAction(self.actionAddressBookSubscribe) @@ -3316,6 +3283,17 @@ class MyForm(QtGui.QMainWindow): shared.reloadBroadcastSendersForWhichImWatching() def on_context_menuSubscriptions(self, point): + self.popMenuSubscriptions = QtGui.QMenu(self) + self.popMenuSubscriptions.addAction(self.actionsubscriptionsNew) + self.popMenuSubscriptions.addAction(self.actionsubscriptionsDelete) + self.popMenuSubscriptions.addSeparator() + if self.getCurrentItem().isEnabled: + self.popMenuSubscriptions.addAction(self.actionsubscriptionsDisable) + else: + self.popMenuSubscriptions.addAction(self.actionsubscriptionsEnable) + self.popMenuSubscriptions.addAction(self.actionsubscriptionsSetAvatar) + self.popMenuSubscriptions.addSeparator() + self.popMenuSubscriptions.addAction(self.actionsubscriptionsClipboard) self.popMenuSubscriptions.exec_( self.ui.treeWidgetSubscriptions.mapToGlobal(point)) @@ -3650,11 +3628,34 @@ class MyForm(QtGui.QMainWindow): return True def on_context_menuYourIdentities(self, point): + self.popMenuYourIdentities = QtGui.QMenu(self) + self.popMenuYourIdentities.addAction(self.actionNewYourIdentities) + self.popMenuYourIdentities.addSeparator() + self.popMenuYourIdentities.addAction(self.actionClipboardYourIdentities) + self.popMenuYourIdentities.addSeparator() + if self.getCurrentItem().isEnabled: + self.popMenuYourIdentities.addAction(self.actionDisableYourIdentities) + else: + self.popMenuYourIdentities.addAction(self.actionEnableYourIdentities) + self.popMenuYourIdentities.addAction(self.actionSetAvatarYourIdentities) + self.popMenuYourIdentities.addAction(self.actionSpecialAddressBehaviorYourIdentities) + self.popMenuYourIdentities.addAction(self.actionEmailGateway) self.popMenuYourIdentities.exec_( self.ui.treeWidgetYourIdentities.mapToGlobal(point)) # TODO make one popMenu def on_context_menuChan(self, point): + self.popMenu = QtGui.QMenu(self) + self.popMenu.addAction(self.actionNew) + self.popMenu.addSeparator() + self.popMenu.addAction(self.actionClipboard) + self.popMenu.addSeparator() + if self.getCurrentItem().isEnabled: + self.popMenu.addAction(self.actionDisable) + else: + self.popMenu.addAction(self.actionEnable) + self.popMenu.addAction(self.actionSetAvatar) + self.popMenu.addAction(self.actionSpecialAddressBehavior) self.popMenu.exec_( self.ui.treeWidgetChans.mapToGlobal(point)) From 372722c979751842a514f061d5121db79435bf8d Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sun, 1 Nov 2015 09:01:34 +0100 Subject: [PATCH 087/210] Unread count for trash MarkUnread assumed all folders are inbox. --- src/bitmessageqt/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 4a3fa711..b62ba046 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2932,9 +2932,9 @@ class MyForm(QtGui.QMainWindow): "?," * len(inventoryHashesToMarkUnread))[:-1], *inventoryHashesToMarkUnread) if modified == 1: # performance optimisation - self.propagateUnreadCount(self.getCurrentAccount()) + self.propagateUnreadCount(self.getCurrentAccount(), self.getCurrentFolder()) else: - self.propagateUnreadCount(self.getCurrentAccount(), "inbox", self.getCurrentTreeWidget(), 0) + self.propagateUnreadCount(self.getCurrentAccount(), self.getCurrentFolder(), self.getCurrentTreeWidget(), 0) # tableWidget.selectRow(currentRow + 1) # This doesn't de-select the last message if you try to mark it unread, but that doesn't interfere. Might not be necessary. # We could also select upwards, but then our problem would be with the topmost message. From 9895833aaee13e7553579586271a785c8f81676e Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sun, 1 Nov 2015 09:02:20 +0100 Subject: [PATCH 088/210] Emptying trash updates Fixes #75, however it is quite slow --- src/bitmessageqt/__init__.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index b62ba046..bca58835 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -1463,6 +1463,16 @@ class MyForm(QtGui.QMainWindow): if QtGui.QMessageBox.question(self, _translate("MainWindow", "Delete trash?"), _translate("MainWindow", "Are you sure you want to delete all trashed messages?"), QtGui.QMessageBox.Yes, QtGui.QMessageBox.No) == QtGui.QMessageBox.No: return sqlStoredProcedure('deleteandvacuume') + self.rerenderTabTreeMessages() + self.rerenderTabTreeSubscriptions() + self.rerenderTabTreeChans() + if self.getCurrentFolder(self.ui.treeWidgetYourIdentities) == "trash": + self.loadMessagelist(self.ui.tableWidgetInbox, self.getCurrentAccount(self.ui.treeWidgetYourIdentities), "trash") + elif self.getCurrentFolder(self.ui.treeWidgetSubscriptions) == "trash": + self.loadMessagelist(self.ui.tableWidgetInboxSubscriptions, self.getCurrentAccount(self.ui.treeWidgetSubscriptions), "trash") + elif self.getCurrentFolder(self.ui.treeWidgetChans) == "trash": + self.loadMessagelist(self.ui.tableWidgetInboxChans, self.getCurrentAccount(self.ui.treeWidgetChans), "trash") + # menu botton 'regenerate deterministic addresses' def click_actionRegenerateDeterministicAddresses(self): From 9629abf1026a1c2b1e4d621c69a11e3be3260d91 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sun, 1 Nov 2015 10:09:04 +0100 Subject: [PATCH 089/210] Clear message lists better Fixes #71 --- src/bitmessageqt/__init__.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index bca58835..4f242651 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -915,8 +915,7 @@ class MyForm(QtGui.QMainWindow): ORDER BY lastactiontime ''' % (where,) - while tableWidget.rowCount() > 0: - tableWidget.removeRow(0) + tableWidget.setRowCount(0) acct = None queryreturn = sqlQuery(sqlStatement, account, what) for row in queryreturn: @@ -1040,8 +1039,7 @@ class MyForm(QtGui.QMainWindow): ''' % (where) queryreturn = sqlQuery(sqlStatement, account, what) - while tableWidget.rowCount() > 0: - tableWidget.removeRow(0) + tableWidget.setRowCount(0) tableWidget.setColumnHidden(0, True) tableWidget.setColumnHidden(1, False) From b3877a94130698dcf5a2c7168c3fbb52b89f6c63 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sun, 1 Nov 2015 10:56:37 +0100 Subject: [PATCH 090/210] Trash undelete Fixes #55 --- src/bitmessageqt/__init__.py | 49 ++++++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 4f242651..5faad4c2 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -147,6 +147,9 @@ class MyForm(QtGui.QMainWindow): self.actionTrashInboxMessage = self.ui.inboxContextMenuToolbar.addAction( _translate("MainWindow", "Move to Trash"), self.on_action_InboxTrash) + self.actionUndeleteTrashedMessage = self.ui.inboxContextMenuToolbar.addAction( + _translate("MainWindow", "Undelete"), + self.on_action_TrashUndelete) self.actionForceHtml = self.ui.inboxContextMenuToolbar.addAction( _translate( "MainWindow", "View HTML code as formatted text"), @@ -179,16 +182,6 @@ class MyForm(QtGui.QMainWindow): 'customContextMenuRequested(const QPoint&)'), self.on_context_menuInbox) - self.popMenuInbox = QtGui.QMenu(self) - self.popMenuInbox.addAction(self.actionForceHtml) - self.popMenuInbox.addAction(self.actionMarkUnread) - self.popMenuInbox.addSeparator() - self.popMenuInbox.addAction(self.actionReply) - self.popMenuInbox.addAction(self.actionAddSenderToAddressBook) - self.popMenuInbox.addSeparator() - self.popMenuInbox.addAction(self.actionSaveMessageAs) - self.popMenuInbox.addAction(self.actionTrashInboxMessage) - def init_identities_popup_menu(self, connectSignal=True): # Popup menu for the Your Identities tab self.ui.addressContextMenuToolbarYourIdentities = QtGui.QToolBar() @@ -3096,6 +3089,30 @@ class MyForm(QtGui.QMainWindow): tableWidget.selectRow(currentRow - 1) if unread: self.propagateUnreadCount(self.getCurrentAccount(), self.getCurrentFolder(), self.getCurrentTreeWidget(), 0) + + def on_action_TrashUndelete(self): + tableWidget = self.getCurrentMessagelist() + if not tableWidget: + return + unread = False + currentRow = 0 + while tableWidget.selectedIndexes(): + currentRow = tableWidget.selectedIndexes()[0].row() + inventoryHashToTrash = str(tableWidget.item( + currentRow, 3).data(Qt.UserRole).toPyObject()) + sqlExecute('''UPDATE inbox SET folder='inbox' WHERE msgid=?''', inventoryHashToTrash) + if tableWidget.item(currentRow, 0).font().bold(): + unread = True + self.getCurrentMessageTextedit().setText("") + tableWidget.removeRow(currentRow) + self.statusBar().showMessage(_translate( + "MainWindow", "Undeleted item.")) + if currentRow == 0: + tableWidget.selectRow(currentRow) + else: + tableWidget.selectRow(currentRow - 1) + if unread: + self.propagateUnreadCount(self.getCurrentAccount(), None, self.getCurrentTreeWidget(), 0) def on_action_InboxSaveMessageAs(self): tableWidget = self.getCurrentMessagelist() @@ -3676,6 +3693,18 @@ class MyForm(QtGui.QMainWindow): if currentFolder == 'sent': self.on_context_menuSent(point) else: + self.popMenuInbox = QtGui.QMenu(self) + self.popMenuInbox.addAction(self.actionForceHtml) + self.popMenuInbox.addAction(self.actionMarkUnread) + self.popMenuInbox.addSeparator() + self.popMenuInbox.addAction(self.actionReply) + self.popMenuInbox.addAction(self.actionAddSenderToAddressBook) + self.popMenuInbox.addSeparator() + self.popMenuInbox.addAction(self.actionSaveMessageAs) + if currentFolder == "trash": + self.popMenuInbox.addAction(self.actionUndeleteTrashedMessage) + else: + self.popMenuInbox.addAction(self.actionTrashInboxMessage) self.popMenuInbox.exec_(tableWidget.mapToGlobal(point)) def on_context_menuSent(self, point): From 8862a88afa415cc0651e81e9a5e75ac9fec26c99 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sun, 1 Nov 2015 10:59:11 +0100 Subject: [PATCH 091/210] Deleting from sent fix Fixes #52 --- src/bitmessageqt/__init__.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 5faad4c2..4be57c10 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -3147,19 +3147,28 @@ class MyForm(QtGui.QMainWindow): # Send item on the Sent tab to trash def on_action_SentTrash(self): - while self.ui.tableWidgetInbox.selectedIndexes() != []: - currentRow = self.ui.tableWidgetInbox.selectedIndexes()[0].row() - ackdataToTrash = str(self.ui.tableWidgetInbox.item( + currentRow = 0 + unread = False + tableWidget = self.getCurrentMessagelist() + if not tableWidget: + return + while tableWidget.selectedIndexes() != []: + currentRow = tableWidget.selectedIndexes()[0].row() + ackdataToTrash = str(tableWidget.item( currentRow, 3).data(Qt.UserRole).toPyObject()) sqlExecute('''UPDATE sent SET folder='trash' WHERE ackdata=?''', ackdataToTrash) - self.ui.textEditSentMessage.setPlainText("") - self.ui.tableWidgetInbox.removeRow(currentRow) + if tableWidget.item(currentRow, 0).font().bold(): + unread = True + self.getCurrentMessageTextedit().setPlainText("") + tableWidget.removeRow(currentRow) self.statusBar().showMessage(_translate( - "MainWindow", "Moved items to trash. There is no user interface to view your trash, but it is still on disk if you are desperate to get it back.")) + "MainWindow", "Moved items to trash.")) if currentRow == 0: self.ui.tableWidgetInbox.selectRow(currentRow) else: self.ui.tableWidgetInbox.selectRow(currentRow - 1) + if unread: + self.propagateUnreadCount(self.getCurrentAccount(), None, self.getCurrentTreeWidget(), 0) def on_action_ForceSend(self): currentRow = self.ui.tableWidgetInbox.currentRow() From a59ea878b91657bc3f81b1bd50043fa927912881 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sun, 1 Nov 2015 11:08:41 +0100 Subject: [PATCH 092/210] getCurrentFolder should return None on unavailable I think None is better than False. --- src/bitmessageqt/__init__.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 4be57c10..7a289d43 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -1017,7 +1017,7 @@ class MyForm(QtGui.QMainWindow): xAddress = "fromaddress" else: xAddress = "toaddress" - if folder != False: + if folder is not None: sqlStatement = ''' SELECT folder, msgid, toaddress, fromaddress, subject, received, read FROM inbox WHERE ''' + xAddress + '''=? AND folder=? AND %s LIKE ? @@ -2350,7 +2350,7 @@ class MyForm(QtGui.QMainWindow): inbox = self.getAccountMessagelist(acct) treeWidget = self.getAccountTreeWidget(acct) self.propagateUnreadCount(acct.address) - if (self.getCurrentFolder(treeWidget) != "inbox" and self.getCurrentFolder(treeWidget) != False) or self.getCurrentAccount(treeWidget) != acct.address: + if (self.getCurrentFolder(treeWidget) != "inbox" and self.getCurrentFolder(treeWidget) is not None) or self.getCurrentAccount(treeWidget) != acct.address: # Ubuntu should notify of new message irespective of whether it's in current message list or not self.ubuntuMessagingMenuUpdate(True, None, acct.toLabel) return @@ -3530,8 +3530,7 @@ class MyForm(QtGui.QMainWindow): if currentItem and hasattr(currentItem, 'folderName'): return currentItem.folderName else: - # TODO need debug msg? - return False + return None def setCurrentItemColor(self, color): treeWidget = self.getCurrentTreeWidget() @@ -3697,7 +3696,7 @@ class MyForm(QtGui.QMainWindow): tableWidget = self.getCurrentMessagelist() if tableWidget: currentFolder = self.getCurrentFolder() - if currentFolder == False: + if currentFolder is None: pass if currentFolder == 'sent': self.on_context_menuSent(point) From b8d2571b7fc5c39eb5aea3faa7d4ee380976549f Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sun, 1 Nov 2015 11:09:36 +0100 Subject: [PATCH 093/210] Unread count fixes --- src/bitmessageqt/__init__.py | 44 ++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 7a289d43..07a90e61 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -832,7 +832,28 @@ class MyForm(QtGui.QMainWindow): def propagateUnreadCount(self, address = None, folder = "inbox", widget = None, type = 1): def updateUnreadCount(item): - if type == 1: + # if refreshing the account root, we need to rescan folders + if type == 0 or (folder is None and isinstance(item, Ui_FolderWidget)): + if addressItem.type == 'subscription' or addressItem.type == 'mailinglist': + xAddress = "fromaddress" + else: + xAddress = "toaddress" + xFolder = folder + if isinstance(item, Ui_FolderWidget): + xFolder = item.folderName + if address and xFolder: + queryreturn = sqlQuery("SELECT COUNT(*) FROM inbox WHERE " + xAddress + " = ? AND folder = ? AND read = 0", address, xFolder) + elif address: + queryreturn = sqlQuery("SELECT COUNT(*) FROM inbox WHERE " + xAddress + " = ? AND read = 0", address) + elif xFolder: + queryreturn = sqlQuery("SELECT COUNT(*) FROM inbox WHERE folder = ? AND read = 0", xFolder) + else: + queryreturn = sqlQuery("SELECT COUNT(*) FROM inbox WHERE read = 0") + for row in queryreturn: + item.setUnreadCount(int(row[0])) + if isinstance(item, Ui_AddressWidget): + self.drawTrayIcon(self.currentTrayIconFileName, self.findInboxUnreadCount()) + elif type == 1: item.setUnreadCount(item.unreadCount + 1) if isinstance(item, Ui_AddressWidget): self.drawTrayIcon(self.currentTrayIconFileName, self.findInboxUnreadCount(self.unreadCount + 1)) @@ -840,23 +861,6 @@ class MyForm(QtGui.QMainWindow): item.setUnreadCount(item.unreadCount - 1) if isinstance(item, Ui_AddressWidget): self.drawTrayIcon(self.currentTrayIconFileName, self.findInboxUnreadCount(self.unreadCount -1)) - else: - if addressItem.type == 'subscription' or addressItem.type == 'mailinglist': - xAddress = "fromaddress" - else: - xAddress = "toaddress" - if address and folder: - queryreturn = sqlQuery("SELECT COUNT(*) FROM inbox WHERE " + xAddress + " = ? AND folder = ? AND read = 0", address, folder) - elif address: - queryreturn = sqlQuery("SELECT COUNT(*) FROM inbox WHERE " + xAddress + " = ? AND read = 0", address) - elif folder: - queryreturn = sqlQuery("SELECT COUNT(*) FROM inbox WHERE folder = ? AND read = 0", folder) - else: - queryreturn = sqlQuery("SELECT COUNT(*) FROM inbox WHERE read = 0") - for row in queryreturn: - item.setUnreadCount(int(row[0])) - if isinstance(item, Ui_AddressWidget): - self.drawTrayIcon(self.currentTrayIconFileName, self.findInboxUnreadCount()) if widget == None: widgets = [self.ui.treeWidgetYourIdentities, self.ui.treeWidgetSubscriptions, self.ui.treeWidgetChans] @@ -873,7 +877,7 @@ class MyForm(QtGui.QMainWindow): continue for j in range(addressItem.childCount()): folderItem = addressItem.child(j) - if folder is not None and folderItem.data(0, QtCore.Qt.UserRole) != folder: + if folder is not None and folderItem.folderName != folder: continue updateUnreadCount(folderItem) @@ -3088,7 +3092,7 @@ class MyForm(QtGui.QMainWindow): else: tableWidget.selectRow(currentRow - 1) if unread: - self.propagateUnreadCount(self.getCurrentAccount(), self.getCurrentFolder(), self.getCurrentTreeWidget(), 0) + self.propagateUnreadCount(self.getCurrentAccount(), None, self.getCurrentTreeWidget(), 0) def on_action_TrashUndelete(self): tableWidget = self.getCurrentMessagelist() From c337f394e89b7adabc4116b097120fcca26d6ed5 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sun, 1 Nov 2015 11:09:58 +0100 Subject: [PATCH 094/210] Wrong messagelist --- src/bitmessageqt/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 07a90e61..cf14845f 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -3083,7 +3083,7 @@ class MyForm(QtGui.QMainWindow): sqlExecute('''UPDATE inbox SET folder='trash' WHERE msgid=?''', inventoryHashToTrash) if tableWidget.item(currentRow, 0).font().bold(): unread = True - self.ui.textEditInboxMessage.setText("") + self.getCurrentMessageTextedit().setText("") tableWidget.removeRow(currentRow) self.statusBar().showMessage(_translate( "MainWindow", "Moved items to trash.")) From 32f2697ae30102e4c2ae809506e22f7d6daac6d4 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sun, 1 Nov 2015 11:43:05 +0100 Subject: [PATCH 095/210] notify of new message fixed Will notify even if not on active messagelist. --- src/bitmessageqt/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index cf14845f..c878e8b1 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2354,6 +2354,8 @@ class MyForm(QtGui.QMainWindow): inbox = self.getAccountMessagelist(acct) treeWidget = self.getAccountTreeWidget(acct) self.propagateUnreadCount(acct.address) + if shared.config.getboolean('bitmessagesettings', 'showtraynotifications'): + self.notifierShow(unicode(_translate("MainWindow",'New Message').toUtf8(),'utf-8'), unicode(_translate("MainWindow",'From ').toUtf8(),'utf-8') + unicode(acct.fromLabel, 'utf-8'), self.SOUND_UNKNOWN, None) if (self.getCurrentFolder(treeWidget) != "inbox" and self.getCurrentFolder(treeWidget) is not None) or self.getCurrentAccount(treeWidget) != acct.address: # Ubuntu should notify of new message irespective of whether it's in current message list or not self.ubuntuMessagingMenuUpdate(True, None, acct.toLabel) @@ -2373,8 +2375,6 @@ class MyForm(QtGui.QMainWindow): newItem = QtGui.QTableWidgetItem(unicode(acct.fromLabel, 'utf-8')) newItem.setToolTip(unicode(acct.fromLabel, 'utf-8')) - if shared.config.getboolean('bitmessagesettings', 'showtraynotifications'): - self.notifierShow(unicode(_translate("MainWindow",'New Message').toUtf8(),'utf-8'), unicode(_translate("MainWindow",'From ').toUtf8(),'utf-8') + unicode(acct.fromLabel, 'utf-8'), self.SOUND_UNKNOWN, None) newItem.setData(Qt.UserRole, str(fromAddress)) newItem.setFont(font) newItem.setTextColor(AccountColor(fromAddress).accountColor()) From 0de75079df0193874d8bb27b73565ec7daa88c1b Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sun, 1 Nov 2015 12:09:03 +0100 Subject: [PATCH 096/210] Version bump --- src/shared.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared.py b/src/shared.py index 54093f51..8e1410e3 100644 --- a/src/shared.py +++ b/src/shared.py @@ -1,6 +1,6 @@ from __future__ import division -softwareVersion = '0.5.0' +softwareVersion = '0.5.1' verbose = 1 maximumAgeOfAnObjectThatIAmWillingToAccept = 216000 # This is obsolete with the change to protocol v3 but the singleCleaner thread still hasn't been updated so we need this a little longer. lengthOfTimeToHoldOnToAllPubkeys = 2419200 # Equals 4 weeks. You could make this longer if you want but making it shorter would not be advisable because there is a very small possibility that it could keep you from obtaining a needed pubkey for a period of time. From 477b624edd948e4069811e5d7b9b5689287b7acc Mon Sep 17 00:00:00 2001 From: mailchuck Date: Thu, 5 Nov 2015 17:27:35 +0100 Subject: [PATCH 097/210] Message status update Someone reported the exception via BM. Fixes #90 Signed-off-by: mailchuck --- src/bitmessageqt/__init__.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index c878e8b1..ce5e3fc4 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -1795,20 +1795,24 @@ class MyForm(QtGui.QMainWindow): return self.unreadCount def updateSentItemStatusByToAddress(self, toAddress, textToDisplay): - for i in range(self.ui.tableWidgetSent.rowCount()): - rowAddress = str(self.ui.tableWidgetSent.item( + sent = self.getAccountMessagelist(toAddress) + treeWidget = self.getAccountTreeWidget(toAddress) + if self.getCurrentFolder(treeWidget) != "sent": + return + for i in range(sent.rowCount()): + rowAddress = str(sent.item( i, 0).data(Qt.UserRole).toPyObject()) if toAddress == rowAddress: - self.ui.tableWidgetSent.item(i, 3).setToolTip(textToDisplay) + sent.item(i, 3).setToolTip(textToDisplay) try: newlinePosition = textToDisplay.indexOf('\n') except: # If someone misses adding a "_translate" to a string before passing it to this function, this function won't receive a qstring which will cause an exception. newlinePosition = 0 if newlinePosition > 1: - self.ui.tableWidgetInbox.item(i, 3).setText( + sent.item(i, 3).setText( textToDisplay[:newlinePosition]) else: - self.ui.tableWidgetInbox.item(i, 3).setText(textToDisplay) + sent.item(i, 3).setText(textToDisplay) def updateSentItemStatusByAckdata(self, ackdata, textToDisplay): for i in range(self.ui.tableWidgetInbox.rowCount()): From c0351fe42ff8f46f71e0641cb183b4a46af700c4 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Thu, 5 Nov 2015 20:41:41 +0100 Subject: [PATCH 098/210] Support for PoW from an external library This will attempt to include a PoW library (.so on Unix, .dll on windows) to do PoW. This is done in a safe way and clean fallback to normal PoW. The code for the library will be in another commit. The code is take from https://github.com/bm-robertwhite/bitmessage-powfaster, with minor modifications. This patch also includes code to make GPU PoW have a safer fallback. --- src/proofofwork.py | 51 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/src/proofofwork.py b/src/proofofwork.py index 81f7e3df..93f6f2cd 100644 --- a/src/proofofwork.py +++ b/src/proofofwork.py @@ -7,7 +7,31 @@ import sys from shared import config, frozen import shared import openclpow -#import os +import os +import ctypes + +curdir = os.path.dirname(__file__) +bitmsglib = 'bitmsghash.so' +if "win32" == sys.platform: + if ctypes.sizeof(ctypes.c_voidp) == 4: + bitmsglib = 'bitmsghash32.dll' + else: + bitmsglib = 'bitmsghash64.dll' + try: + bso = ctypes.WinDLL(os.path.join(curdir, bitmsglib)) + except: + bso = None +else: + try: + bso = ctypes.CDLL(os.path.join(curdir, bitmsglib)) + except: + bso = None +if bso: + try: + bmpow = bso.BitmessagePOW + bmpow.restype = ctypes.c_ulonglong + except: + bmpow = None def _set_idle(): if 'linux' in sys.platform: @@ -72,12 +96,23 @@ def _doFastPoW(target, initialHash): pool.join() #Wait for the workers to exit... return result[0], result[1] time.sleep(0.2) +def _doCPoW(target, initialHash): + h = initialHash + m = target + out_h = ctypes.pointer(ctypes.create_string_buffer(h, 64)) + out_m = ctypes.c_ulonglong(m) + print "C PoW start" + nonce = bmpow(out_h, out_m) + trialValue, = unpack('>Q',hashlib.sha512(hashlib.sha512(pack('>Q',nonce) + initialHash).digest()).digest()[0:8]) + print "C PoW done" + return [trialValue, nonce] def _doGPUPoW(target, initialHash): - print "GPU POW\n" + print "GPU PoW start" nonce = openclpow.do_opencl_pow(initialHash.encode("hex"), target) trialValue, = unpack('>Q',hashlib.sha512(hashlib.sha512(pack('>Q',nonce) + initialHash).digest()).digest()[0:8]) #print "{} - value {} < {}".format(nonce, trialValue, target) + print "GPU PoW done" return [trialValue, nonce] def run(target, initialHash): @@ -88,8 +123,16 @@ def run(target, initialHash): # print "GPU: %s, %s" % (trialvalue1, nonce1) # print "Fast: %s, %s" % (trialvalue, nonce) # return [trialvalue, nonce] - return _doGPUPoW(target, initialHash) - elif frozen == "macosx_app" or not frozen: + try: + return _doGPUPoW(target, initialHash) + except: + pass # fallback to normal PoW + if frozen == "macosx_app" or not frozen: + if bmpow: + try: + return _doCPoW(target, initialHash) + except: + pass # fallback to normal PoW return _doFastPoW(target, initialHash) else: return _doSafePoW(target, initialHash) From 1cace115215f4577bd623985e74a61a68a413b6d Mon Sep 17 00:00:00 2001 From: mailchuck Date: Thu, 5 Nov 2015 20:47:30 +0100 Subject: [PATCH 099/210] C library for bitmessage PoW Originally from https://github.com/bm-robertwhite/bitmessage-powfaster. Modified to compile correctly on Visual Studio, and to decrease thread priority. I have not tried it on Linux yet. --- src/bitmsghash.cpp | 89 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 src/bitmsghash.cpp diff --git a/src/bitmsghash.cpp b/src/bitmsghash.cpp new file mode 100644 index 00000000..9f17d5ff --- /dev/null +++ b/src/bitmsghash.cpp @@ -0,0 +1,89 @@ +// bitmessage cracker, build with g++ or MSVS to a shared library, use included python code for usage under bitmessage +#ifdef _WIN32 +#include "Winsock.h" +#include "Windows.h" +#define uint64_t unsigned __int64 +#else +#include +#include +#include +#endif +#include +#include +#include + +#include "openssl/sha.h" + +#define HASH_SIZE 64 +#define BUFLEN 16384 + +#define ntohll(x) ( ( (uint64_t)(ntohl( (unsigned int)((x << 32) >> 32) )) << 32) | ntohl( ((unsigned int)(x >> 32)) ) ) + +unsigned long long max_val; +unsigned char *initialHash; + + +int numthreads = 8; +unsigned long long successval = 0; +#ifdef _WIN32 +DWORD WINAPI threadfunc(LPVOID lpParameter) { + DWORD incamt = (DWORD)lpParameter; +#else +void * threadfunc(void* param) { + unsigned int incamt = (unsigned int)param; +#endif + SHA512_CTX sha; + unsigned char buf[HASH_SIZE + sizeof(uint64_t)] = { 0 }; + unsigned char output[HASH_SIZE] = { 0 }; + + memcpy(buf + sizeof(uint64_t), initialHash, HASH_SIZE); + + unsigned long long tmpnonce = incamt; + unsigned long long * nonce = (unsigned long long *)buf; + unsigned long long * hash = (unsigned long long *)output; + while (successval == 0) { + tmpnonce += numthreads; + + (*nonce) = ntohll(tmpnonce); /* increment nonce */ + SHA512_Init(&sha); + SHA512_Update(&sha, buf, HASH_SIZE + sizeof(uint64_t)); + SHA512_Final(output, &sha); + SHA512_Init(&sha); + SHA512_Update(&sha, output, HASH_SIZE); + SHA512_Final(output, &sha); + + if (ntohll(*hash) < max_val) { + successval = tmpnonce; + } + } + return NULL; +} + +extern "C" __declspec(dllexport) unsigned long long BitmessagePOW(unsigned char * starthash, unsigned long long target) +{ + successval = 0; + max_val = target; + initialHash = (unsigned char *)starthash; +# ifdef _WIN32 + HANDLE* threads = (HANDLE*)calloc(sizeof(HANDLE), numthreads); +# else + pthread_t* threads = calloc(sizeof(pthread_t), numthreads); +# endif + for (int i = 0; i < numthreads; i++) { +# ifdef _WIN32 + threads[i] = CreateThread(NULL, 0, threadfunc, (LPVOID)i, 0, NULL); + SetThreadPriority(threads[i], THREAD_PRIORITY_IDLE); +# else + pthread_create(&threads[i], NULL, threadfunc, (void*)i); +# endif + } +# ifdef _WIN32 + WaitForMultipleObjects(numthreads, threads, TRUE, INFINITE); +# else + for (int i = 0; i < numthreads; i++) { + pthread_join(threads[i], NULL); + } +# endif + free(threads); + return successval; +} \ No newline at end of file From dfda1527b8406f5a50ed31f928f29884047ef35f Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 5 Nov 2015 22:18:53 +0100 Subject: [PATCH 100/210] C PoW updates Catching up with Grant Olson's fork. Reduce thread priority. --- src/bitmsghash.cpp | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/bitmsghash.cpp b/src/bitmsghash.cpp index 9f17d5ff..d7dc9973 100644 --- a/src/bitmsghash.cpp +++ b/src/bitmsghash.cpp @@ -17,6 +17,14 @@ #define HASH_SIZE 64 #define BUFLEN 16384 +#if defined(__GNUC__) + #define EXPORT __attribute__ ((__visibility__("default"))) + #define UINT intptr_t +#elif defined(WIN32) + #define EXPORT __declspec(dllexport) + #define UINT unsigned int +#endif + #define ntohll(x) ( ( (uint64_t)(ntohl( (unsigned int)((x << 32) >> 32) )) << 32) | ntohl( ((unsigned int)(x >> 32)) ) ) unsigned long long max_val; @@ -30,7 +38,7 @@ DWORD WINAPI threadfunc(LPVOID lpParameter) { DWORD incamt = (DWORD)lpParameter; #else void * threadfunc(void* param) { - unsigned int incamt = (unsigned int)param; + UINT incamt = (UINT)param; #endif SHA512_CTX sha; unsigned char buf[HASH_SIZE + sizeof(uint64_t)] = { 0 }; @@ -59,7 +67,7 @@ void * threadfunc(void* param) { return NULL; } -extern "C" __declspec(dllexport) unsigned long long BitmessagePOW(unsigned char * starthash, unsigned long long target) +extern "C" EXPORT unsigned long long BitmessagePOW(unsigned char * starthash, unsigned long long target) { successval = 0; max_val = target; @@ -67,14 +75,17 @@ extern "C" __declspec(dllexport) unsigned long long BitmessagePOW(unsigned char # ifdef _WIN32 HANDLE* threads = (HANDLE*)calloc(sizeof(HANDLE), numthreads); # else - pthread_t* threads = calloc(sizeof(pthread_t), numthreads); + pthread_t* threads = (pthread_t*)calloc(sizeof(pthread_t), numthreads); + struct sched_param schparam; + schparam.sched_priority = 0; # endif - for (int i = 0; i < numthreads; i++) { + for (UINT i = 0; i < numthreads; i++) { # ifdef _WIN32 threads[i] = CreateThread(NULL, 0, threadfunc, (LPVOID)i, 0, NULL); SetThreadPriority(threads[i], THREAD_PRIORITY_IDLE); # else pthread_create(&threads[i], NULL, threadfunc, (void*)i); + pthread_setschedparam(threads[i], SCHED_IDLE, &schparam); # endif } # ifdef _WIN32 @@ -86,4 +97,4 @@ extern "C" __declspec(dllexport) unsigned long long BitmessagePOW(unsigned char # endif free(threads); return successval; -} \ No newline at end of file +} From cee59a0547df9404d5eb4ff522af4a4aa7d85f2f Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 5 Nov 2015 22:27:34 +0100 Subject: [PATCH 101/210] C PoW library makefile For easy compiling with GNU make (e.g. Unix-like systems). Run as "make bitmsghash.so" --- src/Makefile | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/Makefile diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 00000000..3c9c7470 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,15 @@ +all: + @echo "This compiles and tests the C PoW library, it's not necessary to run PyBitmessage" + +powtest: + ./testpow.py + +bitmsghash.so: bitmsghash.o + g++ bitmsghash.o -shared -fPIC -lpthread -lcrypto -o bitmsghash.so + +bitmsghash.o: + g++ -Wall -O3 -march=native -fPIC -c bitmsghash.cpp + +clean: + rm -f bitmsghash.o bitmsghash.so + From 1721231484343c519b8bde995807a93fc6553470 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sat, 7 Nov 2015 18:39:55 +0100 Subject: [PATCH 102/210] C PoW updates - move to subdirectory - get rid of compile warnings on windows - get number of threads from affinity (Windows, Linux) or core count (BSD/OSX) --- src/{ => bitmsghash}/Makefile | 0 src/{ => bitmsghash}/bitmsghash.cpp | 58 ++++++++++++++++++++++++----- src/proofofwork.py | 4 +- 3 files changed, 50 insertions(+), 12 deletions(-) rename src/{ => bitmsghash}/Makefile (100%) rename src/{ => bitmsghash}/bitmsghash.cpp (63%) diff --git a/src/Makefile b/src/bitmsghash/Makefile similarity index 100% rename from src/Makefile rename to src/bitmsghash/Makefile diff --git a/src/bitmsghash.cpp b/src/bitmsghash/bitmsghash.cpp similarity index 63% rename from src/bitmsghash.cpp rename to src/bitmsghash/bitmsghash.cpp index d7dc9973..c0a40509 100644 --- a/src/bitmsghash.cpp +++ b/src/bitmsghash/bitmsghash.cpp @@ -19,27 +19,23 @@ #if defined(__GNUC__) #define EXPORT __attribute__ ((__visibility__("default"))) - #define UINT intptr_t -#elif defined(WIN32) +#elif defined(_WIN32) #define EXPORT __declspec(dllexport) - #define UINT unsigned int #endif #define ntohll(x) ( ( (uint64_t)(ntohl( (unsigned int)((x << 32) >> 32) )) << 32) | ntohl( ((unsigned int)(x >> 32)) ) ) unsigned long long max_val; unsigned char *initialHash; - - -int numthreads = 8; unsigned long long successval = 0; +unsigned int numthreads = 0; + #ifdef _WIN32 DWORD WINAPI threadfunc(LPVOID lpParameter) { - DWORD incamt = (DWORD)lpParameter; #else void * threadfunc(void* param) { - UINT incamt = (UINT)param; #endif + unsigned int incamt = *((unsigned int*)lpParameter); SHA512_CTX sha; unsigned char buf[HASH_SIZE + sizeof(uint64_t)] = { 0 }; unsigned char output[HASH_SIZE] = { 0 }; @@ -67,25 +63,66 @@ void * threadfunc(void* param) { return NULL; } +void getnumthreads() +{ +#ifdef _WIN32 + DWORD_PTR dwProcessAffinity, dwSystemAffinity; +#elif __linux__ + cpu_set_t dwProcessAffinity; +#else + int dwProcessAffinity = 0; + int32_t core_count = 0; +#endif + unsigned int len = sizeof(dwProcessAffinity); + if (numthreads > 0) + return; +#ifdef _WIN32 + GetProcessAffinityMask(GetCurrentProcess(), &dwProcessAffinity, &dwSystemAffinity); +#elif __linux__ + sched_getaffinity(0, len, &dwProcessAffinity); +#else + if (sysctlbyname("hw.logicalcpu", &core_count, &len, 0, 0)) + numthreads = core_count; +#endif + for (unsigned int i = 0; i < len * 8; i++) + if (dwProcessAffinity & (1i64 << i)) { + numthreads++; + printf("Detected core on: %u\n", i); + } + printf("Affinity: %lx\n", (unsigned long) dwProcessAffinity); + printf("Number of threads: %i\n", (int)numthreads); +} + extern "C" EXPORT unsigned long long BitmessagePOW(unsigned char * starthash, unsigned long long target) { successval = 0; max_val = target; + getnumthreads(); initialHash = (unsigned char *)starthash; # ifdef _WIN32 HANDLE* threads = (HANDLE*)calloc(sizeof(HANDLE), numthreads); # else pthread_t* threads = (pthread_t*)calloc(sizeof(pthread_t), numthreads); struct sched_param schparam; +# ifdef __linux__ schparam.sched_priority = 0; +# else + schparam.sched_priority = PTHREAD_MIN_PRIORITY; # endif +# endif + unsigned int *threaddata = (unsigned int *)calloc(sizeof(unsigned int), numthreads); for (UINT i = 0; i < numthreads; i++) { + threaddata[i] = i; # ifdef _WIN32 - threads[i] = CreateThread(NULL, 0, threadfunc, (LPVOID)i, 0, NULL); + threads[i] = CreateThread(NULL, 0, threadfunc, (LPVOID)&threaddata[i], 0, NULL); SetThreadPriority(threads[i], THREAD_PRIORITY_IDLE); # else - pthread_create(&threads[i], NULL, threadfunc, (void*)i); + pthread_create(&threads[i], NULL, threadfunc, (void*)&threaddata[i]); +# ifdef __linux__ pthread_setschedparam(threads[i], SCHED_IDLE, &schparam); +# else + pthread_setschedparam(threads[i], SCHED_RR, &schparam) +# endif # endif } # ifdef _WIN32 @@ -96,5 +133,6 @@ extern "C" EXPORT unsigned long long BitmessagePOW(unsigned char * starthash, un } # endif free(threads); + free(threaddata); return successval; } diff --git a/src/proofofwork.py b/src/proofofwork.py index 93f6f2cd..0aaf6487 100644 --- a/src/proofofwork.py +++ b/src/proofofwork.py @@ -18,12 +18,12 @@ if "win32" == sys.platform: else: bitmsglib = 'bitmsghash64.dll' try: - bso = ctypes.WinDLL(os.path.join(curdir, bitmsglib)) + bso = ctypes.WinDLL(os.path.join(curdir, "bitmsghash", bitmsglib)) except: bso = None else: try: - bso = ctypes.CDLL(os.path.join(curdir, bitmsglib)) + bso = ctypes.CDLL(os.path.join(curdir, "bitmsghash", bitmsglib)) except: bso = None if bso: From 5d9bcbd27841ceb64ab3630cff637dedf8eca003 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 7 Nov 2015 19:02:53 +0100 Subject: [PATCH 103/210] Make building bitmsghash default --- src/bitmsghash/Makefile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/bitmsghash/Makefile b/src/bitmsghash/Makefile index 3c9c7470..aac89349 100644 --- a/src/bitmsghash/Makefile +++ b/src/bitmsghash/Makefile @@ -1,5 +1,4 @@ -all: - @echo "This compiles and tests the C PoW library, it's not necessary to run PyBitmessage" +all: bitmsghash.so powtest: ./testpow.py From ac461ba261c98815fe15335d5e51b00df1552221 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 7 Nov 2015 19:03:38 +0100 Subject: [PATCH 104/210] Bitmsghash linux compile fixes --- src/bitmsghash/bitmsghash.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/bitmsghash/bitmsghash.cpp b/src/bitmsghash/bitmsghash.cpp index c0a40509..e05c88ae 100644 --- a/src/bitmsghash/bitmsghash.cpp +++ b/src/bitmsghash/bitmsghash.cpp @@ -31,11 +31,11 @@ unsigned long long successval = 0; unsigned int numthreads = 0; #ifdef _WIN32 -DWORD WINAPI threadfunc(LPVOID lpParameter) { +DWORD WINAPI threadfunc(LPVOID param) { #else void * threadfunc(void* param) { #endif - unsigned int incamt = *((unsigned int*)lpParameter); + unsigned int incamt = *((unsigned int*)param); SHA512_CTX sha; unsigned char buf[HASH_SIZE + sizeof(uint64_t)] = { 0 }; unsigned char output[HASH_SIZE] = { 0 }; @@ -85,11 +85,14 @@ void getnumthreads() numthreads = core_count; #endif for (unsigned int i = 0; i < len * 8; i++) +#ifdef _WIN32 if (dwProcessAffinity & (1i64 << i)) { +#else + if (CPU_ISSET(i, &dwProcessAffinity)) { +#endif numthreads++; printf("Detected core on: %u\n", i); } - printf("Affinity: %lx\n", (unsigned long) dwProcessAffinity); printf("Number of threads: %i\n", (int)numthreads); } @@ -111,7 +114,7 @@ extern "C" EXPORT unsigned long long BitmessagePOW(unsigned char * starthash, un # endif # endif unsigned int *threaddata = (unsigned int *)calloc(sizeof(unsigned int), numthreads); - for (UINT i = 0; i < numthreads; i++) { + for (unsigned int i = 0; i < numthreads; i++) { threaddata[i] = i; # ifdef _WIN32 threads[i] = CreateThread(NULL, 0, threadfunc, (LPVOID)&threaddata[i], 0, NULL); @@ -128,7 +131,7 @@ extern "C" EXPORT unsigned long long BitmessagePOW(unsigned char * starthash, un # ifdef _WIN32 WaitForMultipleObjects(numthreads, threads, TRUE, INFINITE); # else - for (int i = 0; i < numthreads; i++) { + for (unsigned int i = 0; i < numthreads; i++) { pthread_join(threads[i], NULL); } # endif From f155966cc8f64e62a0fe1810b0b0ef5dc8e40c09 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sun, 8 Nov 2015 11:17:03 +0100 Subject: [PATCH 105/210] Move and rename OpenCL kernel --- src/{kernel.cl => bitmsghash/bitmsghash.cl} | 556 ++++++++++---------- 1 file changed, 278 insertions(+), 278 deletions(-) rename src/{kernel.cl => bitmsghash/bitmsghash.cl} (96%) diff --git a/src/kernel.cl b/src/bitmsghash/bitmsghash.cl similarity index 96% rename from src/kernel.cl rename to src/bitmsghash/bitmsghash.cl index d8db974a..9af7ca19 100644 --- a/src/kernel.cl +++ b/src/bitmsghash/bitmsghash.cl @@ -1,278 +1,278 @@ -/* -* This is based on the John The Ripper SHA512 code, modified for double SHA512 and for use as a miner in Bitmessage. -* This software is originally Copyright (c) 2012 Myrice -* and it is hereby released to the general public under the following terms: -* Redistribution and use in source and binary forms, with or without modification, are permitted. -*/ - -#ifdef cl_khr_byte_addressable_store -#pragma OPENCL EXTENSION cl_khr_byte_addressable_store : disable -#endif - -#define uint8_t unsigned char -#define uint32_t unsigned int -#define uint64_t unsigned long -#define SALT_SIZE 0 - -#define BINARY_SIZE 8 -#define FULL_BINARY_SIZE 64 - - -#define PLAINTEXT_LENGTH 72 - -#define CIPHERTEXT_LENGTH 128 - - -/// Warning: This version of SWAP64(n) is slow and avoid bugs on AMD GPUs(7970) -#define SWAP64(n) as_ulong(as_uchar8(n).s76543210) - -/* -#define SWAP64(n) \ - (((n) << 56) \ - | (((n) & 0xff00) << 40) \ - | (((n) & 0xff0000) << 24) \ - | (((n) & 0xff000000) << 8) \ - | (((n) >> 8) & 0xff000000) \ - | (((n) >> 24) & 0xff0000) \ - | (((n) >> 40) & 0xff00) \ - | ((n) >> 56)) -*/ - - - -#define rol(x,n) ((x << n) | (x >> (64-n))) -#define ror(x,n) ((x >> n) | (x << (64-n))) -#define Ch(x,y,z) ((x & y) ^ ( (~x) & z)) -#define Maj(x,y,z) ((x & y) ^ (x & z) ^ (y & z)) -#define Sigma0(x) ((ror(x,28)) ^ (ror(x,34)) ^ (ror(x,39))) -#define Sigma1(x) ((ror(x,14)) ^ (ror(x,18)) ^ (ror(x,41))) -#define sigma0(x) ((ror(x,1)) ^ (ror(x,8)) ^(x>>7)) -#define sigma1(x) ((ror(x,19)) ^ (ror(x,61)) ^(x>>6)) - - - -typedef struct { // notice memory align problem - uint64_t H[8]; - uint32_t buffer[32]; //1024 bits - uint32_t buflen; -} sha512_ctx; - -typedef struct { - uint64_t target; - char v[PLAINTEXT_LENGTH+1]; -} sha512_key; - - -/* Macros for reading/writing chars from int32's */ -#define PUTCHAR(buf, index, val) (buf)[(index)>>2] = ((buf)[(index)>>2] & ~(0xffU << (((index) & 3) << 3))) + ((val) << (((index) & 3) << 3)) - - -__constant uint64_t k[] = { - 0x428a2f98d728ae22UL, 0x7137449123ef65cdUL, 0xb5c0fbcfec4d3b2fUL, - 0xe9b5dba58189dbbcUL, - 0x3956c25bf348b538UL, 0x59f111f1b605d019UL, 0x923f82a4af194f9bUL, - 0xab1c5ed5da6d8118UL, - 0xd807aa98a3030242UL, 0x12835b0145706fbeUL, 0x243185be4ee4b28cUL, - 0x550c7dc3d5ffb4e2UL, - 0x72be5d74f27b896fUL, 0x80deb1fe3b1696b1UL, 0x9bdc06a725c71235UL, - 0xc19bf174cf692694UL, - 0xe49b69c19ef14ad2UL, 0xefbe4786384f25e3UL, 0x0fc19dc68b8cd5b5UL, - 0x240ca1cc77ac9c65UL, - 0x2de92c6f592b0275UL, 0x4a7484aa6ea6e483UL, 0x5cb0a9dcbd41fbd4UL, - 0x76f988da831153b5UL, - 0x983e5152ee66dfabUL, 0xa831c66d2db43210UL, 0xb00327c898fb213fUL, - 0xbf597fc7beef0ee4UL, - 0xc6e00bf33da88fc2UL, 0xd5a79147930aa725UL, 0x06ca6351e003826fUL, - 0x142929670a0e6e70UL, - 0x27b70a8546d22ffcUL, 0x2e1b21385c26c926UL, 0x4d2c6dfc5ac42aedUL, - 0x53380d139d95b3dfUL, - 0x650a73548baf63deUL, 0x766a0abb3c77b2a8UL, 0x81c2c92e47edaee6UL, - 0x92722c851482353bUL, - 0xa2bfe8a14cf10364UL, 0xa81a664bbc423001UL, 0xc24b8b70d0f89791UL, - 0xc76c51a30654be30UL, - 0xd192e819d6ef5218UL, 0xd69906245565a910UL, 0xf40e35855771202aUL, - 0x106aa07032bbd1b8UL, - 0x19a4c116b8d2d0c8UL, 0x1e376c085141ab53UL, 0x2748774cdf8eeb99UL, - 0x34b0bcb5e19b48a8UL, - 0x391c0cb3c5c95a63UL, 0x4ed8aa4ae3418acbUL, 0x5b9cca4f7763e373UL, - 0x682e6ff3d6b2b8a3UL, - 0x748f82ee5defb2fcUL, 0x78a5636f43172f60UL, 0x84c87814a1f0ab72UL, - 0x8cc702081a6439ecUL, - 0x90befffa23631e28UL, 0xa4506cebde82bde9UL, 0xbef9a3f7b2c67915UL, - 0xc67178f2e372532bUL, - 0xca273eceea26619cUL, 0xd186b8c721c0c207UL, 0xeada7dd6cde0eb1eUL, - 0xf57d4f7fee6ed178UL, - 0x06f067aa72176fbaUL, 0x0a637dc5a2c898a6UL, 0x113f9804bef90daeUL, - 0x1b710b35131c471bUL, - 0x28db77f523047d84UL, 0x32caab7b40c72493UL, 0x3c9ebe0a15c9bebcUL, - 0x431d67c49c100d4cUL, - 0x4cc5d4becb3e42b6UL, 0x597f299cfc657e2aUL, 0x5fcb6fab3ad6faecUL, - 0x6c44198c4a475817UL, -}; - - - -void setup_ctx(sha512_ctx* ctx, const char * password, uint8_t pass_len) -{ - uint32_t* b32 = ctx->buffer; - - //set password to buffer - for (uint32_t i = 0; i < pass_len; i++) { - PUTCHAR(b32,i,password[i]); - } - ctx->buflen = pass_len; - - //append 1 to ctx buffer - uint32_t length = ctx->buflen; - PUTCHAR(b32, length, 0x80); - while((++length & 3) != 0) { - PUTCHAR(b32, length, 0); - } - - uint32_t* buffer32 = b32+(length>>2); - for(uint32_t i = length; i < 128; i+=4) {// append 0 to 128 - *buffer32++=0; - } - - //append length to buffer - uint64_t *buffer64 = (uint64_t *)ctx->buffer; - buffer64[15] = SWAP64(((uint64_t) ctx->buflen) * 8); -} - -inline uint64_t sha512(char* password) -{ - __private sha512_ctx ctx; - setup_ctx(&ctx, password, 72); - // sha512 main` - int i; - - uint64_t a = 0x6a09e667f3bcc908UL; - uint64_t b = 0xbb67ae8584caa73bUL; - uint64_t c = 0x3c6ef372fe94f82bUL; - uint64_t d = 0xa54ff53a5f1d36f1UL; - uint64_t e = 0x510e527fade682d1UL; - uint64_t f = 0x9b05688c2b3e6c1fUL; - uint64_t g = 0x1f83d9abfb41bd6bUL; - uint64_t h = 0x5be0cd19137e2179UL; - - __private uint64_t w[16]; - - uint64_t *data = (uint64_t *) ctx.buffer; - - for (i = 0; i < 16; i++) - w[i] = SWAP64(data[i]); - - uint64_t t1, t2; - for (i = 0; i < 16; i++) { - t1 = k[i] + w[i] + h + Sigma1(e) + Ch(e, f, g); - t2 = Maj(a, b, c) + Sigma0(a); - - h = g; - g = f; - f = e; - e = d + t1; - d = c; - c = b; - b = a; - a = t1 + t2; - } - - for (i = 16; i < 80; i++) { - - w[i & 15] =sigma1(w[(i - 2) & 15]) + sigma0(w[(i - 15) & 15]) + w[(i -16) & 15] + w[(i - 7) & 15]; - t1 = k[i] + w[i & 15] + h + Sigma1(e) + Ch(e, f, g); - t2 = Maj(a, b, c) + Sigma0(a); - - h = g; - g = f; - f = e; - e = d + t1; - d = c; - c = b; - b = a; - a = t1 + t2; - } - - uint64_t finalhash[8]; - - finalhash[0] = SWAP64(a + 0x6a09e667f3bcc908UL); - finalhash[1] = SWAP64(b + 0xbb67ae8584caa73bUL); - finalhash[2] = SWAP64(c + 0x3c6ef372fe94f82bUL); - finalhash[3] = SWAP64(d + 0xa54ff53a5f1d36f1UL); - finalhash[4] = SWAP64(e + 0x510e527fade682d1UL); - finalhash[5] = SWAP64(f + 0x9b05688c2b3e6c1fUL); - finalhash[6] = SWAP64(g + 0x1f83d9abfb41bd6bUL); - finalhash[7] = SWAP64(h + 0x5be0cd19137e2179UL); - - setup_ctx(&ctx, (char*) finalhash, 64); - - a = 0x6a09e667f3bcc908UL; - b = 0xbb67ae8584caa73bUL; - c = 0x3c6ef372fe94f82bUL; - d = 0xa54ff53a5f1d36f1UL; - e = 0x510e527fade682d1UL; - f = 0x9b05688c2b3e6c1fUL; - g = 0x1f83d9abfb41bd6bUL; - h = 0x5be0cd19137e2179UL; - - data = (uint64_t *) ctx.buffer; - //((uint64_t*)ctx.buffer)[8] = SWAP64((uint64_t)0x80); - - for (i = 0; i < 16; i++) - w[i] = SWAP64(data[i]); - - for (i = 0; i < 16; i++) { - t1 = k[i] + w[i] + h + Sigma1(e) + Ch(e, f, g); - t2 = Maj(a, b, c) + Sigma0(a); - - h = g; - g = f; - f = e; - e = d + t1; - d = c; - c = b; - b = a; - a = t1 + t2; - } - - for (i = 16; i < 80; i++) { - - w[i & 15] =sigma1(w[(i - 2) & 15]) + sigma0(w[(i - 15) & 15]) + w[(i -16) & 15] + w[(i - 7) & 15]; - t1 = k[i] + w[i & 15] + h + Sigma1(e) + Ch(e, f, g); - t2 = Maj(a, b, c) + Sigma0(a); - - h = g; - g = f; - f = e; - e = d + t1; - d = c; - c = b; - b = a; - a = t1 + t2; - } - return SWAP64(a + 0x6a09e667f3bcc908UL); -} - -__kernel void kernel_sha512(__global const sha512_key *password,__global uint64_t *hash, uint64_t start) -{ - uint64_t idx = get_global_id(0); - if (idx == 0 && start == 0) { - *hash = 0; - } - uint64_t winval; - - uint64_t junk[9]; - - __global uint64_t * source = (__global uint64_t*) password->v; - for (int i = 1; i < 9; i++) { - junk[i] = source[i]; - } - - junk[0] = SWAP64(idx + (start)); - - winval = sha512((char*)junk); - if (SWAP64(winval) < password->target) { - *hash = SWAP64(junk[0]); - } -} - +/* +* This is based on the John The Ripper SHA512 code, modified for double SHA512 and for use as a miner in Bitmessage. +* This software is originally Copyright (c) 2012 Myrice +* and it is hereby released to the general public under the following terms: +* Redistribution and use in source and binary forms, with or without modification, are permitted. +*/ + +#ifdef cl_khr_byte_addressable_store +#pragma OPENCL EXTENSION cl_khr_byte_addressable_store : disable +#endif + +#define uint8_t unsigned char +#define uint32_t unsigned int +#define uint64_t unsigned long +#define SALT_SIZE 0 + +#define BINARY_SIZE 8 +#define FULL_BINARY_SIZE 64 + + +#define PLAINTEXT_LENGTH 72 + +#define CIPHERTEXT_LENGTH 128 + + +/// Warning: This version of SWAP64(n) is slow and avoid bugs on AMD GPUs(7970) +#define SWAP64(n) as_ulong(as_uchar8(n).s76543210) + +/* +#define SWAP64(n) \ + (((n) << 56) \ + | (((n) & 0xff00) << 40) \ + | (((n) & 0xff0000) << 24) \ + | (((n) & 0xff000000) << 8) \ + | (((n) >> 8) & 0xff000000) \ + | (((n) >> 24) & 0xff0000) \ + | (((n) >> 40) & 0xff00) \ + | ((n) >> 56)) +*/ + + + +#define rol(x,n) ((x << n) | (x >> (64-n))) +#define ror(x,n) ((x >> n) | (x << (64-n))) +#define Ch(x,y,z) ((x & y) ^ ( (~x) & z)) +#define Maj(x,y,z) ((x & y) ^ (x & z) ^ (y & z)) +#define Sigma0(x) ((ror(x,28)) ^ (ror(x,34)) ^ (ror(x,39))) +#define Sigma1(x) ((ror(x,14)) ^ (ror(x,18)) ^ (ror(x,41))) +#define sigma0(x) ((ror(x,1)) ^ (ror(x,8)) ^(x>>7)) +#define sigma1(x) ((ror(x,19)) ^ (ror(x,61)) ^(x>>6)) + + + +typedef struct { // notice memory align problem + uint64_t H[8]; + uint32_t buffer[32]; //1024 bits + uint32_t buflen; +} sha512_ctx; + +typedef struct { + uint64_t target; + char v[PLAINTEXT_LENGTH+1]; +} sha512_key; + + +/* Macros for reading/writing chars from int32's */ +#define PUTCHAR(buf, index, val) (buf)[(index)>>2] = ((buf)[(index)>>2] & ~(0xffU << (((index) & 3) << 3))) + ((val) << (((index) & 3) << 3)) + + +__constant uint64_t k[] = { + 0x428a2f98d728ae22UL, 0x7137449123ef65cdUL, 0xb5c0fbcfec4d3b2fUL, + 0xe9b5dba58189dbbcUL, + 0x3956c25bf348b538UL, 0x59f111f1b605d019UL, 0x923f82a4af194f9bUL, + 0xab1c5ed5da6d8118UL, + 0xd807aa98a3030242UL, 0x12835b0145706fbeUL, 0x243185be4ee4b28cUL, + 0x550c7dc3d5ffb4e2UL, + 0x72be5d74f27b896fUL, 0x80deb1fe3b1696b1UL, 0x9bdc06a725c71235UL, + 0xc19bf174cf692694UL, + 0xe49b69c19ef14ad2UL, 0xefbe4786384f25e3UL, 0x0fc19dc68b8cd5b5UL, + 0x240ca1cc77ac9c65UL, + 0x2de92c6f592b0275UL, 0x4a7484aa6ea6e483UL, 0x5cb0a9dcbd41fbd4UL, + 0x76f988da831153b5UL, + 0x983e5152ee66dfabUL, 0xa831c66d2db43210UL, 0xb00327c898fb213fUL, + 0xbf597fc7beef0ee4UL, + 0xc6e00bf33da88fc2UL, 0xd5a79147930aa725UL, 0x06ca6351e003826fUL, + 0x142929670a0e6e70UL, + 0x27b70a8546d22ffcUL, 0x2e1b21385c26c926UL, 0x4d2c6dfc5ac42aedUL, + 0x53380d139d95b3dfUL, + 0x650a73548baf63deUL, 0x766a0abb3c77b2a8UL, 0x81c2c92e47edaee6UL, + 0x92722c851482353bUL, + 0xa2bfe8a14cf10364UL, 0xa81a664bbc423001UL, 0xc24b8b70d0f89791UL, + 0xc76c51a30654be30UL, + 0xd192e819d6ef5218UL, 0xd69906245565a910UL, 0xf40e35855771202aUL, + 0x106aa07032bbd1b8UL, + 0x19a4c116b8d2d0c8UL, 0x1e376c085141ab53UL, 0x2748774cdf8eeb99UL, + 0x34b0bcb5e19b48a8UL, + 0x391c0cb3c5c95a63UL, 0x4ed8aa4ae3418acbUL, 0x5b9cca4f7763e373UL, + 0x682e6ff3d6b2b8a3UL, + 0x748f82ee5defb2fcUL, 0x78a5636f43172f60UL, 0x84c87814a1f0ab72UL, + 0x8cc702081a6439ecUL, + 0x90befffa23631e28UL, 0xa4506cebde82bde9UL, 0xbef9a3f7b2c67915UL, + 0xc67178f2e372532bUL, + 0xca273eceea26619cUL, 0xd186b8c721c0c207UL, 0xeada7dd6cde0eb1eUL, + 0xf57d4f7fee6ed178UL, + 0x06f067aa72176fbaUL, 0x0a637dc5a2c898a6UL, 0x113f9804bef90daeUL, + 0x1b710b35131c471bUL, + 0x28db77f523047d84UL, 0x32caab7b40c72493UL, 0x3c9ebe0a15c9bebcUL, + 0x431d67c49c100d4cUL, + 0x4cc5d4becb3e42b6UL, 0x597f299cfc657e2aUL, 0x5fcb6fab3ad6faecUL, + 0x6c44198c4a475817UL, +}; + + + +void setup_ctx(sha512_ctx* ctx, const char * password, uint8_t pass_len) +{ + uint32_t* b32 = ctx->buffer; + + //set password to buffer + for (uint32_t i = 0; i < pass_len; i++) { + PUTCHAR(b32,i,password[i]); + } + ctx->buflen = pass_len; + + //append 1 to ctx buffer + uint32_t length = ctx->buflen; + PUTCHAR(b32, length, 0x80); + while((++length & 3) != 0) { + PUTCHAR(b32, length, 0); + } + + uint32_t* buffer32 = b32+(length>>2); + for(uint32_t i = length; i < 128; i+=4) {// append 0 to 128 + *buffer32++=0; + } + + //append length to buffer + uint64_t *buffer64 = (uint64_t *)ctx->buffer; + buffer64[15] = SWAP64(((uint64_t) ctx->buflen) * 8); +} + +inline uint64_t sha512(char* password) +{ + __private sha512_ctx ctx; + setup_ctx(&ctx, password, 72); + // sha512 main` + int i; + + uint64_t a = 0x6a09e667f3bcc908UL; + uint64_t b = 0xbb67ae8584caa73bUL; + uint64_t c = 0x3c6ef372fe94f82bUL; + uint64_t d = 0xa54ff53a5f1d36f1UL; + uint64_t e = 0x510e527fade682d1UL; + uint64_t f = 0x9b05688c2b3e6c1fUL; + uint64_t g = 0x1f83d9abfb41bd6bUL; + uint64_t h = 0x5be0cd19137e2179UL; + + __private uint64_t w[16]; + + uint64_t *data = (uint64_t *) ctx.buffer; + + for (i = 0; i < 16; i++) + w[i] = SWAP64(data[i]); + + uint64_t t1, t2; + for (i = 0; i < 16; i++) { + t1 = k[i] + w[i] + h + Sigma1(e) + Ch(e, f, g); + t2 = Maj(a, b, c) + Sigma0(a); + + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + } + + for (i = 16; i < 80; i++) { + + w[i & 15] =sigma1(w[(i - 2) & 15]) + sigma0(w[(i - 15) & 15]) + w[(i -16) & 15] + w[(i - 7) & 15]; + t1 = k[i] + w[i & 15] + h + Sigma1(e) + Ch(e, f, g); + t2 = Maj(a, b, c) + Sigma0(a); + + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + } + + uint64_t finalhash[8]; + + finalhash[0] = SWAP64(a + 0x6a09e667f3bcc908UL); + finalhash[1] = SWAP64(b + 0xbb67ae8584caa73bUL); + finalhash[2] = SWAP64(c + 0x3c6ef372fe94f82bUL); + finalhash[3] = SWAP64(d + 0xa54ff53a5f1d36f1UL); + finalhash[4] = SWAP64(e + 0x510e527fade682d1UL); + finalhash[5] = SWAP64(f + 0x9b05688c2b3e6c1fUL); + finalhash[6] = SWAP64(g + 0x1f83d9abfb41bd6bUL); + finalhash[7] = SWAP64(h + 0x5be0cd19137e2179UL); + + setup_ctx(&ctx, (char*) finalhash, 64); + + a = 0x6a09e667f3bcc908UL; + b = 0xbb67ae8584caa73bUL; + c = 0x3c6ef372fe94f82bUL; + d = 0xa54ff53a5f1d36f1UL; + e = 0x510e527fade682d1UL; + f = 0x9b05688c2b3e6c1fUL; + g = 0x1f83d9abfb41bd6bUL; + h = 0x5be0cd19137e2179UL; + + data = (uint64_t *) ctx.buffer; + //((uint64_t*)ctx.buffer)[8] = SWAP64((uint64_t)0x80); + + for (i = 0; i < 16; i++) + w[i] = SWAP64(data[i]); + + for (i = 0; i < 16; i++) { + t1 = k[i] + w[i] + h + Sigma1(e) + Ch(e, f, g); + t2 = Maj(a, b, c) + Sigma0(a); + + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + } + + for (i = 16; i < 80; i++) { + + w[i & 15] =sigma1(w[(i - 2) & 15]) + sigma0(w[(i - 15) & 15]) + w[(i -16) & 15] + w[(i - 7) & 15]; + t1 = k[i] + w[i & 15] + h + Sigma1(e) + Ch(e, f, g); + t2 = Maj(a, b, c) + Sigma0(a); + + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + } + return SWAP64(a + 0x6a09e667f3bcc908UL); +} + +__kernel void kernel_sha512(__global const sha512_key *password,__global uint64_t *hash, uint64_t start) +{ + uint64_t idx = get_global_id(0); + if (idx == 0 && start == 0) { + *hash = 0; + } + uint64_t winval; + + uint64_t junk[9]; + + __global uint64_t * source = (__global uint64_t*) password->v; + for (int i = 1; i < 9; i++) { + junk[i] = source[i]; + } + + junk[0] = SWAP64(idx + (start)); + + winval = sha512((char*)junk); + if (SWAP64(winval) < password->target) { + *hash = SWAP64(junk[0]); + } +} + From 425667ee31569e2474e78111e43d94d9d4877050 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sun, 8 Nov 2015 11:18:37 +0100 Subject: [PATCH 106/210] OpenCL compile warning --- src/bitmsghash/bitmsghash.cl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitmsghash/bitmsghash.cl b/src/bitmsghash/bitmsghash.cl index 9af7ca19..1ab5d219 100644 --- a/src/bitmsghash/bitmsghash.cl +++ b/src/bitmsghash/bitmsghash.cl @@ -112,7 +112,7 @@ __constant uint64_t k[] = { -void setup_ctx(sha512_ctx* ctx, const char * password, uint8_t pass_len) +static void setup_ctx(sha512_ctx* ctx, const char * password, uint8_t pass_len) { uint32_t* b32 = ctx->buffer; From 15200356fef93537690db49450f55034f6676a0d Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sun, 8 Nov 2015 11:22:17 +0100 Subject: [PATCH 107/210] OSX fixes and default compile It should built on OSX 10.11 now It will build bitmsghash.so by default --- src/bitmsghash/Makefile | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/bitmsghash/Makefile b/src/bitmsghash/Makefile index 3c9c7470..88744524 100644 --- a/src/bitmsghash/Makefile +++ b/src/bitmsghash/Makefile @@ -1,14 +1,19 @@ -all: - @echo "This compiles and tests the C PoW library, it's not necessary to run PyBitmessage" +UNAME_S := $(shell uname -s) +ifeq ($(UNAME_S),Darwin) + CCFLAGS += -I/usr/local/Cellar/openssl/1.0.2d_1/include + LDFLAGS += -L/usr/local/Cellar/openssl/1.0.2d_1/lib +endif + +all: bitmsghash.so powtest: ./testpow.py bitmsghash.so: bitmsghash.o - g++ bitmsghash.o -shared -fPIC -lpthread -lcrypto -o bitmsghash.so + g++ bitmsghash.o -shared -fPIC -lpthread -lcrypto $(LDFLAGS) -o bitmsghash.so bitmsghash.o: - g++ -Wall -O3 -march=native -fPIC -c bitmsghash.cpp + g++ -Wall -O3 -march=native -fPIC $(CCFLAGS) -c bitmsghash.cpp clean: rm -f bitmsghash.o bitmsghash.so From e78c5820af072b4a08d389850cc0ceb80841d28d Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sun, 8 Nov 2015 11:23:41 +0100 Subject: [PATCH 108/210] OpenCL fixes - directory change - automatically detect all available GPUs --- src/openclpow.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/openclpow.py b/src/openclpow.py index 7ab4dba8..a2cfb068 100644 --- a/src/openclpow.py +++ b/src/openclpow.py @@ -13,15 +13,21 @@ try: import numpy import pyopencl as cl hash_dt = numpy.dtype([('target', numpy.uint64), ('v', numpy.str_, 73)]) - if (len(cl.get_platforms()) > 0): - ctx = cl.create_some_context() + gpus = [] + for platform in cl.get_platforms(): + gpus.extend(platform.get_devices(device_type=cl.device_type.GPU)) + if (len(gpus) > 0): + ctx = cl.Context(devices=gpus) queue = cl.CommandQueue(ctx) full_path = os.path.dirname(os.path.realpath(__file__)) - f = open(os.path.join(full_path, 'kernel.cl'), 'r') + f = open(os.path.join(full_path, "bitmsghash", 'bitmsghash.cl'), 'r') fstr = ''.join(f.readlines()) program = cl.Program(ctx, fstr).build(options="") + else: + print "No OpenCL GPUs found" + ctx = False except Exception as e: - print "opencl fail:" + str(e) + print "opencl fail: " + str(e) ctx = False def has_opencl(): @@ -40,7 +46,7 @@ def do_opencl_pow(hash, target): dest_buf = cl.Buffer(ctx, cl.mem_flags.WRITE_ONLY, output.nbytes) kernel = program.kernel_sha512 - worksize = kernel.get_work_group_info(cl.kernel_work_group_info.WORK_GROUP_SIZE, cl.get_platforms()[0].get_devices()[1]) + worksize = kernel.get_work_group_info(cl.kernel_work_group_info.WORK_GROUP_SIZE, gpus[0]) kernel.set_arg(0, hash_buf) kernel.set_arg(1, dest_buf) From c81b23ff54d90359d5d06ca0dd19640d09cffc04 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sun, 8 Nov 2015 11:26:17 +0100 Subject: [PATCH 109/210] Compile fixes - OSX and Windows compile fixes --- src/bitmsghash/bitmsghash.cpp | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/src/bitmsghash/bitmsghash.cpp b/src/bitmsghash/bitmsghash.cpp index c0a40509..4862c341 100644 --- a/src/bitmsghash/bitmsghash.cpp +++ b/src/bitmsghash/bitmsghash.cpp @@ -11,6 +11,10 @@ #include #include #include +#ifdef __APPLE__ +#include +#include +#endif #include "openssl/sha.h" @@ -23,7 +27,9 @@ #define EXPORT __declspec(dllexport) #endif +#ifndef __APPLE__ #define ntohll(x) ( ( (uint64_t)(ntohl( (unsigned int)((x << 32) >> 32) )) << 32) | ntohl( ((unsigned int)(x >> 32)) ) ) +#endif unsigned long long max_val; unsigned char *initialHash; @@ -31,11 +37,11 @@ unsigned long long successval = 0; unsigned int numthreads = 0; #ifdef _WIN32 -DWORD WINAPI threadfunc(LPVOID lpParameter) { +DWORD WINAPI threadfunc(LPVOID param) { #else void * threadfunc(void* param) { #endif - unsigned int incamt = *((unsigned int*)lpParameter); + unsigned int incamt = *((unsigned int*)param); SHA512_CTX sha; unsigned char buf[HASH_SIZE + sizeof(uint64_t)] = { 0 }; unsigned char output[HASH_SIZE] = { 0 }; @@ -73,7 +79,7 @@ void getnumthreads() int dwProcessAffinity = 0; int32_t core_count = 0; #endif - unsigned int len = sizeof(dwProcessAffinity); + size_t len = sizeof(dwProcessAffinity); if (numthreads > 0) return; #ifdef _WIN32 @@ -81,15 +87,20 @@ void getnumthreads() #elif __linux__ sched_getaffinity(0, len, &dwProcessAffinity); #else - if (sysctlbyname("hw.logicalcpu", &core_count, &len, 0, 0)) + if (sysctlbyname("hw.logicalcpu", &core_count, &len, 0, 0) == 0) numthreads = core_count; #endif for (unsigned int i = 0; i < len * 8; i++) +#if defined(_WIN32) if (dwProcessAffinity & (1i64 << i)) { +#elif defined __linux__ + if (CPU_ISSET(i, &dwProcessAffinity)) { +#else + if (dwProcessAffinity & (1 << i)) { +#endif numthreads++; printf("Detected core on: %u\n", i); } - printf("Affinity: %lx\n", (unsigned long) dwProcessAffinity); printf("Number of threads: %i\n", (int)numthreads); } @@ -104,14 +115,10 @@ extern "C" EXPORT unsigned long long BitmessagePOW(unsigned char * starthash, un # else pthread_t* threads = (pthread_t*)calloc(sizeof(pthread_t), numthreads); struct sched_param schparam; -# ifdef __linux__ schparam.sched_priority = 0; -# else - schparam.sched_priority = PTHREAD_MIN_PRIORITY; -# endif # endif unsigned int *threaddata = (unsigned int *)calloc(sizeof(unsigned int), numthreads); - for (UINT i = 0; i < numthreads; i++) { + for (unsigned int i = 0; i < numthreads; i++) { threaddata[i] = i; # ifdef _WIN32 threads[i] = CreateThread(NULL, 0, threadfunc, (LPVOID)&threaddata[i], 0, NULL); @@ -121,14 +128,14 @@ extern "C" EXPORT unsigned long long BitmessagePOW(unsigned char * starthash, un # ifdef __linux__ pthread_setschedparam(threads[i], SCHED_IDLE, &schparam); # else - pthread_setschedparam(threads[i], SCHED_RR, &schparam) + pthread_setschedparam(threads[i], SCHED_RR, &schparam); # endif # endif } # ifdef _WIN32 WaitForMultipleObjects(numthreads, threads, TRUE, INFINITE); # else - for (int i = 0; i < numthreads; i++) { + for (unsigned int i = 0; i < numthreads; i++) { pthread_join(threads[i], NULL); } # endif From 90db7c9ef1c845de582afe7ec1a1719f4a614664 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sun, 8 Nov 2015 11:44:19 +0100 Subject: [PATCH 110/210] Missing library fix Missing library error wasn't handled correctly. --- src/proofofwork.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/proofofwork.py b/src/proofofwork.py index 0aaf6487..f7776638 100644 --- a/src/proofofwork.py +++ b/src/proofofwork.py @@ -32,6 +32,8 @@ if bso: bmpow.restype = ctypes.c_ulonglong except: bmpow = None +else: + bmpow = None def _set_idle(): if 'linux' in sys.platform: From ed02e68a2221860b26153fbc9eb9ff5b3be96727 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sun, 8 Nov 2015 21:44:02 +0100 Subject: [PATCH 111/210] More tabs resizable and save state Fixes #15 Addresses #12 --- src/bitmessageqt/__init__.py | 48 ++-- src/bitmessageqt/bitmessageui.py | 352 +++++++++++++++++++----------- src/bitmessageqt/settingsmixin.py | 69 ++++++ 3 files changed, 318 insertions(+), 151 deletions(-) create mode 100644 src/bitmessageqt/settingsmixin.py diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index ce5e3fc4..9bc69d79 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -36,6 +36,7 @@ from newchandialog import * from specialaddressbehavior import * from emailgateway import * from settings import * +import settingsmixin from about import * from help import * from iconglossary import * @@ -55,6 +56,7 @@ import subprocess import datetime from helper_sql import * import l10n +import types from utils import * from collections import OrderedDict from account import * @@ -73,7 +75,7 @@ def change_translation(locale): qtranslator.load(translationpath) QtGui.QApplication.installTranslator(qtranslator) -class MyForm(QtGui.QMainWindow): +class MyForm(settingsmixin.SMainWindow): # sound type constants SOUND_NONE = 0 @@ -701,9 +703,9 @@ class MyForm(QtGui.QMainWindow): QtCore.QObject.connect(self.ui.horizontalSliderTTL, QtCore.SIGNAL( "valueChanged(int)"), self.updateTTL) - - self.readSettings() - + + self.initSettings() + # Check to see whether we can connect to namecoin. Hide the 'Fetch Namecoin ID' button if we can't. try: options = {} @@ -2860,9 +2862,9 @@ class MyForm(QtGui.QMainWindow): if self.mmapp is not None: self.mmapp.unregister() - settings = QSettings("Bitmessage", "PyBitmessage") - settings.setValue("geometry", self.saveGeometry()) - settings.setValue("state", self.saveState()) +# settings = QSettings("Bitmessage", "PyBitmessage") +# settings.setValue("geometry", self.saveGeometry()) +# settings.setValue("state", self.saveState()) self.statusBar().showMessage(_translate( "MainWindow", "All done. Closing user interface...")) @@ -2883,6 +2885,13 @@ class MyForm(QtGui.QMainWindow): # minimize the application event.ignore() else: + # save state and geometry self and all widgets + self.saveSettings() + for attr, obj in self.ui.__dict__.iteritems(): + if hasattr(obj, "__class__") and isinstance(obj, settingsmixin.SettingsMixin): + saveMethod = getattr(obj, "saveSettings", None) + if callable (saveMethod): + obj.saveSettings() # quit the application event.accept() self.quit() @@ -3887,20 +3896,17 @@ class MyForm(QtGui.QMainWindow): self.statusBar().showMessage(data) - def readSettings(self): - settings = QSettings("Bitmessage", "PyBitmessage") - try: - geom = settings.value("geometry") - self.restoreGeometry(geom.toByteArray() if hasattr(geom, 'toByteArray') else geom) - except Exception as e: - pass - - try: - state = settings.value("state") - self.restoreState(state.toByteArray() if hasattr(state, 'toByteArray') else state) - except Exception as e: - pass - + def initSettings(self): + QtCore.QCoreApplication.setOrganizationName("PyBitmessage") + QtCore.QCoreApplication.setOrganizationDomain("bitmessage.org") + QtCore.QCoreApplication.setApplicationName("pybitmessageqt") + self.loadSettings() + for attr, obj in self.ui.__dict__.iteritems(): + if hasattr(obj, "__class__") and isinstance(obj, settingsmixin.SettingsMixin): + loadMethod = getattr(obj, "loadSettings", None) + if callable (loadMethod): + obj.loadSettings() + class helpDialog(QtGui.QDialog): diff --git a/src/bitmessageqt/bitmessageui.py b/src/bitmessageqt/bitmessageui.py index 26493241..0e719c17 100644 --- a/src/bitmessageqt/bitmessageui.py +++ b/src/bitmessageqt/bitmessageui.py @@ -8,6 +8,7 @@ # WARNING! All changes made in this file will be lost! from PyQt4 import QtCore, QtGui +import settingsmixin try: _fromUtf8 = QtCore.QString.fromUtf8 @@ -53,9 +54,9 @@ class Ui_MainWindow(object): self.inbox.setObjectName(_fromUtf8("inbox")) self.gridLayout = QtGui.QGridLayout(self.inbox) self.gridLayout.setObjectName(_fromUtf8("gridLayout")) - self.horizontalSplitter_3 = QtGui.QSplitter() + self.horizontalSplitter_3 = settingsmixin.SSplitter() self.horizontalSplitter_3.setObjectName(_fromUtf8("horizontalSplitter_3")) - self.verticalSplitter_12 = QtGui.QSplitter() + self.verticalSplitter_12 = settingsmixin.SSplitter() self.verticalSplitter_12.setObjectName(_fromUtf8("verticalSplitter_12")) self.verticalSplitter_12.setOrientation(QtCore.Qt.Vertical) self.treeWidgetYourIdentities = QtGui.QTreeWidget(self.inbox) @@ -75,10 +76,10 @@ class Ui_MainWindow(object): self.verticalSplitter_12.setCollapsible(1, False) self.verticalSplitter_12.handle(1).setEnabled(False) self.horizontalSplitter_3.addWidget(self.verticalSplitter_12) - self.verticalSplitter_7 = QtGui.QSplitter() + self.verticalSplitter_7 = settingsmixin.SSplitter() self.verticalSplitter_7.setObjectName(_fromUtf8("verticalSplitter_7")) self.verticalSplitter_7.setOrientation(QtCore.Qt.Vertical) - self.horizontalSplitterSearch = QtGui.QSplitter() + self.horizontalSplitterSearch = settingsmixin.SSplitter() self.horizontalSplitterSearch.setObjectName(_fromUtf8("horizontalSplitterSearch")) self.inboxSearchLineEdit = QtGui.QLineEdit(self.inbox) self.inboxSearchLineEdit.setObjectName(_fromUtf8("inboxSearchLineEdit")) @@ -96,7 +97,7 @@ class Ui_MainWindow(object): self.horizontalSplitterSearch.setStretchFactor(0, 1) self.horizontalSplitterSearch.setStretchFactor(1, 0) self.verticalSplitter_7.addWidget(self.horizontalSplitterSearch) - self.tableWidgetInbox = QtGui.QTableWidget(self.inbox) + self.tableWidgetInbox = settingsmixin.STableWidget(self.inbox) self.tableWidgetInbox.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers) self.tableWidgetInbox.setAlternatingRowColors(True) self.tableWidgetInbox.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection) @@ -147,18 +148,19 @@ class Ui_MainWindow(object): self.send.setObjectName(_fromUtf8("send")) self.gridLayout_7 = QtGui.QGridLayout(self.send) self.gridLayout_7.setObjectName(_fromUtf8("gridLayout_7")) - self.horizontalLayout = QtGui.QHBoxLayout() - self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout")) - self.verticalLayout_2 = QtGui.QVBoxLayout() - self.verticalLayout_2.setObjectName(_fromUtf8("verticalLayout_2")) - self.tableWidgetAddressBook = QtGui.QTableWidget(self.send) - self.tableWidgetAddressBook.setMaximumSize(QtCore.QSize(200, 16777215)) + self.horizontalSplitter = settingsmixin.SSplitter() + self.horizontalSplitter.setObjectName(_fromUtf8("horizontalSplitter")) + self.verticalSplitter_2 = settingsmixin.SSplitter() + self.verticalSplitter_2.setObjectName(_fromUtf8("verticalSplitter_2")) + self.verticalSplitter_2.setOrientation(QtCore.Qt.Vertical) + self.tableWidgetAddressBook = settingsmixin.STableWidget(self.send) self.tableWidgetAddressBook.setAlternatingRowColors(True) self.tableWidgetAddressBook.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection) self.tableWidgetAddressBook.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) self.tableWidgetAddressBook.setObjectName(_fromUtf8("tableWidgetAddressBook")) self.tableWidgetAddressBook.setColumnCount(2) self.tableWidgetAddressBook.setRowCount(0) + self.tableWidgetAddressBook.resize(200, self.tableWidgetAddressBook.height()) item = QtGui.QTableWidgetItem() icon3 = QtGui.QIcon() icon3.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/addressbook.png")), QtGui.QIcon.Selected, QtGui.QIcon.Off) @@ -171,95 +173,119 @@ class Ui_MainWindow(object): self.tableWidgetAddressBook.horizontalHeader().setHighlightSections(False) self.tableWidgetAddressBook.horizontalHeader().setStretchLastSection(True) self.tableWidgetAddressBook.verticalHeader().setVisible(False) - self.verticalLayout_2.addWidget(self.tableWidgetAddressBook) + self.verticalSplitter_2.addWidget(self.tableWidgetAddressBook) self.pushButtonAddAddressBook = QtGui.QPushButton(self.send) - self.pushButtonAddAddressBook.setMaximumSize(QtCore.QSize(200, 16777215)) self.pushButtonAddAddressBook.setObjectName(_fromUtf8("pushButtonAddAddressBook")) - self.verticalLayout_2.addWidget(self.pushButtonAddAddressBook) + self.pushButtonAddAddressBook.resize(200, self.pushButtonAddAddressBook.height()) + self.verticalSplitter_2.addWidget(self.pushButtonAddAddressBook) self.pushButtonFetchNamecoinID = QtGui.QPushButton(self.send) - self.pushButtonFetchNamecoinID.setMaximumSize(QtCore.QSize(200, 16777215)) + self.pushButtonFetchNamecoinID.resize(200, self.pushButtonFetchNamecoinID.height()) font = QtGui.QFont() font.setPointSize(9) self.pushButtonFetchNamecoinID.setFont(font) self.pushButtonFetchNamecoinID.setObjectName(_fromUtf8("pushButtonFetchNamecoinID")) - self.verticalLayout_2.addWidget(self.pushButtonFetchNamecoinID) - self.horizontalLayout.addLayout(self.verticalLayout_2) - self.verticalLayout = QtGui.QVBoxLayout() - self.verticalLayout.setObjectName(_fromUtf8("verticalLayout")) + self.verticalSplitter_2.addWidget(self.pushButtonFetchNamecoinID) + self.verticalSplitter_2.setStretchFactor(0, 1) + self.verticalSplitter_2.setStretchFactor(1, 0) + self.verticalSplitter_2.setStretchFactor(2, 0) + self.verticalSplitter_2.setCollapsible(0, False) + self.verticalSplitter_2.setCollapsible(1, False) + self.verticalSplitter_2.setCollapsible(2, False) + self.verticalSplitter_2.handle(1).setEnabled(False) + self.verticalSplitter_2.handle(2).setEnabled(False) + self.horizontalSplitter.addWidget(self.verticalSplitter_2) + self.verticalSplitter = settingsmixin.SSplitter() + self.verticalSplitter.setObjectName(_fromUtf8("verticalSplitter")) + self.verticalSplitter.setOrientation(QtCore.Qt.Vertical) self.tabWidgetSend = QtGui.QTabWidget(self.send) self.tabWidgetSend.setObjectName(_fromUtf8("tabWidgetSend")) - self.tab = QtGui.QWidget() - self.tab.setObjectName(_fromUtf8("tab")) - self.gridLayout_8 = QtGui.QGridLayout(self.tab) + self.sendDirect = QtGui.QWidget() + self.sendDirect.setObjectName(_fromUtf8("sendDirect")) + self.gridLayout_8 = QtGui.QGridLayout(self.sendDirect) self.gridLayout_8.setObjectName(_fromUtf8("gridLayout_8")) - self.verticalLayout_5 = QtGui.QVBoxLayout() - self.verticalLayout_5.setObjectName(_fromUtf8("verticalLayout_5")) + self.verticalSplitter_5 = settingsmixin.SSplitter() + self.verticalSplitter_5.setObjectName(_fromUtf8("verticalSplitter_5")) + self.verticalSplitter_5.setOrientation(QtCore.Qt.Vertical) self.gridLayout_2 = QtGui.QGridLayout() self.gridLayout_2.setObjectName(_fromUtf8("gridLayout_2")) - self.label_3 = QtGui.QLabel(self.tab) + self.label_3 = QtGui.QLabel(self.sendDirect) self.label_3.setObjectName(_fromUtf8("label_3")) self.gridLayout_2.addWidget(self.label_3, 2, 0, 1, 1) - self.label_2 = QtGui.QLabel(self.tab) + self.label_2 = QtGui.QLabel(self.sendDirect) self.label_2.setObjectName(_fromUtf8("label_2")) self.gridLayout_2.addWidget(self.label_2, 0, 0, 1, 1) - self.lineEditSubject = QtGui.QLineEdit(self.tab) + self.lineEditSubject = QtGui.QLineEdit(self.sendDirect) self.lineEditSubject.setText(_fromUtf8("")) self.lineEditSubject.setObjectName(_fromUtf8("lineEditSubject")) self.gridLayout_2.addWidget(self.lineEditSubject, 2, 1, 1, 1) - self.label = QtGui.QLabel(self.tab) + self.label = QtGui.QLabel(self.sendDirect) self.label.setObjectName(_fromUtf8("label")) self.gridLayout_2.addWidget(self.label, 1, 0, 1, 1) - self.comboBoxSendFrom = QtGui.QComboBox(self.tab) + self.comboBoxSendFrom = QtGui.QComboBox(self.sendDirect) self.comboBoxSendFrom.setMinimumSize(QtCore.QSize(300, 0)) self.comboBoxSendFrom.setObjectName(_fromUtf8("comboBoxSendFrom")) self.gridLayout_2.addWidget(self.comboBoxSendFrom, 0, 1, 1, 1) - self.lineEditTo = QtGui.QLineEdit(self.tab) + self.lineEditTo = QtGui.QLineEdit(self.sendDirect) self.lineEditTo.setObjectName(_fromUtf8("lineEditTo")) self.gridLayout_2.addWidget(self.lineEditTo, 1, 1, 1, 1) - self.verticalLayout_5.addLayout(self.gridLayout_2) - self.textEditMessage = QtGui.QTextEdit(self.tab) + self.gridLayout_2_Widget = QtGui.QWidget() + self.gridLayout_2_Widget.setLayout(self.gridLayout_2) + self.verticalSplitter_5.addWidget(self.gridLayout_2_Widget) + self.textEditMessage = QtGui.QTextEdit(self.sendDirect) self.textEditMessage.setObjectName(_fromUtf8("textEditMessage")) - self.verticalLayout_5.addWidget(self.textEditMessage) - self.gridLayout_8.addLayout(self.verticalLayout_5, 0, 0, 1, 1) - self.tabWidgetSend.addTab(self.tab, _fromUtf8("")) - self.tab_2 = QtGui.QWidget() - self.tab_2.setObjectName(_fromUtf8("tab_2")) - self.gridLayout_9 = QtGui.QGridLayout(self.tab_2) + self.verticalSplitter_5.addWidget(self.textEditMessage) + self.verticalSplitter_5.setStretchFactor(0, 0) + self.verticalSplitter_5.setStretchFactor(1, 1) + self.verticalSplitter_5.setCollapsible(0, False) + self.verticalSplitter_5.setCollapsible(1, False) + self.verticalSplitter_5.handle(1).setEnabled(False) + self.gridLayout_8.addWidget(self.verticalSplitter_5, 0, 0, 1, 1) + self.tabWidgetSend.addTab(self.sendDirect, _fromUtf8("")) + self.sendBroadcast = QtGui.QWidget() + self.sendBroadcast.setObjectName(_fromUtf8("sendBroadcast")) + self.gridLayout_9 = QtGui.QGridLayout(self.sendBroadcast) self.gridLayout_9.setObjectName(_fromUtf8("gridLayout_9")) - self.verticalLayout_6 = QtGui.QVBoxLayout() - self.verticalLayout_6.setObjectName(_fromUtf8("verticalLayout_6")) + self.verticalSplitter_6 = settingsmixin.SSplitter() + self.verticalSplitter_6.setObjectName(_fromUtf8("verticalSplitter_6")) + self.verticalSplitter_6.setOrientation(QtCore.Qt.Vertical) self.gridLayout_5 = QtGui.QGridLayout() self.gridLayout_5.setObjectName(_fromUtf8("gridLayout_5")) - self.label_8 = QtGui.QLabel(self.tab_2) + self.label_8 = QtGui.QLabel(self.sendBroadcast) self.label_8.setObjectName(_fromUtf8("label_8")) self.gridLayout_5.addWidget(self.label_8, 0, 0, 1, 1) - self.lineEditSubjectBroadcast = QtGui.QLineEdit(self.tab_2) + self.lineEditSubjectBroadcast = QtGui.QLineEdit(self.sendBroadcast) self.lineEditSubjectBroadcast.setText(_fromUtf8("")) self.lineEditSubjectBroadcast.setObjectName(_fromUtf8("lineEditSubjectBroadcast")) self.gridLayout_5.addWidget(self.lineEditSubjectBroadcast, 1, 1, 1, 1) - self.label_7 = QtGui.QLabel(self.tab_2) + self.label_7 = QtGui.QLabel(self.sendBroadcast) self.label_7.setObjectName(_fromUtf8("label_7")) self.gridLayout_5.addWidget(self.label_7, 1, 0, 1, 1) - self.comboBoxSendFromBroadcast = QtGui.QComboBox(self.tab_2) + self.comboBoxSendFromBroadcast = QtGui.QComboBox(self.sendBroadcast) self.comboBoxSendFromBroadcast.setMinimumSize(QtCore.QSize(300, 0)) self.comboBoxSendFromBroadcast.setObjectName(_fromUtf8("comboBoxSendFromBroadcast")) self.gridLayout_5.addWidget(self.comboBoxSendFromBroadcast, 0, 1, 1, 1) - self.verticalLayout_6.addLayout(self.gridLayout_5) - self.textEditMessageBroadcast = QtGui.QTextEdit(self.tab_2) + self.gridLayout_5_Widget = QtGui.QWidget() + self.gridLayout_5_Widget.setLayout(self.gridLayout_5) + self.verticalSplitter_6.addWidget(self.gridLayout_5_Widget) + self.textEditMessageBroadcast = QtGui.QTextEdit(self.sendBroadcast) self.textEditMessageBroadcast.setObjectName(_fromUtf8("textEditMessageBroadcast")) - self.verticalLayout_6.addWidget(self.textEditMessageBroadcast) - self.gridLayout_9.addLayout(self.verticalLayout_6, 0, 0, 1, 1) - self.tabWidgetSend.addTab(self.tab_2, _fromUtf8("")) - self.verticalLayout.addWidget(self.tabWidgetSend) - self.horizontalLayout_5 = QtGui.QHBoxLayout() - self.horizontalLayout_5.setObjectName(_fromUtf8("horizontalLayout_5")) + self.verticalSplitter_6.addWidget(self.textEditMessageBroadcast) + self.verticalSplitter_6.setStretchFactor(0, 0) + self.verticalSplitter_6.setStretchFactor(1, 1) + self.verticalSplitter_6.setCollapsible(0, False) + self.verticalSplitter_6.setCollapsible(1, False) + self.verticalSplitter_6.handle(1).setEnabled(False) + self.gridLayout_9.addWidget(self.verticalSplitter_6, 0, 0, 1, 1) + self.tabWidgetSend.addTab(self.sendBroadcast, _fromUtf8("")) + self.verticalSplitter.addWidget(self.tabWidgetSend) + self.horizontalSplitter_5 = settingsmixin.SSplitter() + self.horizontalSplitter_5.setObjectName(_fromUtf8("horizontalSplitter_5")) self.pushButtonTTL = QtGui.QPushButton(self.send) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.pushButtonTTL.sizePolicy().hasHeightForWidth()) - self.pushButtonTTL.setSizePolicy(sizePolicy) - self.pushButtonTTL.setMaximumSize(QtCore.QSize(32, 16777215)) +# sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) +# sizePolicy.setHorizontalStretch(0) +# sizePolicy.setVerticalStretch(0) +# sizePolicy.setHeightForWidth(self.pushButtonTTL.sizePolicy().hasHeightForWidth()) +# self.pushButtonTTL.setSizePolicy(sizePolicy) palette = QtGui.QPalette() brush = QtGui.QBrush(QtGui.QColor(0, 0, 255)) brush.setStyle(QtCore.Qt.SolidPattern) @@ -276,32 +302,53 @@ class Ui_MainWindow(object): self.pushButtonTTL.setFont(font) self.pushButtonTTL.setFlat(True) self.pushButtonTTL.setObjectName(_fromUtf8("pushButtonTTL")) - self.horizontalLayout_5.addWidget(self.pushButtonTTL) + self.horizontalSplitter_5.addWidget(self.pushButtonTTL) self.horizontalSliderTTL = QtGui.QSlider(self.send) self.horizontalSliderTTL.setMinimumSize(QtCore.QSize(35, 0)) - self.horizontalSliderTTL.setMaximumSize(QtCore.QSize(70, 16777215)) self.horizontalSliderTTL.setOrientation(QtCore.Qt.Horizontal) self.horizontalSliderTTL.setInvertedAppearance(False) self.horizontalSliderTTL.setInvertedControls(False) self.horizontalSliderTTL.setObjectName(_fromUtf8("horizontalSliderTTL")) - self.horizontalLayout_5.addWidget(self.horizontalSliderTTL) + self.horizontalSplitter_5.addWidget(self.horizontalSliderTTL) self.labelHumanFriendlyTTLDescription = QtGui.QLabel(self.send) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.labelHumanFriendlyTTLDescription.sizePolicy().hasHeightForWidth()) - self.labelHumanFriendlyTTLDescription.setSizePolicy(sizePolicy) +# sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Preferred) +# sizePolicy.setHorizontalStretch(0) +# sizePolicy.setVerticalStretch(0) +# sizePolicy.setHeightForWidth(self.labelHumanFriendlyTTLDescription.sizePolicy().hasHeightForWidth()) +# self.labelHumanFriendlyTTLDescription.setSizePolicy(sizePolicy) self.labelHumanFriendlyTTLDescription.setMinimumSize(QtCore.QSize(45, 0)) - self.labelHumanFriendlyTTLDescription.setMaximumSize(QtCore.QSize(45, 16777215)) self.labelHumanFriendlyTTLDescription.setObjectName(_fromUtf8("labelHumanFriendlyTTLDescription")) - self.horizontalLayout_5.addWidget(self.labelHumanFriendlyTTLDescription) + self.horizontalSplitter_5.addWidget(self.labelHumanFriendlyTTLDescription) self.pushButtonSend = QtGui.QPushButton(self.send) - self.pushButtonSend.setMaximumSize(QtCore.QSize(16777215, 16777215)) self.pushButtonSend.setObjectName(_fromUtf8("pushButtonSend")) - self.horizontalLayout_5.addWidget(self.pushButtonSend) - self.verticalLayout.addLayout(self.horizontalLayout_5) - self.horizontalLayout.addLayout(self.verticalLayout) - self.gridLayout_7.addLayout(self.horizontalLayout, 0, 0, 1, 1) + self.horizontalSplitter_5.addWidget(self.pushButtonSend) + self.pushButtonTTL.setMaximumSize(QtCore.QSize(32, self.pushButtonSend.height())) + self.labelHumanFriendlyTTLDescription.setMaximumSize(QtCore.QSize(45, self.pushButtonSend.height())) + self.horizontalSliderTTL.setMaximumSize(QtCore.QSize(70, self.pushButtonSend.height())) + self.horizontalSplitter_5.resize(self.horizontalSplitter_5.width(), self.pushButtonSend.height()) + self.horizontalSplitter_5.setStretchFactor(0, 0) + self.horizontalSplitter_5.setStretchFactor(1, 0) + self.horizontalSplitter_5.setStretchFactor(2, 0) + self.horizontalSplitter_5.setStretchFactor(3, 1) + self.horizontalSplitter_5.setCollapsible(0, False) + self.horizontalSplitter_5.setCollapsible(1, False) + self.horizontalSplitter_5.setCollapsible(2, False) + self.horizontalSplitter_5.setCollapsible(3, False) + self.horizontalSplitter_5.handle(1).setEnabled(False) + self.horizontalSplitter_5.handle(2).setEnabled(False) + self.horizontalSplitter_5.handle(3).setEnabled(False) + self.verticalSplitter.addWidget(self.horizontalSplitter_5) + self.verticalSplitter.setStretchFactor(0, 0) + self.verticalSplitter.setStretchFactor(1, 1) + self.verticalSplitter.setCollapsible(0, False) + self.verticalSplitter.setCollapsible(1, False) + self.verticalSplitter.handle(1).setEnabled(False) + self.horizontalSplitter.addWidget(self.verticalSplitter) + self.horizontalSplitter.setStretchFactor(0, 0) + self.horizontalSplitter.setStretchFactor(1, 1) + self.horizontalSplitter.setCollapsible(0, False) + self.horizontalSplitter.setCollapsible(1, False) + self.gridLayout_7.addWidget(self.horizontalSplitter, 0, 0, 1, 1) icon4 = QtGui.QIcon() icon4.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/send.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.tabWidget.addTab(self.send, icon4, _fromUtf8("")) @@ -309,32 +356,39 @@ class Ui_MainWindow(object): self.subscriptions.setObjectName(_fromUtf8("subscriptions")) self.gridLayout_3 = QtGui.QGridLayout(self.subscriptions) self.gridLayout_3.setObjectName(_fromUtf8("gridLayout_3")) - self.horizontalLayout_4 = QtGui.QHBoxLayout() - self.horizontalLayout_4.setObjectName(_fromUtf8("horizontalLayout_4")) - self.verticalLayout_3 = QtGui.QVBoxLayout() - self.verticalLayout_3.setObjectName(_fromUtf8("verticalLayout_3")) + self.horizontalSplitter_4 = settingsmixin.SSplitter() + self.horizontalSplitter_4.setObjectName(_fromUtf8("horizontalSplitter_4")) + self.verticalSplitter_3 = settingsmixin.SSplitter() + self.verticalSplitter_3.setObjectName(_fromUtf8("verticalSplitter_3")) + self.verticalSplitter_3.setOrientation(QtCore.Qt.Vertical) self.treeWidgetSubscriptions = QtGui.QTreeWidget(self.subscriptions) - self.treeWidgetSubscriptions.setMaximumSize(QtCore.QSize(200, 16777215)) self.treeWidgetSubscriptions.setAlternatingRowColors(True) self.treeWidgetSubscriptions.setSelectionMode(QtGui.QAbstractItemView.SingleSelection) self.treeWidgetSubscriptions.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) self.treeWidgetSubscriptions.setObjectName(_fromUtf8("treeWidgetSubscriptions")) + self.treeWidgetSubscriptions.resize(200, self.treeWidgetSubscriptions.height()) icon5 = QtGui.QIcon() icon5.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/subscriptions.png")), QtGui.QIcon.Selected, QtGui.QIcon.Off) self.treeWidgetSubscriptions.headerItem().setIcon(0, icon5) - self.verticalLayout_3.addWidget(self.treeWidgetSubscriptions) + self.verticalSplitter_3.addWidget(self.treeWidgetSubscriptions) self.pushButtonAddSubscription = QtGui.QPushButton(self.subscriptions) - self.pushButtonAddSubscription.setMaximumSize(QtCore.QSize(200, 16777215)) self.pushButtonAddSubscription.setObjectName(_fromUtf8("pushButtonAddSubscription")) - self.verticalLayout_3.addWidget(self.pushButtonAddSubscription) - self.horizontalLayout_4.addLayout(self.verticalLayout_3) - self.verticalLayout_4 = QtGui.QVBoxLayout() - self.verticalLayout_4.setObjectName(_fromUtf8("verticalLayout_4")) - self.horizontalLayout_2 = QtGui.QHBoxLayout() - self.horizontalLayout_2.setObjectName(_fromUtf8("horizontalLayout_2")) + self.pushButtonAddSubscription.resize(200, self.pushButtonAddSubscription.height()) + self.verticalSplitter_3.addWidget(self.pushButtonAddSubscription) + self.verticalSplitter_3.setStretchFactor(0, 1) + self.verticalSplitter_3.setStretchFactor(1, 0) + self.verticalSplitter_3.setCollapsible(0, False) + self.verticalSplitter_3.setCollapsible(1, False) + self.verticalSplitter_3.handle(1).setEnabled(False) + self.horizontalSplitter_4.addWidget(self.verticalSplitter_3) + self.verticalSplitter_4 = settingsmixin.SSplitter() + self.verticalSplitter_4.setObjectName(_fromUtf8("verticalSplitter_4")) + self.verticalSplitter_4.setOrientation(QtCore.Qt.Vertical) + self.horizontalSplitter_2 = settingsmixin.SSplitter() + self.horizontalSplitter_2.setObjectName(_fromUtf8("horizontalSplitter_2")) self.inboxSearchLineEditSubscriptions = QtGui.QLineEdit(self.subscriptions) self.inboxSearchLineEditSubscriptions.setObjectName(_fromUtf8("inboxSearchLineEditSubscriptions")) - self.horizontalLayout_2.addWidget(self.inboxSearchLineEditSubscriptions) + self.horizontalSplitter_2.addWidget(self.inboxSearchLineEditSubscriptions) self.inboxSearchOptionSubscriptions = QtGui.QComboBox(self.subscriptions) self.inboxSearchOptionSubscriptions.setObjectName(_fromUtf8("inboxSearchOptionSubscriptions")) self.inboxSearchOptionSubscriptions.addItem(_fromUtf8("")) @@ -342,9 +396,13 @@ class Ui_MainWindow(object): self.inboxSearchOptionSubscriptions.addItem(_fromUtf8("")) self.inboxSearchOptionSubscriptions.addItem(_fromUtf8("")) self.inboxSearchOptionSubscriptions.addItem(_fromUtf8("")) - self.horizontalLayout_2.addWidget(self.inboxSearchOptionSubscriptions) - self.verticalLayout_4.addLayout(self.horizontalLayout_2) - self.tableWidgetInboxSubscriptions = QtGui.QTableWidget(self.subscriptions) + self.inboxSearchOptionSubscriptions.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToContents) + self.horizontalSplitter_2.addWidget(self.inboxSearchOptionSubscriptions) + self.horizontalSplitter_2.handle(1).setEnabled(False) + self.horizontalSplitter_2.setStretchFactor(0, 1) + self.horizontalSplitter_2.setStretchFactor(1, 0) + self.verticalSplitter_4.addWidget(self.horizontalSplitter_2) + self.tableWidgetInboxSubscriptions = settingsmixin.STableWidget(self.subscriptions) self.tableWidgetInboxSubscriptions.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers) self.tableWidgetInboxSubscriptions.setAlternatingRowColors(True) self.tableWidgetInboxSubscriptions.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection) @@ -369,59 +427,81 @@ class Ui_MainWindow(object): self.tableWidgetInboxSubscriptions.horizontalHeader().setStretchLastSection(True) self.tableWidgetInboxSubscriptions.verticalHeader().setVisible(False) self.tableWidgetInboxSubscriptions.verticalHeader().setDefaultSectionSize(26) - self.verticalLayout_4.addWidget(self.tableWidgetInboxSubscriptions) + self.verticalSplitter_4.addWidget(self.tableWidgetInboxSubscriptions) self.textEditInboxMessageSubscriptions = QtGui.QTextEdit(self.subscriptions) self.textEditInboxMessageSubscriptions.setBaseSize(QtCore.QSize(0, 500)) self.textEditInboxMessageSubscriptions.setReadOnly(True) self.textEditInboxMessageSubscriptions.setObjectName(_fromUtf8("textEditInboxMessageSubscriptions")) - self.verticalLayout_4.addWidget(self.textEditInboxMessageSubscriptions) - self.horizontalLayout_4.addLayout(self.verticalLayout_4) - self.gridLayout_3.addLayout(self.horizontalLayout_4, 0, 0, 1, 1) + self.verticalSplitter_4.addWidget(self.textEditInboxMessageSubscriptions) + self.verticalSplitter_4.setStretchFactor(0, 0) + self.verticalSplitter_4.setStretchFactor(1, 1) + self.verticalSplitter_4.setStretchFactor(2, 2) + self.verticalSplitter_4.setCollapsible(0, False) + self.verticalSplitter_4.setCollapsible(1, False) + self.verticalSplitter_4.setCollapsible(2, False) + self.verticalSplitter_4.handle(1).setEnabled(False) + self.horizontalSplitter_4.addWidget(self.verticalSplitter_4) + self.horizontalSplitter_4.setStretchFactor(0, 0) + self.horizontalSplitter_4.setStretchFactor(1, 1) + self.horizontalSplitter_4.setCollapsible(0, False) + self.horizontalSplitter_4.setCollapsible(1, False) + self.gridLayout_3.addWidget(self.horizontalSplitter_4, 0, 0, 1, 1) icon6 = QtGui.QIcon() icon6.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/subscriptions.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.tabWidget.addTab(self.subscriptions, icon6, _fromUtf8("")) - self.tab_3 = QtGui.QWidget() - self.tab_3.setObjectName(_fromUtf8("tab_3")) - self.gridLayout_4 = QtGui.QGridLayout(self.tab_3) + self.chans = QtGui.QWidget() + self.chans.setObjectName(_fromUtf8("chans")) + self.gridLayout_4 = QtGui.QGridLayout(self.chans) self.gridLayout_4.setObjectName(_fromUtf8("gridLayout_4")) - self.horizontalLayout_7 = QtGui.QHBoxLayout() - self.horizontalLayout_7.setObjectName(_fromUtf8("horizontalLayout_7")) - self.verticalLayout_17 = QtGui.QVBoxLayout() - self.verticalLayout_17.setObjectName(_fromUtf8("verticalLayout_17")) - self.treeWidgetChans = QtGui.QTreeWidget(self.tab_3) - self.treeWidgetChans.setMaximumSize(QtCore.QSize(200, 16777215)) + self.horizontalSplitter_7 = settingsmixin.SSplitter() + self.horizontalSplitter_7.setObjectName(_fromUtf8("horizontalSplitter_7")) + self.verticalSplitter_17 = settingsmixin.SSplitter() + self.verticalSplitter_17.setObjectName(_fromUtf8("verticalSplitter_17")) + self.verticalSplitter_17.setOrientation(QtCore.Qt.Vertical) + self.treeWidgetChans = QtGui.QTreeWidget(self.chans) self.treeWidgetChans.setFrameShadow(QtGui.QFrame.Sunken) self.treeWidgetChans.setLineWidth(1) self.treeWidgetChans.setAlternatingRowColors(True) self.treeWidgetChans.setSelectionMode(QtGui.QAbstractItemView.SingleSelection) self.treeWidgetChans.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) self.treeWidgetChans.setObjectName(_fromUtf8("treeWidgetChans")) + self.treeWidgetChans.resize(200, self.treeWidgetChans.height()) icon7 = QtGui.QIcon() icon7.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/can-icon-16px.png")), QtGui.QIcon.Selected, QtGui.QIcon.Off) self.treeWidgetChans.headerItem().setIcon(0, icon7) - self.verticalLayout_17.addWidget(self.treeWidgetChans) - self.pushButtonAddChan = QtGui.QPushButton(self.tab_3) - self.pushButtonAddChan.setMaximumSize(QtCore.QSize(200, 16777215)) + self.verticalSplitter_17.addWidget(self.treeWidgetChans) + self.pushButtonAddChan = QtGui.QPushButton(self.chans) self.pushButtonAddChan.setObjectName(_fromUtf8("pushButtonAddChan")) - self.verticalLayout_17.addWidget(self.pushButtonAddChan) - self.horizontalLayout_7.addLayout(self.verticalLayout_17) - self.verticalLayout_8 = QtGui.QVBoxLayout() - self.verticalLayout_8.setObjectName(_fromUtf8("verticalLayout_8")) - self.horizontalLayout_6 = QtGui.QHBoxLayout() - self.horizontalLayout_6.setObjectName(_fromUtf8("horizontalLayout_6")) - self.inboxSearchLineEditChans = QtGui.QLineEdit(self.tab_3) + self.pushButtonAddChan.resize(200, self.pushButtonAddChan.height()) + self.verticalSplitter_17.addWidget(self.pushButtonAddChan) + self.verticalSplitter_17.setStretchFactor(0, 1) + self.verticalSplitter_17.setStretchFactor(1, 0) + self.verticalSplitter_17.setCollapsible(0, False) + self.verticalSplitter_17.setCollapsible(1, False) + self.verticalSplitter_17.handle(1).setEnabled(False) + self.horizontalSplitter_7.addWidget(self.verticalSplitter_17) + self.verticalSplitter_8 = settingsmixin.SSplitter() + self.verticalSplitter_8.setObjectName(_fromUtf8("verticalSplitter_8")) + self.verticalSplitter_8.setOrientation(QtCore.Qt.Vertical) + self.horizontalSplitter_6 = settingsmixin.SSplitter() + self.horizontalSplitter_6.setObjectName(_fromUtf8("horizontalSplitter_6")) + self.inboxSearchLineEditChans = QtGui.QLineEdit(self.chans) self.inboxSearchLineEditChans.setObjectName(_fromUtf8("inboxSearchLineEditChans")) - self.horizontalLayout_6.addWidget(self.inboxSearchLineEditChans) - self.inboxSearchOptionChans = QtGui.QComboBox(self.tab_3) + self.horizontalSplitter_6.addWidget(self.inboxSearchLineEditChans) + self.inboxSearchOptionChans = QtGui.QComboBox(self.chans) self.inboxSearchOptionChans.setObjectName(_fromUtf8("inboxSearchOptionChans")) self.inboxSearchOptionChans.addItem(_fromUtf8("")) self.inboxSearchOptionChans.addItem(_fromUtf8("")) self.inboxSearchOptionChans.addItem(_fromUtf8("")) self.inboxSearchOptionChans.addItem(_fromUtf8("")) self.inboxSearchOptionChans.addItem(_fromUtf8("")) - self.horizontalLayout_6.addWidget(self.inboxSearchOptionChans) - self.verticalLayout_8.addLayout(self.horizontalLayout_6) - self.tableWidgetInboxChans = QtGui.QTableWidget(self.tab_3) + self.inboxSearchOptionChans.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToContents) + self.horizontalSplitter_6.addWidget(self.inboxSearchOptionChans) + self.horizontalSplitter_6.handle(1).setEnabled(False) + self.horizontalSplitter_6.setStretchFactor(0, 1) + self.horizontalSplitter_6.setStretchFactor(1, 0) + self.verticalSplitter_8.addWidget(self.horizontalSplitter_6) + self.tableWidgetInboxChans = settingsmixin.STableWidget(self.chans) self.tableWidgetInboxChans.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers) self.tableWidgetInboxChans.setAlternatingRowColors(True) self.tableWidgetInboxChans.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection) @@ -446,17 +526,28 @@ class Ui_MainWindow(object): self.tableWidgetInboxChans.horizontalHeader().setStretchLastSection(True) self.tableWidgetInboxChans.verticalHeader().setVisible(False) self.tableWidgetInboxChans.verticalHeader().setDefaultSectionSize(26) - self.verticalLayout_8.addWidget(self.tableWidgetInboxChans) - self.textEditInboxMessageChans = QtGui.QTextEdit(self.tab_3) + self.verticalSplitter_8.addWidget(self.tableWidgetInboxChans) + self.textEditInboxMessageChans = QtGui.QTextEdit(self.chans) self.textEditInboxMessageChans.setBaseSize(QtCore.QSize(0, 500)) self.textEditInboxMessageChans.setReadOnly(True) self.textEditInboxMessageChans.setObjectName(_fromUtf8("textEditInboxMessageChans")) - self.verticalLayout_8.addWidget(self.textEditInboxMessageChans) - self.horizontalLayout_7.addLayout(self.verticalLayout_8) - self.gridLayout_4.addLayout(self.horizontalLayout_7, 0, 0, 1, 1) + self.verticalSplitter_8.addWidget(self.textEditInboxMessageChans) + self.verticalSplitter_8.setStretchFactor(0, 0) + self.verticalSplitter_8.setStretchFactor(1, 1) + self.verticalSplitter_8.setStretchFactor(2, 2) + self.verticalSplitter_8.setCollapsible(0, False) + self.verticalSplitter_8.setCollapsible(1, False) + self.verticalSplitter_8.setCollapsible(2, False) + self.verticalSplitter_8.handle(1).setEnabled(False) + self.horizontalSplitter_7.addWidget(self.verticalSplitter_8) + self.horizontalSplitter_7.setStretchFactor(0, 0) + self.horizontalSplitter_7.setStretchFactor(1, 1) + self.horizontalSplitter_7.setCollapsible(0, False) + self.horizontalSplitter_7.setCollapsible(1, False) + self.gridLayout_4.addWidget(self.horizontalSplitter_7, 0, 0, 1, 1) icon8 = QtGui.QIcon() icon8.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/can-icon-16px.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) - self.tabWidget.addTab(self.tab_3, icon8, _fromUtf8("")) + self.tabWidget.addTab(self.chans, icon8, _fromUtf8("")) self.blackwhitelist = QtGui.QWidget() self.blackwhitelist.setObjectName(_fromUtf8("blackwhitelist")) self.gridLayout_6 = QtGui.QGridLayout(self.blackwhitelist) @@ -473,7 +564,7 @@ class Ui_MainWindow(object): self.gridLayout_6.addWidget(self.pushButtonAddBlacklist, 2, 0, 1, 1) spacerItem = QtGui.QSpacerItem(689, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) self.gridLayout_6.addItem(spacerItem, 2, 1, 1, 1) - self.tableWidgetBlacklist = QtGui.QTableWidget(self.blackwhitelist) + self.tableWidgetBlacklist = settingsmixin.STableWidget(self.blackwhitelist) self.tableWidgetBlacklist.setAlternatingRowColors(True) self.tableWidgetBlacklist.setSelectionMode(QtGui.QAbstractItemView.SingleSelection) self.tableWidgetBlacklist.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) @@ -504,7 +595,7 @@ class Ui_MainWindow(object): self.pushButtonStatusIcon.setIcon(icon10) self.pushButtonStatusIcon.setFlat(True) self.pushButtonStatusIcon.setObjectName(_fromUtf8("pushButtonStatusIcon")) - self.tableWidgetConnectionCount = QtGui.QTableWidget(self.networkstatus) + self.tableWidgetConnectionCount = settingsmixin.STableWidget(self.networkstatus) self.tableWidgetConnectionCount.setGeometry(QtCore.QRect(20, 70, 241, 241)) palette = QtGui.QPalette() brush = QtGui.QBrush(QtGui.QColor(212, 208, 200)) @@ -675,7 +766,7 @@ class Ui_MainWindow(object): "p, li { white-space: pre-wrap; }\n" "\n" "


", None)) - self.tabWidgetSend.setTabText(self.tabWidgetSend.indexOf(self.tab), _translate("MainWindow", "Send ordinary Message", None)) + self.tabWidgetSend.setTabText(self.tabWidgetSend.indexOf(self.sendDirect), _translate("MainWindow", "Send ordinary Message", None)) self.label_8.setText(_translate("MainWindow", "From:", None)) self.label_7.setText(_translate("MainWindow", "Subject:", None)) self.textEditMessageBroadcast.setHtml(_translate("MainWindow", "\n" @@ -683,7 +774,7 @@ class Ui_MainWindow(object): "p, li { white-space: pre-wrap; }\n" "\n" "


", None)) - self.tabWidgetSend.setTabText(self.tabWidgetSend.indexOf(self.tab_2), _translate("MainWindow", "Send Message to your Subscribers", None)) + self.tabWidgetSend.setTabText(self.tabWidgetSend.indexOf(self.sendBroadcast), _translate("MainWindow", "Send Message to your Subscribers", None)) self.pushButtonTTL.setText(_translate("MainWindow", "TTL:", None)) self.labelHumanFriendlyTTLDescription.setText(_translate("MainWindow", "X days", None)) self.pushButtonSend.setText(_translate("MainWindow", "Send", None)) @@ -723,7 +814,7 @@ class Ui_MainWindow(object): item.setText(_translate("MainWindow", "Subject", None)) item = self.tableWidgetInboxChans.horizontalHeaderItem(3) item.setText(_translate("MainWindow", "Received", None)) - self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_3), _translate("MainWindow", "Chans", None)) + self.tabWidget.setTabText(self.tabWidget.indexOf(self.chans), _translate("MainWindow", "Chans", None)) self.radioButtonBlacklist.setText(_translate("MainWindow", "Use a Blacklist (Allow all incoming messages except those on the Blacklist)", None)) self.radioButtonWhitelist.setText(_translate("MainWindow", "Use a Whitelist (Block all incoming messages except those on the Whitelist)", None)) self.pushButtonAddBlacklist.setText(_translate("MainWindow", "Add new entry", None)) @@ -765,8 +856,9 @@ import bitmessage_icons_rc if __name__ == "__main__": import sys + app = QtGui.QApplication(sys.argv) - MainWindow = QtGui.QMainWindow() + MainWindow = settingsmixin.SMainWindow() ui = Ui_MainWindow() ui.setupUi(MainWindow) MainWindow.show() diff --git a/src/bitmessageqt/settingsmixin.py b/src/bitmessageqt/settingsmixin.py new file mode 100644 index 00000000..301ce96e --- /dev/null +++ b/src/bitmessageqt/settingsmixin.py @@ -0,0 +1,69 @@ +#!/usr/bin/python2.7 + +from PyQt4 import QtCore, QtGui + +class SettingsMixin(object): + def warnIfNoObjectName(self): + if self.objectName() == "": + # TODO: logger + pass + + def writeState(self, source): + self.warnIfNoObjectName() + settings = QtCore.QSettings() + settings.beginGroup(self.objectName()) + settings.setValue("state", source.saveState()) + settings.endGroup() + + def writeGeometry(self, source): + self.warnIfNoObjectName() + settings = QtCore.QSettings() + settings.beginGroup(self.objectName()) + settings.setValue("geometry", source.saveGeometry()) + settings.endGroup() + + def readGeometry(self, target): + self.warnIfNoObjectName() + settings = QtCore.QSettings() + try: + geom = settings.value("/".join([str(self.objectName()), "geometry"])) + target.restoreGeometry(geom.toByteArray() if hasattr(geom, 'toByteArray') else geom) + except Exception as e: + pass + + def readState(self, target): + self.warnIfNoObjectName() + settings = QtCore.QSettings() + try: + state = settings.value("/".join([str(self.objectName()), "state"])) + target.restoreState(state.toByteArray() if hasattr(state, 'toByteArray') else state) + except Exception as e: + pass + + +class SMainWindow(QtGui.QMainWindow, SettingsMixin): + def loadSettings(self): + self.readGeometry(self) + self.readState(self) + + def saveSettings(self): + self.writeState(self) + self.writeGeometry(self) + + +class STableWidget(QtGui.QTableWidget, SettingsMixin): + def loadSettings(self): + self.readState(self.horizontalHeader()) + + def saveSettings(self): + return + self.writeState(self.horizontalHeader()) + + +class SSplitter(QtGui.QSplitter, SettingsMixin): + def loadSettings(self): + self.readState(self) + + def saveSettings(self): + return + self.writeState(self) \ No newline at end of file From cc5ed73060d2eb06e650635b101a28cb8f08a769 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sun, 8 Nov 2015 21:45:49 +0100 Subject: [PATCH 112/210] Actually enable saving states It was temporarily disabled for testing resizing. Addresses #12 --- src/bitmessageqt/settingsmixin.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/bitmessageqt/settingsmixin.py b/src/bitmessageqt/settingsmixin.py index 301ce96e..75822fa4 100644 --- a/src/bitmessageqt/settingsmixin.py +++ b/src/bitmessageqt/settingsmixin.py @@ -56,7 +56,6 @@ class STableWidget(QtGui.QTableWidget, SettingsMixin): self.readState(self.horizontalHeader()) def saveSettings(self): - return self.writeState(self.horizontalHeader()) @@ -65,5 +64,4 @@ class SSplitter(QtGui.QSplitter, SettingsMixin): self.readState(self) def saveSettings(self): - return self.writeState(self) \ No newline at end of file From 1f67b00956b053ab0f418241d0aadcc8e502dfef Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sun, 8 Nov 2015 22:12:12 +0100 Subject: [PATCH 113/210] Do not store search splitter state It causes problems because it can't be resized through GUI. --- src/bitmessageqt/bitmessageui.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bitmessageqt/bitmessageui.py b/src/bitmessageqt/bitmessageui.py index 0e719c17..d8d63f31 100644 --- a/src/bitmessageqt/bitmessageui.py +++ b/src/bitmessageqt/bitmessageui.py @@ -79,7 +79,7 @@ class Ui_MainWindow(object): self.verticalSplitter_7 = settingsmixin.SSplitter() self.verticalSplitter_7.setObjectName(_fromUtf8("verticalSplitter_7")) self.verticalSplitter_7.setOrientation(QtCore.Qt.Vertical) - self.horizontalSplitterSearch = settingsmixin.SSplitter() + self.horizontalSplitterSearch = QtGui.QSplitter() self.horizontalSplitterSearch.setObjectName(_fromUtf8("horizontalSplitterSearch")) self.inboxSearchLineEdit = QtGui.QLineEdit(self.inbox) self.inboxSearchLineEdit.setObjectName(_fromUtf8("inboxSearchLineEdit")) @@ -384,7 +384,7 @@ class Ui_MainWindow(object): self.verticalSplitter_4 = settingsmixin.SSplitter() self.verticalSplitter_4.setObjectName(_fromUtf8("verticalSplitter_4")) self.verticalSplitter_4.setOrientation(QtCore.Qt.Vertical) - self.horizontalSplitter_2 = settingsmixin.SSplitter() + self.horizontalSplitter_2 = QtGui.QSplitter() self.horizontalSplitter_2.setObjectName(_fromUtf8("horizontalSplitter_2")) self.inboxSearchLineEditSubscriptions = QtGui.QLineEdit(self.subscriptions) self.inboxSearchLineEditSubscriptions.setObjectName(_fromUtf8("inboxSearchLineEditSubscriptions")) @@ -483,7 +483,7 @@ class Ui_MainWindow(object): self.verticalSplitter_8 = settingsmixin.SSplitter() self.verticalSplitter_8.setObjectName(_fromUtf8("verticalSplitter_8")) self.verticalSplitter_8.setOrientation(QtCore.Qt.Vertical) - self.horizontalSplitter_6 = settingsmixin.SSplitter() + self.horizontalSplitter_6 = QtGui.QSplitter() self.horizontalSplitter_6.setObjectName(_fromUtf8("horizontalSplitter_6")) self.inboxSearchLineEditChans = QtGui.QLineEdit(self.chans) self.inboxSearchLineEditChans.setObjectName(_fromUtf8("inboxSearchLineEditChans")) From 80b02b4a9638dfbee97d379f01a3aaf97a26289c Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sun, 8 Nov 2015 23:03:05 +0100 Subject: [PATCH 114/210] TTL/send splitter shouldn't save state Because it also cannot be resized from the UI --- src/bitmessageqt/bitmessageui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitmessageqt/bitmessageui.py b/src/bitmessageqt/bitmessageui.py index d8d63f31..269ec099 100644 --- a/src/bitmessageqt/bitmessageui.py +++ b/src/bitmessageqt/bitmessageui.py @@ -278,7 +278,7 @@ class Ui_MainWindow(object): self.gridLayout_9.addWidget(self.verticalSplitter_6, 0, 0, 1, 1) self.tabWidgetSend.addTab(self.sendBroadcast, _fromUtf8("")) self.verticalSplitter.addWidget(self.tabWidgetSend) - self.horizontalSplitter_5 = settingsmixin.SSplitter() + self.horizontalSplitter_5 = QtGui.QSplitter() self.horizontalSplitter_5.setObjectName(_fromUtf8("horizontalSplitter_5")) self.pushButtonTTL = QtGui.QPushButton(self.send) # sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) From bfd64bb93f7d7493b166fca6bcdc0fb5877678b3 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sun, 8 Nov 2015 23:03:30 +0100 Subject: [PATCH 115/210] Bump version get ready for 0.5.2 --- src/shared.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared.py b/src/shared.py index 8e1410e3..342a7315 100644 --- a/src/shared.py +++ b/src/shared.py @@ -1,6 +1,6 @@ from __future__ import division -softwareVersion = '0.5.1' +softwareVersion = '0.5.2' verbose = 1 maximumAgeOfAnObjectThatIAmWillingToAccept = 216000 # This is obsolete with the change to protocol v3 but the singleCleaner thread still hasn't been updated so we need this a little longer. lengthOfTimeToHoldOnToAllPubkeys = 2419200 # Equals 4 weeks. You could make this longer if you want but making it shorter would not be advisable because there is a very small possibility that it could keep you from obtaining a needed pubkey for a period of time. From e5d6da2ec1cb46df0b0942738f4b816d66c70fa4 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Mon, 9 Nov 2015 17:15:05 +0100 Subject: [PATCH 116/210] Fix Py2Exe / Py2App Fixes the C PoW loading in frozen apps (Windows and OSX) Cleaner fallback in PoW if something goes wrong --- src/proofofwork.py | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/proofofwork.py b/src/proofofwork.py index f7776638..04e07af3 100644 --- a/src/proofofwork.py +++ b/src/proofofwork.py @@ -10,7 +10,12 @@ import openclpow import os import ctypes -curdir = os.path.dirname(__file__) +if frozen == "macosx_app": + curdir = os.environ.get("RESOURCEPATH") +elif frozen: # windows + curdir = sys._MEIPASS +else: + curdir = os.path.dirname(__file__) bitmsglib = 'bitmsghash.so' if "win32" == sys.platform: if ctypes.sizeof(ctypes.c_voidp) == 4: @@ -128,13 +133,18 @@ def run(target, initialHash): try: return _doGPUPoW(target, initialHash) except: - pass # fallback to normal PoW + pass # fallback + if bmpow: + try: + return _doCPoW(target, initialHash) + except: + pass # fallback if frozen == "macosx_app" or not frozen: - if bmpow: - try: - return _doCPoW(target, initialHash) - except: - pass # fallback to normal PoW - return _doFastPoW(target, initialHash) - else: - return _doSafePoW(target, initialHash) + # on my (Peter Surda) Windows 10, Windows Defender + # does not like this and fights with PyBitmessage + # over CPU, resulting in very slow PoW + try: + return _doFastPoW(target, initialHash) + except: + pass #fallback + return _doSafePoW(target, initialHash) From 33b95db55b6ad37497bdc3dd08e01fea82b47637 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Mon, 9 Nov 2015 17:21:47 +0100 Subject: [PATCH 117/210] Sqlite secure delete Fixes #102 --- src/class_sqlThread.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/class_sqlThread.py b/src/class_sqlThread.py index f0862591..9de44a42 100644 --- a/src/class_sqlThread.py +++ b/src/class_sqlThread.py @@ -25,6 +25,8 @@ class sqlThread(threading.Thread): self.conn = sqlite3.connect(shared.appdata + 'messages.dat') self.conn.text_factory = str self.cur = self.conn.cursor() + + self.cur.execute('PRAGMA secure_delete = true') try: self.cur.execute( From 9d2689db7a5b221038d718f5dd3abe5ead79192e Mon Sep 17 00:00:00 2001 From: mailchuck Date: Mon, 9 Nov 2015 17:31:06 +0100 Subject: [PATCH 118/210] Disable "Special address behaviour" in chan Fixes #97 --- src/bitmessageqt/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 9bc69d79..1831f5a2 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -3705,7 +3705,6 @@ class MyForm(settingsmixin.SMainWindow): else: self.popMenu.addAction(self.actionEnable) self.popMenu.addAction(self.actionSetAvatar) - self.popMenu.addAction(self.actionSpecialAddressBehavior) self.popMenu.exec_( self.ui.treeWidgetChans.mapToGlobal(point)) From b01d5db549be38f835f9a0abb78e5a809390b831 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Mon, 9 Nov 2015 18:22:38 +0100 Subject: [PATCH 119/210] Allow deleting channels Fixes #96 --- src/bitmessageqt/__init__.py | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 1831f5a2..fae731b6 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -226,6 +226,9 @@ class MyForm(settingsmixin.SMainWindow): # Actions self.actionNew = self.ui.addressContextMenuToolbar.addAction(_translate( "MainWindow", "New"), self.on_action_YourIdentitiesNew) + self.actionDelete = self.ui.addressContextMenuToolbar.addAction( + _translate("MainWindow", "Delete"), + self.on_action_YourIdentitiesDelete) self.actionEnable = self.ui.addressContextMenuToolbar.addAction( _translate( "MainWindow", "Enable"), self.on_action_Enable) @@ -459,7 +462,10 @@ class MyForm(settingsmixin.SMainWindow): widgets = {} for i in range (0, treeWidget.topLevelItemCount()): widget = treeWidget.topLevelItem(i) - toAddress = widget.address + if widget is not None: + toAddress = widget.address + else: + toAddress = None if not toAddress in db: treeWidget.takeTopLevelItem(i) @@ -3561,6 +3567,24 @@ class MyForm(settingsmixin.SMainWindow): def on_action_YourIdentitiesNew(self): self.click_NewAddressDialog() + def on_action_YourIdentitiesDelete(self): + account = self.getCurrentItem() + if account.type == "normal": + return # maybe in the future + elif account.type == "chan": + if QtGui.QMessageBox.question(self, "Delete channel?", _translate("MainWindow", "If you delete the channel, messages that you already received will become inaccessible. Maybe you can consider disabling the channel instead. Disabled channels will not receive new messages, but you can still view messages you already received.\n\nAre you sure you want to delete the channel?"), QMessageBox.Yes|QMessageBox.No) == QMessageBox.Yes: + shared.config.remove_section(str(account.address)) + else: + return + else: + return + shared.writeKeysFile() + shared.reloadMyAddressHashes() + if account.type == "normal": + self.rerenderTabTreeMessages() + elif account.type == "chan": + self.rerenderTabTreeChans() + def on_action_Enable(self): addressAtCurrentRow = self.getCurrentAccount() self.enableIdentity(addressAtCurrentRow) @@ -3697,6 +3721,7 @@ class MyForm(settingsmixin.SMainWindow): def on_context_menuChan(self, point): self.popMenu = QtGui.QMenu(self) self.popMenu.addAction(self.actionNew) + self.popMenu.addAction(self.actionDelete) self.popMenu.addSeparator() self.popMenu.addAction(self.actionClipboard) self.popMenu.addSeparator() From c1269d9d56b353d8664ef3a791ef24d629202860 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Mon, 9 Nov 2015 18:44:27 +0100 Subject: [PATCH 120/210] several bugfixes for rerendertabtree It was prone to infinite loops, mixing elements etc. --- src/bitmessageqt/__init__.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index fae731b6..0b656fa5 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -460,7 +460,8 @@ class MyForm(settingsmixin.SMainWindow): treeWidget.setSortingEnabled(False) widgets = {} - for i in range (0, treeWidget.topLevelItemCount()): + i = 0 + while i < treeWidget.topLevelItemCount(): widget = treeWidget.topLevelItem(i) if widget is not None: toAddress = widget.address @@ -469,28 +470,32 @@ class MyForm(settingsmixin.SMainWindow): if not toAddress in db: treeWidget.takeTopLevelItem(i) - i -= 1 + # no increment continue unread = 0 - for j in range (0, widget.childCount()): + j = 0 + while j < widget.childCount(): subwidget = widget.child(j) try: subwidget.setUnreadCount(db[toAddress][subwidget.folderName]) unread += db[toAddress][subwidget.folderName] db[toAddress].pop(subwidget.folderName, None) except: - widget.takeChild(i) - j -= 1 + widget.takeChild(j) + # no increment + continue + j += 1 # add missing folders if len(db[toAddress]) > 0: - i = 0 + j = 0 for f, c in db[toAddress].iteritems(): print "adding %s, %i" % (f, c) - subwidget = Ui_FolderWidget(widget, i, toAddress, f, c) - i += 1 + subwidget = Ui_FolderWidget(widget, j, toAddress, f, c) + j += 1 widget.setUnreadCount(unread) db.pop(toAddress, None) + i += 1 i = 0 for toAddress in db: From e72773c5bbc5a0b849eaec410ff3e432ca152a0a Mon Sep 17 00:00:00 2001 From: mailchuck Date: Mon, 9 Nov 2015 18:45:35 +0100 Subject: [PATCH 121/210] Autofold/unfold when enabling/disabling Fixes #93 --- src/bitmessageqt/foldertree.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/bitmessageqt/foldertree.py b/src/bitmessageqt/foldertree.py index 6f5ad40c..1aab7e0e 100644 --- a/src/bitmessageqt/foldertree.py +++ b/src/bitmessageqt/foldertree.py @@ -29,6 +29,8 @@ class AccountMixin (object): def setEnabled(self, enabled): self.isEnabled = enabled + if hasattr(self, "setExpanded"): + self.setExpanded(enabled) self.updateText() def setType(self): @@ -107,9 +109,8 @@ class Ui_AddressWidget(QtGui.QTreeWidgetItem, AccountMixin): self.setAddress(address) self.setEnabled(enabled) self.setUnreadCount(unreadCount) - self.setType() self.initialised = True - self.setExpanded(enabled) # does updateText + self.setType() # does updateText def setAddress(self, address): super(Ui_AddressWidget, self).setAddress(address) @@ -182,9 +183,8 @@ class Ui_SubscriptionWidget(Ui_AddressWidget, AccountMixin): self.setEnabled(enabled) self.setType() self.setLabel(label) - self.setUnreadCount (unreadCount) self.initialised = True - self.setExpanded(enabled) # does updateText + self.setUnreadCount (unreadCount) # does updateText def setLabel(self, label): self.label = label From cfea0bbdd1ede1f1b26d2ac4faf5b51863a4d8a2 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Mon, 9 Nov 2015 18:58:27 +0100 Subject: [PATCH 122/210] Disabled folder color Fixes #92 --- src/bitmessageqt/foldertree.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/bitmessageqt/foldertree.py b/src/bitmessageqt/foldertree.py index 1aab7e0e..8bbba0c9 100644 --- a/src/bitmessageqt/foldertree.py +++ b/src/bitmessageqt/foldertree.py @@ -14,10 +14,21 @@ class AccountMixin (object): else: return QtGui.QApplication.palette().text().color() + def folderColor (self): + if not self.parent.isEnabled: + return QtGui.QColor(128, 128, 128) + else: + return QtGui.QApplication.palette().text().color() + def accountBrush(self): brush = QtGui.QBrush(self.accountColor()) brush.setStyle(QtCore.Qt.NoBrush) return brush + + def folderBrush(self): + brush = QtGui.QBrush(self.folderColor()) + brush.setStyle(QtCore.Qt.NoBrush) + return brush def setAddress(self, address): self.address = str(address) @@ -53,6 +64,7 @@ class Ui_FolderWidget(QtGui.QTreeWidgetItem, AccountMixin): self.setAddress(address) self.setFolderName(folderName) self.setUnreadCount(unreadCount) + self.parent = parent self.initialised = True self.updateText() parent.insertChild(pos, self) @@ -73,6 +85,7 @@ class Ui_FolderWidget(QtGui.QTreeWidgetItem, AccountMixin): else: font.setBold(False) self.setFont(0, font) + self.setForeground(0, self.folderBrush()) self.setText(0, text) self.setToolTip(0, text) # self.setData(0, QtCore.Qt.UserRole, [self.address, self.folderName]) From bdf2b28b0b2a80ac145cdb189de58b040a6cc264 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Mon, 9 Nov 2015 19:39:30 +0100 Subject: [PATCH 123/210] Newly sent messages and status update - newly sent messages did not appear in all tabs - message status change didn't work in all tabs - addresses #90 - however, still new sent message sender/recipient do not have the correct color --- src/bitmessageqt/__init__.py | 173 +++++++++++++++++++---------------- 1 file changed, 95 insertions(+), 78 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 0b656fa5..f73f8243 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -1808,56 +1808,53 @@ class MyForm(settingsmixin.SMainWindow): return self.unreadCount def updateSentItemStatusByToAddress(self, toAddress, textToDisplay): - sent = self.getAccountMessagelist(toAddress) - treeWidget = self.getAccountTreeWidget(toAddress) - if self.getCurrentFolder(treeWidget) != "sent": - return - for i in range(sent.rowCount()): - rowAddress = str(sent.item( - i, 0).data(Qt.UserRole).toPyObject()) - if toAddress == rowAddress: - sent.item(i, 3).setToolTip(textToDisplay) - try: - newlinePosition = textToDisplay.indexOf('\n') - except: # If someone misses adding a "_translate" to a string before passing it to this function, this function won't receive a qstring which will cause an exception. - newlinePosition = 0 - if newlinePosition > 1: - sent.item(i, 3).setText( - textToDisplay[:newlinePosition]) - else: - sent.item(i, 3).setText(textToDisplay) + for sent in [self.ui.tableWidgetInbox, self.ui.tableWidgetInboxSubscriptions, self.ui.tableWidgetInboxChans]: + treeWidget = self.widgetConvert(sent) + if self.getCurrentFolder(treeWidget) != "sent": + continue + if treeWidget in [self.ui.treeWidgetSubscriptions, self.ui.treeWidgetChans] and self.getCurrentAccount(treeWidget) != toAddress: + continue + + for i in range(sent.rowCount()): + rowAddress = str(sent.item( + i, 0).data(Qt.UserRole).toPyObject()) + if toAddress == rowAddress: + sent.item(i, 3).setToolTip(textToDisplay) + try: + newlinePosition = textToDisplay.indexOf('\n') + except: # If someone misses adding a "_translate" to a string before passing it to this function, this function won't receive a qstring which will cause an exception. + newlinePosition = 0 + if newlinePosition > 1: + sent.item(i, 3).setText( + textToDisplay[:newlinePosition]) + else: + sent.item(i, 3).setText(textToDisplay) def updateSentItemStatusByAckdata(self, ackdata, textToDisplay): - for i in range(self.ui.tableWidgetInbox.rowCount()): - toAddress = str(self.ui.tableWidgetInbox.item( - i, 0).data(Qt.UserRole).toPyObject()) - tableAckdata = self.ui.tableWidgetInbox.item( - i, 3).data(Qt.UserRole).toPyObject() - status, addressVersionNumber, streamNumber, ripe = decodeAddress( - toAddress) - if ackdata == tableAckdata: - self.ui.tableWidgetInbox.item(i, 3).setToolTip(textToDisplay) - try: - newlinePosition = textToDisplay.indexOf('\n') - except: # If someone misses adding a "_translate" to a string before passing it to this function, this function won't receive a qstring which will cause an exception. - newlinePosition = 0 - if newlinePosition > 1: - self.ui.tableWidgetInbox.item(i, 3).setText( - textToDisplay[:newlinePosition]) - else: - self.ui.tableWidgetInbox.item(i, 3).setText(textToDisplay) + for sent in [self.ui.tableWidgetInbox, self.ui.tableWidgetInboxSubscriptions, self.ui.tableWidgetInboxChans]: + treeWidget = self.widgetConvert(sent) + if self.getCurrentFolder(treeWidget) != "sent": + continue + for i in range(sent.rowCount()): + toAddress = str(sent.item( + i, 0).data(Qt.UserRole).toPyObject()) + tableAckdata = sent.item( + i, 3).data(Qt.UserRole).toPyObject() + status, addressVersionNumber, streamNumber, ripe = decodeAddress( + toAddress) + if ackdata == tableAckdata: + sent.item(i, 3).setToolTip(textToDisplay) + try: + newlinePosition = textToDisplay.indexOf('\n') + except: # If someone misses adding a "_translate" to a string before passing it to this function, this function won't receive a qstring which will cause an exception. + newlinePosition = 0 + if newlinePosition > 1: + sent.item(i, 3).setText( + textToDisplay[:newlinePosition]) + else: + sent.item(i, 3).setText(textToDisplay) def removeInboxRowByMsgid(self, msgid): # msgid and inventoryHash are the same thing - def widgetConvert (tableWidget): - if tableWidget == self.ui.tableWidgetInbox: - return self.ui.treeWidgetYourIdentities - elif tableWidget == self.ui.tableWidgetInboxSubscriptions: - return self.ui.treeWidgetSubscriptions - elif tableWidget == self.ui.tableWidgetInboxChans: - return self.ui.treeWidgetChans - else: - return None - for inbox in ([ self.ui.tableWidgetInbox, self.ui.tableWidgetInboxSubscriptions, @@ -1866,7 +1863,7 @@ class MyForm(settingsmixin.SMainWindow): if msgid == str(inbox.item(i, 3).data(Qt.UserRole).toPyObject()): self.statusBar().showMessage(_translate( "MainWindow", "Message trashed")) - treeWidget = widgetConvert(inbox) + treeWidget = self.widgetConvert(inbox) self.propagateUnreadCount(self.getCurrentAccount(treeWidget), self.getCurrentFolder(treeWidget), treeWidget, 0) inbox.removeRow(i) break @@ -2327,39 +2324,43 @@ class MyForm(settingsmixin.SMainWindow): message = shared.fixPotentiallyInvalidUTF8Data(message) acct = accountClass(fromAddress) acct.parseMessage(toAddress, fromAddress, subject, message) - sent = self.getAccountMessagelist(acct) - treeWidget = self.getAccountTreeWidget(acct) - if self.getCurrentFolder(treeWidget) != "sent" or self.getCurrentAccount(treeWidget) != fromAddress: - return + for sent in [self.ui.tableWidgetInbox, self.ui.tableWidgetInboxSubscriptions, self.ui.tableWidgetInboxChans]: + treeWidget = self.widgetConvert(sent) + if self.getCurrentFolder(treeWidget) != "sent": + continue + if treeWidget == self.ui.treeWidgetYourIdentities and self.getCurrentAccount(treeWidget) != fromAddress: + continue + elif treeWidget in [self.ui.treeWidgetSubscriptions, self.ui.treeWidgetChans] and self.getCurrentAccount(treeWidget) != toAddress: + continue - sent.setSortingEnabled(False) - sent.insertRow(0) - newItem = QtGui.QTableWidgetItem(unicode(acct.toLabel, 'utf-8')) - newItem.setToolTip(unicode(acct.toLabel, 'utf-8')) - newItem.setData(Qt.UserRole, str(toAddress)) - newItem.setIcon(avatarize(toAddress)) - sent.setItem(0, 0, newItem) - newItem = QtGui.QTableWidgetItem(unicode(acct.fromLabel, 'utf-8')) - newItem.setToolTip(unicode(acct.fromLabel, 'utf-8')) - newItem.setData(Qt.UserRole, str(fromAddress)) - newItem.setIcon(avatarize(fromAddress)) - sent.setItem(0, 1, newItem) - newItem = QtGui.QTableWidgetItem(unicode(acct.subject, 'utf-8)')) - newItem.setToolTip(unicode(acct.subject, 'utf-8)')) - newItem.setData(Qt.UserRole, str(subject)) + sent.setSortingEnabled(False) + sent.insertRow(0) + newItem = QtGui.QTableWidgetItem(unicode(acct.toLabel, 'utf-8')) + newItem.setToolTip(unicode(acct.toLabel, 'utf-8')) + newItem.setData(Qt.UserRole, str(toAddress)) + newItem.setIcon(avatarize(toAddress)) + sent.setItem(0, 0, newItem) + newItem = QtGui.QTableWidgetItem(unicode(acct.fromLabel, 'utf-8')) + newItem.setToolTip(unicode(acct.fromLabel, 'utf-8')) + newItem.setData(Qt.UserRole, str(fromAddress)) + newItem.setIcon(avatarize(fromAddress)) + sent.setItem(0, 1, newItem) + newItem = QtGui.QTableWidgetItem(unicode(acct.subject, 'utf-8)')) + newItem.setToolTip(unicode(acct.subject, 'utf-8)')) + newItem.setData(Qt.UserRole, str(subject)) - #newItem.setData(Qt.UserRole, unicode(message, 'utf-8)')) # No longer hold the message in the table; we'll use a SQL query to display it as needed. - sent.setItem(0, 2, newItem) - # newItem = QtGui.QTableWidgetItem('Doing work necessary to send - # broadcast...'+ - # l10n.formatTimestamp()) - newItem = myTableWidgetItem(_translate("MainWindow", "Work is queued. %1").arg(l10n.formatTimestamp())) - newItem.setToolTip(_translate("MainWindow", "Work is queued. %1").arg(l10n.formatTimestamp())) - newItem.setData(Qt.UserRole, QByteArray(ackdata)) - newItem.setData(33, int(time.time())) - sent.setItem(0, 3, newItem) - self.getAccountTextedit(acct).setPlainText(unicode(message, 'utf-8)')) - sent.setSortingEnabled(True) + #newItem.setData(Qt.UserRole, unicode(message, 'utf-8)')) # No longer hold the message in the table; we'll use a SQL query to display it as needed. + sent.setItem(0, 2, newItem) + # newItem = QtGui.QTableWidgetItem('Doing work necessary to send + # broadcast...'+ + # l10n.formatTimestamp()) + newItem = myTableWidgetItem(_translate("MainWindow", "Work is queued. %1").arg(l10n.formatTimestamp())) + newItem.setToolTip(_translate("MainWindow", "Work is queued. %1").arg(l10n.formatTimestamp())) + newItem.setData(Qt.UserRole, QByteArray(ackdata)) + newItem.setData(33, int(time.time())) + sent.setItem(0, 3, newItem) + self.getAccountTextedit(acct).setPlainText(unicode(message, 'utf-8)')) + sent.setSortingEnabled(True) def displayNewInboxMessage(self, inventoryHash, toAddress, fromAddress, subject, message): subject = shared.fixPotentiallyInvalidUTF8Data(subject) @@ -3422,6 +3423,22 @@ class MyForm(settingsmixin.SMainWindow): sqlExecute( '''UPDATE whitelist SET enabled=0 WHERE address=?''', str(addressAtCurrentRow)) + def widgetConvert (self, widget): + if widget == self.ui.tableWidgetInbox: + return self.ui.treeWidgetYourIdentities + elif widget == self.ui.tableWidgetInboxSubscriptions: + return self.ui.treeWidgetSubscriptions + elif widget == self.ui.tableWidgetInboxChans: + return self.ui.treeWidgetChans + elif widget == self.ui.treeWidgetYourIdentities: + return self.ui.tableWidgetInbox + elif widget == self.ui.treeWidgetSubscriptions: + return self.ui.tableWidgetInboxSubscriptions + elif twidget == self.ui.treeWidgetChans: + return self.ui.tableWidgetInboxChans + else: + return None + def getCurrentTreeWidget(self): currentIndex = self.ui.tabWidget.currentIndex(); treeWidgetList = [ From 33b4f896f619ccc5e26b19c8ff19819b9094f7a3 Mon Sep 17 00:00:00 2001 From: Henrik Olsson Date: Mon, 9 Nov 2015 19:38:16 +0100 Subject: [PATCH 124/210] Fix some typos Author: Henrik Olsson --- src/class_singleWorker.py | 2 +- src/helper_startup.py | 2 +- src/translations/bitmessage_de.ts | 2 +- src/translations/bitmessage_eo.ts | 2 +- src/translations/bitmessage_zh_cn.ts | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/class_singleWorker.py b/src/class_singleWorker.py index 327f526a..888d96f6 100644 --- a/src/class_singleWorker.py +++ b/src/class_singleWorker.py @@ -948,7 +948,7 @@ class singleWorker(threading.Thread): toAddress) shared.UISignalQueue.put(( - 'updateStatusBar', tr.translateText("MainWindow",'Broacasting the public key request. This program will auto-retry if they are offline.'))) + 'updateStatusBar', tr.translateText("MainWindow",'Broadcasting the public key request. This program will auto-retry if they are offline.'))) shared.UISignalQueue.put(('updateSentItemStatusByToAddress', (toAddress, tr.translateText("MainWindow",'Sending public key request. Waiting for reply. Requested at %1').arg(l10n.formatTimestamp())))) def generateFullAckMessage(self, ackdata, toStreamNumber, TTL): diff --git a/src/helper_startup.py b/src/helper_startup.py index 778fe70a..0d4e0cb0 100644 --- a/src/helper_startup.py +++ b/src/helper_startup.py @@ -145,5 +145,5 @@ def isOurOperatingSystemLimitedToHavingVeryFewHalfOpenConnections(): return StrictVersion("5.1.2600")<=VER_THIS and StrictVersion("6.0.6000")>=VER_THIS return False except Exception as err: - print "Info: we could not tell whether your OS is limited to having very view half open connections because we couldn't interpret the platform version. Don't worry; we'll assume that it is not limited. This tends to occur on Raspberry Pis. :", err + print "Info: we could not tell whether your OS is limited to having very few half open connections because we couldn't interpret the platform version. Don't worry; we'll assume that it is not limited. This tends to occur on Raspberry Pis. :", err return False diff --git a/src/translations/bitmessage_de.ts b/src/translations/bitmessage_de.ts index 1c817a98..3e0a2546 100644 --- a/src/translations/bitmessage_de.ts +++ b/src/translations/bitmessage_de.ts @@ -905,7 +905,7 @@ p, li { white-space: pre-wrap; } - Broacasting the public key request. This program will auto-retry if they are offline. + Broadcasting the public key request. This program will auto-retry if they are offline. Anfrage für den Verschlüsselungscode versendet (wird automatisch periodisch neu verschickt). diff --git a/src/translations/bitmessage_eo.ts b/src/translations/bitmessage_eo.ts index 00e33f9e..8fac557f 100644 --- a/src/translations/bitmessage_eo.ts +++ b/src/translations/bitmessage_eo.ts @@ -899,7 +899,7 @@ p, li { white-space: pre-wrap; } - Broacasting the public key request. This program will auto-retry if they are offline. + Broadcasting the public key request. This program will auto-retry if they are offline. Anfrage für den Verschlüsselungscode versendet (wird automatisch periodisch neu verschickt). diff --git a/src/translations/bitmessage_zh_cn.ts b/src/translations/bitmessage_zh_cn.ts index 40c648e9..0b580e65 100644 --- a/src/translations/bitmessage_zh_cn.ts +++ b/src/translations/bitmessage_zh_cn.ts @@ -1065,7 +1065,7 @@ Receiver's required difficulty: %1 and %2 - Broacasting the public key request. This program will auto-retry if they are offline. + Broadcasting the public key request. This program will auto-retry if they are offline. 正在广播公钥请求. 如果他们不在线, 这一过程将自动重试. From 891e3d6517e4a76367802b97397c5038a5ca3b4c Mon Sep 17 00:00:00 2001 From: mailchuck Date: Tue, 10 Nov 2015 13:15:07 +0100 Subject: [PATCH 125/210] Unregistering of unregeistered accounts Since it's unregistered, we don't know where to unregister from. Fixes #91. --- src/bitmessageqt/__init__.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index f73f8243..73d1fc2b 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2802,7 +2802,7 @@ class MyForm(settingsmixin.SMainWindow): # no chans / mailinglists if acct.type != 'normal': return - if self.dialog.ui.radioButtonUnregister.isChecked(): + if self.dialog.ui.radioButtonUnregister.isChecked() and isinstance(acct, GatewayAccount): print "unregister" acct.unregister() shared.config.remove_option(addressAtCurrentRow, 'gateway') @@ -4243,7 +4243,10 @@ class EmailGatewayDialog(QtGui.QDialog): self.parent = parent addressAtCurrentRow = parent.getCurrentAccount() acct = accountClass(addressAtCurrentRow) -# if isinstance(acct, GatewayAccount): + if isinstance(acct, GatewayAccount): + self.ui.radioButtonUnregister.setEnabled(True) + else: + self.ui.radioButtonUnregister.setEnabled(False) label = shared.config.get(addressAtCurrentRow, 'label') if label.find("@mailchuck.com") > -1: self.ui.lineEditEmail.setText(label) From 78734d81a17bcd3672db6fc764b380c2813634d4 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Wed, 11 Nov 2015 00:51:55 +0100 Subject: [PATCH 126/210] Default message font Fixes #106 and Bitmesage#248 --- src/bitmessageqt/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 73d1fc2b..447b8d9b 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -3915,6 +3915,8 @@ class MyForm(settingsmixin.SMainWindow): if data != False: message = "Error occurred: could not load message from disk." message = unicode(message, 'utf-8)') + messageTextedit.setCurrentFont(QtGui.QFont()) + messageTextedit.setTextColor(QtGui.QColor()) messageTextedit.setPlainText(message) def tableWidgetAddressBookItemChanged(self): From 23b8811addf3c2823e098e7a66475b1615b0048d Mon Sep 17 00:00:00 2001 From: mailchuck Date: Wed, 11 Nov 2015 01:02:23 +0100 Subject: [PATCH 127/210] Enabling/disabling color propagation Fixes #105 --- src/bitmessageqt/foldertree.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/bitmessageqt/foldertree.py b/src/bitmessageqt/foldertree.py index 8bbba0c9..c27cf6c5 100644 --- a/src/bitmessageqt/foldertree.py +++ b/src/bitmessageqt/foldertree.py @@ -42,6 +42,10 @@ class AccountMixin (object): self.isEnabled = enabled if hasattr(self, "setExpanded"): self.setExpanded(enabled) + if isinstance(self, Ui_AddressWidget): + for i in range(self.childCount()): + if isinstance(self.child(i), Ui_FolderWidget): + self.child(i).setEnabled(enabled) self.updateText() def setType(self): From df54bbad81d4b2fa1b419d848432a0969d234e2c Mon Sep 17 00:00:00 2001 From: mailchuck Date: Wed, 11 Nov 2015 02:42:03 +0100 Subject: [PATCH 128/210] Translation directory for OSX bundles Addresses bitmessage#514 --- src/bitmessageqt/__init__.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 447b8d9b..3d68f37d 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -67,11 +67,13 @@ def _translate(context, text): def change_translation(locale): global qtranslator qtranslator = QtCore.QTranslator() - translationpath = os.path.join( - getattr(sys, '_MEIPASS', sys.path[0]), - 'translations', - 'bitmessage_' + locale - ) + if shared.frozen == "macosx_app": + translationpath = os.environ.get("RESOURCEPATH") + elif shared.frozen: # windows + translationpath = sys._MEIPASS + else: + translationpath = os.path.dirname(os.path.dirname(__file__)) + translationpath = os.path.join (translationpath, 'translations', 'bitmessage_' + locale) qtranslator.load(translationpath) QtGui.QApplication.installTranslator(qtranslator) From 6fef09026166a1932d5ed2f252494c5e7eb15c3b Mon Sep 17 00:00:00 2001 From: mailchuck Date: Thu, 12 Nov 2015 00:33:57 +0100 Subject: [PATCH 129/210] Prepare for tree widgets to store settings Precursor for #12 --- src/bitmessageqt/bitmessageui.py | 6 +++--- src/bitmessageqt/foldertree.py | 3 ++- src/bitmessageqt/settingsmixin.py | 14 +++++++++++++- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/bitmessageqt/bitmessageui.py b/src/bitmessageqt/bitmessageui.py index 269ec099..230104b3 100644 --- a/src/bitmessageqt/bitmessageui.py +++ b/src/bitmessageqt/bitmessageui.py @@ -59,7 +59,7 @@ class Ui_MainWindow(object): self.verticalSplitter_12 = settingsmixin.SSplitter() self.verticalSplitter_12.setObjectName(_fromUtf8("verticalSplitter_12")) self.verticalSplitter_12.setOrientation(QtCore.Qt.Vertical) - self.treeWidgetYourIdentities = QtGui.QTreeWidget(self.inbox) + self.treeWidgetYourIdentities = settingsmixin.STreeWidget(self.inbox) self.treeWidgetYourIdentities.setObjectName(_fromUtf8("treeWidgetYourIdentities")) self.treeWidgetYourIdentities.resize(200, self.treeWidgetYourIdentities.height()) icon1 = QtGui.QIcon() @@ -361,7 +361,7 @@ class Ui_MainWindow(object): self.verticalSplitter_3 = settingsmixin.SSplitter() self.verticalSplitter_3.setObjectName(_fromUtf8("verticalSplitter_3")) self.verticalSplitter_3.setOrientation(QtCore.Qt.Vertical) - self.treeWidgetSubscriptions = QtGui.QTreeWidget(self.subscriptions) + self.treeWidgetSubscriptions = settingsmixin.STreeWidget(self.subscriptions) self.treeWidgetSubscriptions.setAlternatingRowColors(True) self.treeWidgetSubscriptions.setSelectionMode(QtGui.QAbstractItemView.SingleSelection) self.treeWidgetSubscriptions.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) @@ -458,7 +458,7 @@ class Ui_MainWindow(object): self.verticalSplitter_17 = settingsmixin.SSplitter() self.verticalSplitter_17.setObjectName(_fromUtf8("verticalSplitter_17")) self.verticalSplitter_17.setOrientation(QtCore.Qt.Vertical) - self.treeWidgetChans = QtGui.QTreeWidget(self.chans) + self.treeWidgetChans = settingsmixin.STreeWidget(self.chans) self.treeWidgetChans.setFrameShadow(QtGui.QFrame.Sunken) self.treeWidgetChans.setLineWidth(1) self.treeWidgetChans.setAlternatingRowColors(True) diff --git a/src/bitmessageqt/foldertree.py b/src/bitmessageqt/foldertree.py index c27cf6c5..59f16667 100644 --- a/src/bitmessageqt/foldertree.py +++ b/src/bitmessageqt/foldertree.py @@ -2,6 +2,7 @@ from PyQt4 import QtCore, QtGui from utils import * import shared +from settingsmixin import SettingsMixin class AccountMixin (object): def accountColor (self): @@ -116,7 +117,7 @@ class Ui_FolderWidget(QtGui.QTreeWidgetItem, AccountMixin): return super(QtGui.QTreeWidgetItem, self).__lt__(other) -class Ui_AddressWidget(QtGui.QTreeWidgetItem, AccountMixin): +class Ui_AddressWidget(QtGui.QTreeWidgetItem, AccountMixin, SettingsMixin): def __init__(self, parent, pos = 0, address = "", unreadCount = 0, enabled = True): super(QtGui.QTreeWidgetItem, self).__init__() parent.insertTopLevelItem(pos, self) diff --git a/src/bitmessageqt/settingsmixin.py b/src/bitmessageqt/settingsmixin.py index 75822fa4..c534d1b5 100644 --- a/src/bitmessageqt/settingsmixin.py +++ b/src/bitmessageqt/settingsmixin.py @@ -64,4 +64,16 @@ class SSplitter(QtGui.QSplitter, SettingsMixin): self.readState(self) def saveSettings(self): - self.writeState(self) \ No newline at end of file + self.writeState(self) + + +class STreeWidget(QtGui.QTreeWidget, SettingsMixin): + def loadSettings(self): + #recurse children + #self.readState(self) + pass + + def saveSettings(self): + #recurse children + #self.writeState(self) + pass From 53062f61b9eff29059bad81237161c8d4ee81ca1 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Thu, 12 Nov 2015 00:54:22 +0100 Subject: [PATCH 130/210] Show sync status in Network tab Addresses Bitmessage#745 (but no API exposure yet) --- src/bitmessageqt/__init__.py | 3 +++ src/bitmessageqt/bitmessageui.py | 10 +++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 3d68f37d..37892aa6 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -1605,14 +1605,17 @@ class MyForm(settingsmixin.SMainWindow): self.appIndicatorShowOrHideWindow() def updateNumberOfMessagesProcessed(self): + self.ui.labelSyncStatus.setText(_translate("MainWindow", "Objects to be synced: %1").arg(str(sum(shared.numberOfObjectsThatWeHaveYetToGetPerPeer)))) self.ui.labelMessageCount.setText(_translate( "MainWindow", "Processed %1 person-to-person messages.").arg(str(shared.numberOfMessagesProcessed))) def updateNumberOfBroadcastsProcessed(self): + self.ui.labelSyncStatus.setText(_translate("MainWindow", "Objects to be synced: %1").arg(str(sum(shared.numberOfObjectsThatWeHaveYetToGetPerPeer)))) self.ui.labelBroadcastCount.setText(_translate( "MainWindow", "Processed %1 broadcast messages.").arg(str(shared.numberOfBroadcastsProcessed))) def updateNumberOfPubkeysProcessed(self): + self.ui.labelSyncStatus.setText(_translate("MainWindow", "Objects to be synced: %1").arg(str(sum(shared.numberOfObjectsThatWeHaveYetToGetPerPeer)))) self.ui.labelPubkeyCount.setText(_translate( "MainWindow", "Processed %1 public keys.").arg(str(shared.numberOfPubkeysProcessed))) diff --git a/src/bitmessageqt/bitmessageui.py b/src/bitmessageqt/bitmessageui.py index 230104b3..e303567f 100644 --- a/src/bitmessageqt/bitmessageui.py +++ b/src/bitmessageqt/bitmessageui.py @@ -639,14 +639,17 @@ class Ui_MainWindow(object): self.labelBroadcastCount = QtGui.QLabel(self.networkstatus) self.labelBroadcastCount.setGeometry(QtCore.QRect(350, 150, 351, 16)) self.labelBroadcastCount.setObjectName(_fromUtf8("labelBroadcastCount")) + self.labelSyncStatus = QtGui.QLabel(self.networkstatus) + self.labelSyncStatus.setGeometry(QtCore.QRect(350, 190, 331, 20)) + self.labelSyncStatus.setObjectName(_fromUtf8("labelSyncStatus")) self.labelLookupsPerSecond = QtGui.QLabel(self.networkstatus) - self.labelLookupsPerSecond.setGeometry(QtCore.QRect(320, 250, 291, 16)) + self.labelLookupsPerSecond.setGeometry(QtCore.QRect(320, 270, 291, 16)) self.labelLookupsPerSecond.setObjectName(_fromUtf8("labelLookupsPerSecond")) self.labelBytesRecvCount = QtGui.QLabel(self.networkstatus) - self.labelBytesRecvCount.setGeometry(QtCore.QRect(350, 210, 251, 16)) + self.labelBytesRecvCount.setGeometry(QtCore.QRect(350, 230, 251, 16)) self.labelBytesRecvCount.setObjectName(_fromUtf8("labelBytesRecvCount")) self.labelBytesSentCount = QtGui.QLabel(self.networkstatus) - self.labelBytesSentCount.setGeometry(QtCore.QRect(350, 230, 251, 16)) + self.labelBytesSentCount.setGeometry(QtCore.QRect(350, 250, 251, 16)) self.labelBytesSentCount.setObjectName(_fromUtf8("labelBytesSentCount")) icon11 = QtGui.QIcon() icon11.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/networkstatus.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) @@ -830,6 +833,7 @@ class Ui_MainWindow(object): item.setText(_translate("MainWindow", "Connections", None)) self.labelTotalConnections.setText(_translate("MainWindow", "Total connections:", None)) self.labelStartupTime.setText(_translate("MainWindow", "Since startup:", None)) + self.labelSyncStatus.setText(_translate("MainWindow", "Objects to be synced:", None)) self.labelMessageCount.setText(_translate("MainWindow", "Processed 0 person-to-person messages.", None)) self.labelPubkeyCount.setText(_translate("MainWindow", "Processed 0 public keys.", None)) self.labelBroadcastCount.setText(_translate("MainWindow", "Processed 0 broadcasts.", None)) From f933cea697d47f700ab36c566fc6a6e31f36cfb3 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Thu, 12 Nov 2015 01:00:27 +0100 Subject: [PATCH 131/210] Fix sync status sum Last commit was broken. --- src/bitmessageqt/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 37892aa6..e47d15ee 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -1605,17 +1605,17 @@ class MyForm(settingsmixin.SMainWindow): self.appIndicatorShowOrHideWindow() def updateNumberOfMessagesProcessed(self): - self.ui.labelSyncStatus.setText(_translate("MainWindow", "Objects to be synced: %1").arg(str(sum(shared.numberOfObjectsThatWeHaveYetToGetPerPeer)))) + self.ui.labelSyncStatus.setText(_translate("MainWindow", "Objects to be synced: %1").arg(str(sum(shared.numberOfObjectsThatWeHaveYetToGetPerPeer.itervalues())))) self.ui.labelMessageCount.setText(_translate( "MainWindow", "Processed %1 person-to-person messages.").arg(str(shared.numberOfMessagesProcessed))) def updateNumberOfBroadcastsProcessed(self): - self.ui.labelSyncStatus.setText(_translate("MainWindow", "Objects to be synced: %1").arg(str(sum(shared.numberOfObjectsThatWeHaveYetToGetPerPeer)))) + self.ui.labelSyncStatus.setText(_translate("MainWindow", "Objects to be synced: %1").arg(str(sum(shared.numberOfObjectsThatWeHaveYetToGetPerPeer.itervalues())))) self.ui.labelBroadcastCount.setText(_translate( "MainWindow", "Processed %1 broadcast messages.").arg(str(shared.numberOfBroadcastsProcessed))) def updateNumberOfPubkeysProcessed(self): - self.ui.labelSyncStatus.setText(_translate("MainWindow", "Objects to be synced: %1").arg(str(sum(shared.numberOfObjectsThatWeHaveYetToGetPerPeer)))) + self.ui.labelSyncStatus.setText(_translate("MainWindow", "Objects to be synced: %1").arg(str(sum(shared.numberOfObjectsThatWeHaveYetToGetPerPeer.itervalues())))) self.ui.labelPubkeyCount.setText(_translate( "MainWindow", "Processed %1 public keys.").arg(str(shared.numberOfPubkeysProcessed))) From a50c5ed16e419540b044c7942633e731af7b4fb0 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Thu, 12 Nov 2015 01:42:20 +0100 Subject: [PATCH 132/210] Fix broadcast Any normal address should be able to broadcast. Reported over BM in one of the chans. --- src/bitmessageqt/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index e47d15ee..0da7b7a9 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2307,8 +2307,8 @@ class MyForm(settingsmixin.SMainWindow): for addressInKeysFile in getSortedAccounts(): isEnabled = shared.config.getboolean( addressInKeysFile, 'enabled') # I realize that this is poor programming practice but I don't care. It's easier for others to read. - isMaillinglist = shared.safeConfigGetBoolean(addressInKeysFile, 'mailinglist') - if isEnabled and isMaillinglist: + isChan = shared.safeConfigGetBoolean(addressInKeysFile, 'chan') + if isEnabled and not isChan: self.ui.comboBoxSendFromBroadcast.addItem(avatarize(addressInKeysFile), unicode(shared.config.get( addressInKeysFile, 'label'), 'utf-8'), addressInKeysFile) for i in range(self.ui.comboBoxSendFromBroadcast.count()): From 7189a826ada317217e30a361c7f2b76fd9897603 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Thu, 12 Nov 2015 01:43:06 +0100 Subject: [PATCH 133/210] Click on inactive tab Would cause exception. --- src/bitmessageqt/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 0da7b7a9..7c579918 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -3874,6 +3874,8 @@ class MyForm(settingsmixin.SMainWindow): def tableWidgetInboxItemClicked(self): folder = self.getCurrentFolder() messageTextedit = self.getCurrentMessageTextedit() + if not messageTextedit: + return queryreturn = [] message = "" From 9daac4b06103cc589ae96d989c72c7a0d9fd2312 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Thu, 12 Nov 2015 17:36:12 +0100 Subject: [PATCH 134/210] Private IP range update Addresses Bitmessage#768 --- src/class_receiveDataThread.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/class_receiveDataThread.py b/src/class_receiveDataThread.py index 07569b7b..19135c1f 100644 --- a/src/class_receiveDataThread.py +++ b/src/class_receiveDataThread.py @@ -485,13 +485,16 @@ class receiveDataThread(threading.Thread): def _checkIPv4Address(self, host, hostStandardFormat): # print 'hostStandardFormat', hostStandardFormat - if host[0] == '\x7F': + if host[0] == '\x7F': # 127/8 print 'Ignoring IP address in loopback range:', hostStandardFormat return False - if host[0] == '\x0A': + if host[0] == '\x0A': # 10/8 print 'Ignoring IP address in private range:', hostStandardFormat return False - if host[0:2] == '\xC0\xA8': + if host[0:2] == '\xC0\xA8': # 192.168/16 + print 'Ignoring IP address in private range:', hostStandardFormat + return False + if host[0:2] >= '\xAC\x10' and host[0:2] < '\xAC\x20': # 172.16/12 print 'Ignoring IP address in private range:', hostStandardFormat return False return True From cdaedaac0bd99cae81c28f65bcf39a07a3bff6af Mon Sep 17 00:00:00 2001 From: mailchuck Date: Fri, 13 Nov 2015 12:25:50 +0100 Subject: [PATCH 135/210] Unified code for finding source code location It is already used in 3 places so I put it into a function --- src/bitmessageqt/__init__.py | 8 +------- src/proofofwork.py | 12 +++--------- src/shared.py | 9 +++++++++ 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 7c579918..8cd8cbda 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -67,13 +67,7 @@ def _translate(context, text): def change_translation(locale): global qtranslator qtranslator = QtCore.QTranslator() - if shared.frozen == "macosx_app": - translationpath = os.environ.get("RESOURCEPATH") - elif shared.frozen: # windows - translationpath = sys._MEIPASS - else: - translationpath = os.path.dirname(os.path.dirname(__file__)) - translationpath = os.path.join (translationpath, 'translations', 'bitmessage_' + locale) + translationpath = os.path.join (shared.codePath(), 'translations', 'bitmessage_' + locale) qtranslator.load(translationpath) QtGui.QApplication.installTranslator(qtranslator) diff --git a/src/proofofwork.py b/src/proofofwork.py index 04e07af3..fc779467 100644 --- a/src/proofofwork.py +++ b/src/proofofwork.py @@ -4,18 +4,12 @@ import hashlib from struct import unpack, pack import sys -from shared import config, frozen +from shared import config, frozen, codePath import shared import openclpow import os import ctypes -if frozen == "macosx_app": - curdir = os.environ.get("RESOURCEPATH") -elif frozen: # windows - curdir = sys._MEIPASS -else: - curdir = os.path.dirname(__file__) bitmsglib = 'bitmsghash.so' if "win32" == sys.platform: if ctypes.sizeof(ctypes.c_voidp) == 4: @@ -23,12 +17,12 @@ if "win32" == sys.platform: else: bitmsglib = 'bitmsghash64.dll' try: - bso = ctypes.WinDLL(os.path.join(curdir, "bitmsghash", bitmsglib)) + bso = ctypes.WinDLL(os.path.join(codePath(), "bitmsghash", bitmsglib)) except: bso = None else: try: - bso = ctypes.CDLL(os.path.join(curdir, "bitmsghash", bitmsglib)) + bso = ctypes.CDLL(os.path.join(codePath(), "bitmsghash", bitmsglib)) except: bso = None if bso: diff --git a/src/shared.py b/src/shared.py index 342a7315..cacb669f 100644 --- a/src/shared.py +++ b/src/shared.py @@ -217,6 +217,15 @@ def lookupAppdataFolder(): pass dataFolder = dataFolder + '/' return dataFolder + +def codePath(): + if frozen == "macosx_app": + codePath = os.environ.get("RESOURCEPATH") + elif frozen: # windows + codePath = sys._MEIPASS + else: + codePath = os.path.dirname(__file__) + return codePath def isAddressInMyAddressBook(address): queryreturn = sqlQuery( From 868544955612d0d966ba881aa541e3067df3744d Mon Sep 17 00:00:00 2001 From: mailchuck Date: Fri, 13 Nov 2015 12:32:10 +0100 Subject: [PATCH 136/210] Opportunistic encryption with TLS (1 of 2) Fixes Bitmessage#264 Fixes Bitmessage#648 --- src/class_receiveDataThread.py | 32 ++++++++++++++++++++++++++++++-- src/class_sendDataThread.py | 7 ++++++- src/shared.py | 2 +- 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/src/class_receiveDataThread.py b/src/class_receiveDataThread.py index 19135c1f..737872d1 100644 --- a/src/class_receiveDataThread.py +++ b/src/class_receiveDataThread.py @@ -1,11 +1,15 @@ doTimingAttackMitigation = True +import errno import time import threading import shared import hashlib +import os +import select import socket import random +import ssl from struct import unpack, pack import sys import traceback @@ -49,6 +53,7 @@ class receiveDataThread(threading.Thread): shared.connectedHostsList[ self.peer.host] = 0 # The very fact that this receiveData thread exists shows that we are connected to the remote host. Let's add it to this list so that an outgoingSynSender thread doesn't try to connect to it. self.connectionIsOrWasFullyEstablished = False # set to true after the remote node and I accept each other's version messages. This is needed to allow the user interface to accurately reflect the current number of connections. + self.services = 0 if self.streamNumber == -1: # This was an incoming connection. Send out a version message if we accept the other node's version message. self.initiatedConnection = False else: @@ -76,7 +81,10 @@ class receiveDataThread(threading.Thread): shared.numberOfBytesReceivedLastSecond = 0 dataLen = len(self.data) try: - dataRecv = self.sock.recv(1024) + if (self.services & 2 == 2) and self.connectionIsOrWasFullyEstablished: + dataRecv = self.sslSock.recv(1024) + else: + dataRecv = self.sock.recv(1024) self.data += dataRecv shared.numberOfBytesReceived += len(dataRecv) # for the 'network status' UI tab. The UI clears this value whenever it updates. shared.numberOfBytesReceivedLastSecond += len(dataRecv) # for the download rate limit @@ -85,6 +93,9 @@ class receiveDataThread(threading.Thread): print 'Timeout occurred waiting for data from', self.peer, '. Closing receiveData thread. (ID:', str(id(self)) + ')' break except Exception as err: + if (sys.platform == 'win32' and err.errno == 10035) or (sys.platform != 'win32' and err.errno == errno.EWOULDBLOCK): + select.select([self.sslSock], [], []) + continue with shared.printLock: print 'sock.recv error. Closing receiveData thread (' + str(self.peer) + ', Thread ID:', str(id(self)) + ').', err break @@ -252,8 +263,24 @@ class receiveDataThread(threading.Thread): # there is no reason to run this function a second time return self.connectionIsOrWasFullyEstablished = True + + self.sslSock = self.sock + if (self.services & 2 == 2): + self.sslSock = ssl.wrap_socket(self.sock, keyfile = os.path.join(shared.codePath(), 'sslkeys', 'key.pem'), certfile = os.path.join(shared.codePath(), 'sslkeys', 'cert.pem'), server_side = not self.initiatedConnection, ssl_version=ssl.PROTOCOL_SSLv23, do_handshake_on_connect=False, ciphers='AECDH-AES256-SHA') + while True: + try: + self.sslSock.do_handshake() + break + except ssl.SSLError as e: + if e.errno == 2: + select.select([self.sslSock], [self.sslSock], []) + else: + break + except: + break # Command the corresponding sendDataThread to set its own connectionIsOrWasFullyEstablished variable to True also - self.sendDataThreadQueue.put((0, 'connectionIsOrWasFullyEstablished', 'no data')) + self.sendDataThreadQueue.put((0, 'connectionIsOrWasFullyEstablished', (self.services, self.sslSock))) + if not self.initiatedConnection: shared.clientHasReceivedIncomingConnections = True shared.UISignalQueue.put(('setStatusIcon', 'green')) @@ -674,6 +701,7 @@ class receiveDataThread(threading.Thread): """ return self.remoteProtocolVersion, = unpack('>L', data[:4]) + self.services, = unpack('>q', data[4:12]) if self.remoteProtocolVersion < 3: self.sendDataThreadQueue.put((0, 'shutdown','no data')) with shared.printLock: diff --git a/src/class_sendDataThread.py b/src/class_sendDataThread.py index bb19c9f6..fedd4833 100644 --- a/src/class_sendDataThread.py +++ b/src/class_sendDataThread.py @@ -36,6 +36,7 @@ class sendDataThread(threading.Thread): self.sock = sock self.peer = shared.Peer(HOST, PORT) self.streamNumber = streamNumber + self.services = 0 self.remoteProtocolVersion = - \ 1 # This must be set using setRemoteProtocolVersion command which is sent through the self.sendDataThreadQueue queue. self.lastTimeISentData = int( @@ -82,7 +83,10 @@ class sendDataThread(threading.Thread): uploadRateLimitBytes = 999999999 # float("inf") doesn't work else: uploadRateLimitBytes = shared.config.getint('bitmessagesettings', 'maxuploadrate') * 1000 - amountSent = self.sock.send(data[:1000]) + if self.services & 2 == 2 and self.connectionIsOrWasFullyEstablished: + amountSent = self.sslSock.send(data[:1000]) + else: + amountSent = self.sock.send(data[:1000]) shared.numberOfBytesSent += amountSent # used for the 'network status' tab in the UI shared.numberOfBytesSentLastSecond += amountSent self.lastTimeISentData = int(time.time()) @@ -178,6 +182,7 @@ class sendDataThread(threading.Thread): break elif command == 'connectionIsOrWasFullyEstablished': self.connectionIsOrWasFullyEstablished = True + self.services, self.sslSock = data else: with shared.printLock: print 'sendDataThread ID:', id(self), 'ignoring command', command, 'because the thread is not in stream', deststream diff --git a/src/shared.py b/src/shared.py index cacb669f..eed70d10 100644 --- a/src/shared.py +++ b/src/shared.py @@ -144,7 +144,7 @@ def encodeHost(host): def assembleVersionMessage(remoteHost, remotePort, myStreamNumber): payload = '' payload += pack('>L', 3) # protocol version. - payload += pack('>q', 1) # bitflags of the services I offer. + payload += pack('>q', 3) # bitflags of the services I offer. payload += pack('>q', int(time.time())) payload += pack( From 93764eeaad4426542e6255df5eed0cecf8277be3 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Fri, 13 Nov 2015 12:35:28 +0100 Subject: [PATCH 137/210] Opportunistic encryption with TLS (2 of 2) These are dummy key and certificates. They are loaded but otherwise not used. This is a workaround against python's ssl_wrap deficiency, it won't allow to execute a server-side SSL handshake without a loaded key + cert. Since generating key/cert locally would require additional libraries, I opted for including a dummy one in the source. --- src/sslkeys/cert.pem | 15 +++++++++++++++ src/sslkeys/key.pem | 16 ++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 src/sslkeys/cert.pem create mode 100644 src/sslkeys/key.pem diff --git a/src/sslkeys/cert.pem b/src/sslkeys/cert.pem new file mode 100644 index 00000000..a976db75 --- /dev/null +++ b/src/sslkeys/cert.pem @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICWDCCAcGgAwIBAgIJAJs5yni/cDh5MA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQwHhcNMTUxMTEzMDk1NTU3WhcNMTUxMTE0MDk1NTU3WjBF +MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 +ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB +gQCg8XkFpIAYsTSBealTubvu4dzpMnnAOwANG5K9TJeclG9O65cmKWpH8k3hNDif +xagIAI8UanBsQo6SQrK1Iby2kz6DCKmySO1OwoNOOF0Ok31N+5aWsQvYF1wLbk2m +Ti/CSLWBgL25ywCCiP3Mgr+krapT4TrfvF4gCchUdcxMQQIDAQABo1AwTjAdBgNV +HQ4EFgQUWuFUJQC6zu6OTDgHZzhfZxsgJOMwHwYDVR0jBBgwFoAUWuFUJQC6zu6O +TDgHZzhfZxsgJOMwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOBgQAT1I/x +GbsYAE4pM4sVQrcuz7jLwr3k5Zve0z4WKR41W17Nc44G3DyLbkTWYESLfAYsivkx +tRRtYTtJm1qmTPtedXQJK+wJGNHCWRfwSB2CYwmO7+C2rYYzkFndN68kB6RJmyOr +eCX+9vkbQqgh7KfiNquJxCfMSDfhA2RszU43jg== +-----END CERTIFICATE----- diff --git a/src/sslkeys/key.pem b/src/sslkeys/key.pem new file mode 100644 index 00000000..a47bcc3c --- /dev/null +++ b/src/sslkeys/key.pem @@ -0,0 +1,16 @@ +-----BEGIN PRIVATE KEY----- +MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAKDxeQWkgBixNIF5 +qVO5u+7h3OkyecA7AA0bkr1Ml5yUb07rlyYpakfyTeE0OJ/FqAgAjxRqcGxCjpJC +srUhvLaTPoMIqbJI7U7Cg044XQ6TfU37lpaxC9gXXAtuTaZOL8JItYGAvbnLAIKI +/cyCv6StqlPhOt+8XiAJyFR1zExBAgMBAAECgYEAmd2hpQpayMCJgQsOHhRgnoXi +jDOMgIInj2CADmguPi0OqTXEoGBR0ozNdfNV+zGdbmESaSNFbcrHwP7xGQgzABlv +5ANLgBYrHnW/oFCCuw4Lj/CAAHRA4its+2wzf13BYoVitDiYBt3JMRqwLV03aHyy +Oqhvt2nVicz85+HERj0CQQDMJAPUIyOQLO+BPC5MsuxyQFJgie0aB5swumxanOv4 +J8GIvulNEJMG/dq+h/x4paV2LGDlUAOsBUmjXfTPMQAHAkEAydQtYorqYqhFZWWD +3lUMAoa8pGb6BfNXUqxdH0H8fk6B7OxYPpvwm7ce1lD1Oje3/+rMnn8i6A1p9HUy +l9wvdwJAdhxIUs7Z3qsBD8bgCuRixV/NyalDk5HfCnxyAKNWK8fkw9ehaEM0rhDm +JOLNAojkiND4ZvS6iyasCmdsIwx4tQJAAV+eR3NmkPFQN5ZvRU4S3NmJ4xyISw4S +5A8kOxg53aovHCunlhV9l7GxVggLAzBp4iX46oM2+5lLxUwe4gWvlQJBAK0IR8bB +85bKZ+M/O8rbs9kQHjx6GCbbDxH+qbIKkNcvLUvMgwwIFKiwqX+Tedtu2xET0mQM +9tEE5eMBOJ8GrxQ= +-----END PRIVATE KEY----- From 8524f64f637c374e6c806028890b4f5c2fe63e06 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Fri, 13 Nov 2015 14:31:22 +0100 Subject: [PATCH 138/210] Fix adding/editing entries in addresbook It didn't sort correctly and didn't update the label correctly when changed. Fixes #95 --- src/bitmessageqt/__init__.py | 24 +++--------------------- src/bitmessageqt/foldertree.py | 6 ++++++ 2 files changed, 9 insertions(+), 21 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 8cd8cbda..67838836 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2431,17 +2431,8 @@ class MyForm(settingsmixin.SMainWindow): def addEntryToAddressBook(self,address,label): queryreturn = sqlQuery('''select * from addressbook where address=?''', address) if queryreturn == []: - self.ui.tableWidgetAddressBook.setSortingEnabled(False) - self.ui.tableWidgetAddressBook.insertRow(0) - newItem = QtGui.QTableWidgetItem(unicode(label, 'utf-8')) - newItem.setIcon(avatarize(address)) - self.ui.tableWidgetAddressBook.setItem(0, 0, newItem) - newItem = QtGui.QTableWidgetItem(address) - newItem.setFlags( - QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) - self.ui.tableWidgetAddressBook.setItem(0, 1, newItem) - self.ui.tableWidgetAddressBook.setSortingEnabled(True) sqlExecute('''INSERT INTO addressbook VALUES (?,?)''', str(label), address) + self.rerenderAddressBook() self.rerenderInboxFromLabels() self.rerenderSentToLabels() else: @@ -3073,20 +3064,10 @@ class MyForm(settingsmixin.SMainWindow): queryreturn = sqlQuery('''select * from addressbook where address=?''', addressAtCurrentInboxRow) if queryreturn == []: - self.ui.tableWidgetAddressBook.insertRow(0) - newItem = QtGui.QTableWidgetItem( - '--New entry. Change label in Address Book.--') - self.ui.tableWidgetAddressBook.setItem(0, 0, newItem) - newItem.setIcon(avatarize(addressAtCurrentInboxRow)) - newItem = QtGui.QTableWidgetItem(addressAtCurrentInboxRow) - newItem.setFlags( - QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) - self.ui.tableWidgetAddressBook.setItem(0, 1, newItem) sqlExecute('''INSERT INTO addressbook VALUES (?,?)''', '--New entry. Change label in Address Book.--', addressAtCurrentInboxRow) - self.ui.tabWidget.setCurrentIndex(1) - self.ui.tableWidgetAddressBook.setCurrentCell(0, 0) + self.rerenderAddressBook() self.statusBar().showMessage(_translate( "MainWindow", "Entry added to the Address Book. Edit the label to your liking.")) else: @@ -3928,6 +3909,7 @@ class MyForm(settingsmixin.SMainWindow): sqlExecute('''UPDATE addressbook set label=? WHERE address=?''', str(self.ui.tableWidgetAddressBook.item(currentRow, 0).text().toUtf8()), str(addressAtCurrentRow)) + self.ui.tableWidgetAddressBook.item(currentRow, 0).setLabel(str(self.ui.tableWidgetAddressBook.item(currentRow, 0).text().toUtf8())) self.rerenderInboxFromLabels() self.rerenderSentToLabels() diff --git a/src/bitmessageqt/foldertree.py b/src/bitmessageqt/foldertree.py index 59f16667..20291c5d 100644 --- a/src/bitmessageqt/foldertree.py +++ b/src/bitmessageqt/foldertree.py @@ -287,9 +287,15 @@ class Ui_AddressBookWidgetItem(QtGui.QTableWidgetItem, AccountMixin): class Ui_AddressBookWidgetItemLabel(Ui_AddressBookWidgetItem): def __init__ (self, address, label, type): Ui_AddressBookWidgetItem.__init__(self, label, type) + self.address = address + self.label = label self.setIcon(avatarize(address)) self.setToolTip(label + " (" + address + ")") + def setLabel(self, label): + self.label = label + self.setToolTip(self.label + " (" + self.address + ")") + class Ui_AddressBookWidgetItemAddress(Ui_AddressBookWidgetItem): def __init__ (self, address, label, type): From b0b01efd592cd29061f3a9a0852320fe2f367dd7 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Fri, 13 Nov 2015 17:01:09 +0100 Subject: [PATCH 139/210] SSL fixes - uses constants for service flags - requires SSL to use TLSv1 version (protection against POODLE-style attacks) - retry if sock.recv incomplete --- src/class_receiveDataThread.py | 10 +++++----- src/class_sendDataThread.py | 2 +- src/shared.py | 6 +++++- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/class_receiveDataThread.py b/src/class_receiveDataThread.py index 737872d1..059bce87 100644 --- a/src/class_receiveDataThread.py +++ b/src/class_receiveDataThread.py @@ -81,7 +81,7 @@ class receiveDataThread(threading.Thread): shared.numberOfBytesReceivedLastSecond = 0 dataLen = len(self.data) try: - if (self.services & 2 == 2) and self.connectionIsOrWasFullyEstablished: + if (self.services & shared.NODE_SSL == shared.NODE_SSL) and self.connectionIsOrWasFullyEstablished: dataRecv = self.sslSock.recv(1024) else: dataRecv = self.sock.recv(1024) @@ -93,11 +93,11 @@ class receiveDataThread(threading.Thread): print 'Timeout occurred waiting for data from', self.peer, '. Closing receiveData thread. (ID:', str(id(self)) + ')' break except Exception as err: - if (sys.platform == 'win32' and err.errno == 10035) or (sys.platform != 'win32' and err.errno == errno.EWOULDBLOCK): + if (sys.platform == 'win32' and err.errno in ([2, 10035])) or (sys.platform != 'win32' and err.errno == errno.EWOULDBLOCK): select.select([self.sslSock], [], []) continue with shared.printLock: - print 'sock.recv error. Closing receiveData thread (' + str(self.peer) + ', Thread ID:', str(id(self)) + ').', err + print 'sock.recv error. Closing receiveData thread (' + str(self.peer) + ', Thread ID:', str(id(self)) + ').', str(err.errno), "/", err break # print 'Received', repr(self.data) if len(self.data) == dataLen: # If self.sock.recv returned no data: @@ -265,8 +265,8 @@ class receiveDataThread(threading.Thread): self.connectionIsOrWasFullyEstablished = True self.sslSock = self.sock - if (self.services & 2 == 2): - self.sslSock = ssl.wrap_socket(self.sock, keyfile = os.path.join(shared.codePath(), 'sslkeys', 'key.pem'), certfile = os.path.join(shared.codePath(), 'sslkeys', 'cert.pem'), server_side = not self.initiatedConnection, ssl_version=ssl.PROTOCOL_SSLv23, do_handshake_on_connect=False, ciphers='AECDH-AES256-SHA') + if (self.services & shared.NODE_SSL == shared.NODE_SSL): + self.sslSock = ssl.wrap_socket(self.sock, keyfile = os.path.join(shared.codePath(), 'sslkeys', 'key.pem'), certfile = os.path.join(shared.codePath(), 'sslkeys', 'cert.pem'), server_side = not self.initiatedConnection, ssl_version=ssl.PROTOCOL_TLSv1, do_handshake_on_connect=False, ciphers='AECDH-AES256-SHA') while True: try: self.sslSock.do_handshake() diff --git a/src/class_sendDataThread.py b/src/class_sendDataThread.py index fedd4833..4ee88399 100644 --- a/src/class_sendDataThread.py +++ b/src/class_sendDataThread.py @@ -83,7 +83,7 @@ class sendDataThread(threading.Thread): uploadRateLimitBytes = 999999999 # float("inf") doesn't work else: uploadRateLimitBytes = shared.config.getint('bitmessagesettings', 'maxuploadrate') * 1000 - if self.services & 2 == 2 and self.connectionIsOrWasFullyEstablished: + if self.services & shared.NODE_SSL == shared.NODE_SSL and self.connectionIsOrWasFullyEstablished: amountSent = self.sslSock.send(data[:1000]) else: amountSent = self.sock.send(data[:1000]) diff --git a/src/shared.py b/src/shared.py index eed70d10..ecf3f029 100644 --- a/src/shared.py +++ b/src/shared.py @@ -120,6 +120,10 @@ trustedPeer = None #New code should use CreatePacket instead of Header.pack Header = Struct('!L12sL4s') +#Service flags +NODE_NETWORK = 1 +NODE_SSL = 2 + #Create a packet def CreatePacket(command, payload=''): payload_length = len(payload) @@ -144,7 +148,7 @@ def encodeHost(host): def assembleVersionMessage(remoteHost, remotePort, myStreamNumber): payload = '' payload += pack('>L', 3) # protocol version. - payload += pack('>q', 3) # bitflags of the services I offer. + payload += pack('>q', NODE_NETWORK|NODE_SSL) # bitflags of the services I offer. payload += pack('>q', int(time.time())) payload += pack( From c09981bd420d3c0233132967235d5e0eb40acb07 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sat, 14 Nov 2015 01:14:10 +0100 Subject: [PATCH 140/210] Blacklist through context menu Fixes #101 --- src/bitmessageqt/__init__.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 67838836..2299c524 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -142,6 +142,10 @@ class MyForm(settingsmixin.SMainWindow): _translate( "MainWindow", "Add sender to your Address Book"), self.on_action_InboxAddSenderToAddressBook) + self.actionAddSenderToBlackList = self.ui.inboxContextMenuToolbar.addAction( + _translate( + "MainWindow", "Add sender to your Blacklist"), + self.on_action_InboxAddSenderToBlackList) self.actionTrashInboxMessage = self.ui.inboxContextMenuToolbar.addAction( _translate("MainWindow", "Move to Trash"), self.on_action_InboxTrash) @@ -3074,6 +3078,28 @@ class MyForm(settingsmixin.SMainWindow): self.statusBar().showMessage(_translate( "MainWindow", "Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want.")) + def on_action_InboxAddSenderToBlackList(self): + tableWidget = self.getCurrentMessagelist() + if not tableWidget: + return + currentInboxRow = tableWidget.currentRow() + # tableWidget.item(currentRow,1).data(Qt.UserRole).toPyObject() + addressAtCurrentInboxRow = str(tableWidget.item( + currentInboxRow, 1).data(Qt.UserRole).toPyObject()) + # Let's make sure that it isn't already in the address book + queryreturn = sqlQuery('''select * from blacklist where address=?''', + addressAtCurrentInboxRow) + if queryreturn == []: + sqlExecute('''INSERT INTO blacklist VALUES (?,?, ?)''', + '--New entry. Change label in Blacklist.--', + addressAtCurrentInboxRow, True) + self.rerenderBlackWhiteList() + self.statusBar().showMessage(_translate( + "MainWindow", "Entry added to the blacklist. Edit the label to your liking.")) + else: + self.statusBar().showMessage(_translate( + "MainWindow", "Error: You cannot add the same address to your blacklist twice. Try renaming the existing one if you want.")) + # Send item on the Inbox tab to trash def on_action_InboxTrash(self): tableWidget = self.getCurrentMessagelist() @@ -3751,6 +3777,8 @@ class MyForm(settingsmixin.SMainWindow): self.popMenuInbox.addAction(self.actionReply) self.popMenuInbox.addAction(self.actionAddSenderToAddressBook) self.popMenuInbox.addSeparator() + self.popMenuInbox.addAction(self.actionAddSenderToBlackList) + self.popMenuInbox.addSeparator() self.popMenuInbox.addAction(self.actionSaveMessageAs) if currentFolder == "trash": self.popMenuInbox.addAction(self.actionUndeleteTrashedMessage) From 9dc9718f26026cea437a9ec2c3d7955a1c02b5d3 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sat, 14 Nov 2015 12:10:43 +0100 Subject: [PATCH 141/210] Don't commit DLLs --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 8e9f9031..b0bf5ae4 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ src/build src/dist src/.project src/.pydevproject -src/.settings/ \ No newline at end of file +src/.settings/ +*.dll \ No newline at end of file From 075f1792999e0ce96a7dd283d4abb2a8a0ceb939 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sat, 14 Nov 2015 13:11:33 +0100 Subject: [PATCH 142/210] OSX build fixes - include bitmsghash so that it bundles the C library PoW - include sslkeys so that SSL works in the bundle - PyBitmessage version now propagated from the shell build script --- osx.sh | 2 ++ src/build_osx.py | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/osx.sh b/osx.sh index eaa33183..e58a49f4 100755 --- a/osx.sh +++ b/osx.sh @@ -14,6 +14,8 @@ fi echo "Creating OS X packages for Bitmessage." +export PYBITMESSAGEVERSION=$1 + cd src && python2.7 build_osx.py py2app if [[ $? = "0" ]]; then diff --git a/src/build_osx.py b/src/build_osx.py index ce70cd54..a5b004e6 100644 --- a/src/build_osx.py +++ b/src/build_osx.py @@ -1,7 +1,8 @@ +import os from setuptools import setup name = "Bitmessage" -version = "0.4.4" +version = os.getenv("PYBITMESSAGEVERSION", "custom") mainscript = ["bitmessagemain.py"] setup( @@ -11,7 +12,7 @@ setup( setup_requires = ["py2app"], options = dict( py2app = dict( - resources = ["images", "translations"], + resources = ["images", "translations", "bitmsghash", "sslkeys"], includes = ['sip', 'PyQt4._qt'], iconfile = "images/bitmessage.icns" ) From 51232d3dd9b9620d0c53af56c6dcd1ef7801971e Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sat, 14 Nov 2015 18:53:11 +0100 Subject: [PATCH 143/210] Improve blacklist autolabel --- src/bitmessageqt/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 2299c524..06b56605 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -3090,8 +3090,9 @@ class MyForm(settingsmixin.SMainWindow): queryreturn = sqlQuery('''select * from blacklist where address=?''', addressAtCurrentInboxRow) if queryreturn == []: + label = "\"" + str(tableWidget.item(currentInboxRow, 2).data(Qt.UserRole).toPyObject()) + "\" in " + shared.config.get(self.getCurrentAccount(), "label") sqlExecute('''INSERT INTO blacklist VALUES (?,?, ?)''', - '--New entry. Change label in Blacklist.--', + label, addressAtCurrentInboxRow, True) self.rerenderBlackWhiteList() self.statusBar().showMessage(_translate( From da18e4be26f1d339356a34b4226e235a38dc57c2 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sat, 14 Nov 2015 21:07:46 +0100 Subject: [PATCH 144/210] Move saving UI state quit is more appropriate than close event, but it my resurrect the issue with linux saving config data. --- src/bitmessageqt/__init__.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 06b56605..9ac08d46 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2862,6 +2862,14 @@ class MyForm(settingsmixin.SMainWindow): if reply is QtGui.QMessageBox.No: return ''' + # save state and geometry self and all widgets + self.saveSettings() + for attr, obj in self.ui.__dict__.iteritems(): + if hasattr(obj, "__class__") and isinstance(obj, settingsmixin.SettingsMixin): + saveMethod = getattr(obj, "saveSettings", None) + if callable (saveMethod): + obj.saveSettings() + shared.doCleanShutdown() self.tray.hide() # unregister the messaging system @@ -2891,13 +2899,6 @@ class MyForm(settingsmixin.SMainWindow): # minimize the application event.ignore() else: - # save state and geometry self and all widgets - self.saveSettings() - for attr, obj in self.ui.__dict__.iteritems(): - if hasattr(obj, "__class__") and isinstance(obj, settingsmixin.SettingsMixin): - saveMethod = getattr(obj, "saveSettings", None) - if callable (saveMethod): - obj.saveSettings() # quit the application event.accept() self.quit() From dd6de5210f4dd7c96d3ac60ee891fd6af3401fd1 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sun, 15 Nov 2015 02:20:08 +0100 Subject: [PATCH 145/210] SSL workaround Python < 2.7.9 does not support anonymous SSL server through ssl.wrap_socket, so we have to disable it. Works fine as client. Try to prefer secp256k1 curve (again, requires python >= 2.7.9) --- src/class_receiveDataThread.py | 4 +++- src/shared.py | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/class_receiveDataThread.py b/src/class_receiveDataThread.py index 059bce87..582ac5b4 100644 --- a/src/class_receiveDataThread.py +++ b/src/class_receiveDataThread.py @@ -265,8 +265,10 @@ class receiveDataThread(threading.Thread): self.connectionIsOrWasFullyEstablished = True self.sslSock = self.sock - if (self.services & shared.NODE_SSL == shared.NODE_SSL): + if (self.services & shared.NODE_SSL == shared.NODE_SSL and (self.initiatedConnection or sys.version_info >= (2, 7, 9))): self.sslSock = ssl.wrap_socket(self.sock, keyfile = os.path.join(shared.codePath(), 'sslkeys', 'key.pem'), certfile = os.path.join(shared.codePath(), 'sslkeys', 'cert.pem'), server_side = not self.initiatedConnection, ssl_version=ssl.PROTOCOL_TLSv1, do_handshake_on_connect=False, ciphers='AECDH-AES256-SHA') + if hasattr(self.sslSock, "context"): + self.sslSock.context.set_ecdh_curve("secp256k1") while True: try: self.sslSock.do_handshake() diff --git a/src/shared.py b/src/shared.py index ecf3f029..a8fc5675 100644 --- a/src/shared.py +++ b/src/shared.py @@ -148,7 +148,8 @@ def encodeHost(host): def assembleVersionMessage(remoteHost, remotePort, myStreamNumber): payload = '' payload += pack('>L', 3) # protocol version. - payload += pack('>q', NODE_NETWORK|NODE_SSL) # bitflags of the services I offer. + payload += pack('>q', NODE_NETWORK|(NODE_SSL if sys.version_info >= (2, 7, 9) else 0)) # bitflags of the services I offer. + # python < 2.7.9's ssl library does not support ECDSA server due to missing initialisation of available curves, but client works ok payload += pack('>q', int(time.time())) payload += pack( From c37fbd40eaf3fef9559e7f36f5d1c23ab85f8519 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sun, 15 Nov 2015 02:22:57 +0100 Subject: [PATCH 146/210] Version bump We have 0.5.3 now --- src/shared.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared.py b/src/shared.py index a8fc5675..d19b0853 100644 --- a/src/shared.py +++ b/src/shared.py @@ -1,6 +1,6 @@ from __future__ import division -softwareVersion = '0.5.2' +softwareVersion = '0.5.3' verbose = 1 maximumAgeOfAnObjectThatIAmWillingToAccept = 216000 # This is obsolete with the change to protocol v3 but the singleCleaner thread still hasn't been updated so we need this a little longer. lengthOfTimeToHoldOnToAllPubkeys = 2419200 # Equals 4 weeks. You could make this longer if you want but making it shorter would not be advisable because there is a very small possibility that it could keep you from obtaining a needed pubkey for a period of time. From e4edeaceb5496c1f947fc8be1195a95dc169bed6 Mon Sep 17 00:00:00 2001 From: "Denilson M. Amorim" Date: Sat, 14 Nov 2015 20:12:19 -0300 Subject: [PATCH 147/210] Squash: Single instance and pop up old instance --- src/bitmessagemain.py | 2 +- src/bitmessageqt/__init__.py | 66 +++++++++++++++++++++++++++++++++++- src/singleton.py | 16 +++++---- 3 files changed, 76 insertions(+), 8 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index bf1d74f2..78e1e903 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -143,7 +143,7 @@ class Main: shared.daemon = daemon # is the application already running? If yes then exit. - thisapp = singleton.singleinstance() + thisapp = singleton.singleinstance("", daemon) # get curses flag curses = False diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 1819628b..ba2015f4 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -41,6 +41,7 @@ try: from PyQt4 import QtCore, QtGui from PyQt4.QtCore import * from PyQt4.QtGui import * + from PyQt4.QtNetwork import QLocalSocket, QLocalServer except Exception as err: print 'PyBitmessage requires PyQt unless you want to run it as a daemon and interact with it using the API. You can download it from http://www.riverbankcomputing.com/software/pyqt/download or by searching Google for \'PyQt Download\' (without quotes).' @@ -52,6 +53,7 @@ try: except AttributeError: print 'QtGui.QApplication.UnicodeUTF8 error:', err + def _translate(context, text): return QtGui.QApplication.translate(context, text) @@ -3901,8 +3903,70 @@ class UISignaler(QThread): sys.stderr.write( 'Command sent to UISignaler not recognized: %s\n' % command) + +app = None +myapp = None + +class MySingleApplication(QApplication): + """ + Listener to allow our Qt form to get focus when another instance of the + application is open. + + Based off this nice reimplmentation of MySingleApplication: + http://stackoverflow.com/a/12712362/2679626 + """ + + # Unique identifier for this application + uuid = '6ec0149b-96e1-4be1-93ab-1465fb3ebf7c' + + def __init__(self, *argv): + super(MySingleApplication, self).__init__(*argv) + id = MySingleApplication.uuid + + self.server = None + self.is_running = False + + socket = QLocalSocket() + socket.connectToServer(id) + self.is_running = socket.waitForConnected() + + # Cleanup past crashed servers + if not self.is_running: + if socket.error() == QLocalSocket.ConnectionRefusedError: + socket.disconnectFromServer() + QLocalServer.removeServer(id) + + socket.abort() + + # Checks if there's an instance of the local server id running + if self.is_running: + # This should be ignored, singleton.py will take care of exiting me. + pass + else: + # Nope, create a local server with this id and assign on_new_connection + # for whenever a second instance tries to run focus the application. + self.server = QLocalServer() + self.server.listen(id) + self.server.newConnection.connect(self.on_new_connection) + + def __del__(self): + if self.server: + self.server.close() + + def on_new_connection(self): + global myapp + if myapp: + myapp.appIndicatorShow() + +def init(): + global app + if not app: + app = MySingleApplication(sys.argv) + return app + def run(): - app = QtGui.QApplication(sys.argv) + global myapp + app = init() change_translation(l10n.getTranslationLanguage()) app.setStyleSheet("QStatusBar::item { border: 0px solid black }") myapp = MyForm() diff --git a/src/singleton.py b/src/singleton.py index ee5c3077..f3124424 100644 --- a/src/singleton.py +++ b/src/singleton.py @@ -3,22 +3,26 @@ import sys import os import errno -import tempfile +import shared from multiprocessing import Process - class singleinstance: """ - Implements a single instance application by creating a lock file based on the full path to the script file. + Implements a single instance application by creating a lock file at appdata. This is based upon the singleton class from tendo https://github.com/pycontribs/tendo which is under the Python Software Foundation License version 2 """ - def __init__(self, flavor_id=""): + def __init__(self, flavor_id="", daemon=False): import sys self.initialized = False - basename = os.path.splitext(os.path.abspath(sys.argv[0]))[0].replace("/", "-").replace(":", "").replace("\\", "-") + '-%s' % flavor_id + '.lock' - self.lockfile = os.path.normpath(tempfile.gettempdir() + '/' + basename) + self.daemon = daemon; + self.lockfile = os.path.normpath(os.path.join(shared.appdata, 'singleton%s.lock' % flavor_id)) + + if not self.daemon: + # Tells the already running (if any) application to get focus. + import bitmessageqt + bitmessageqt.init() if sys.platform == 'win32': try: From 2add3f6bc865bddaa2f75dfb99819337c6b73b7d Mon Sep 17 00:00:00 2001 From: "Denilson M. Amorim" Date: Sun, 15 Nov 2015 11:08:48 -0300 Subject: [PATCH 148/210] Dont run twice if daemon too --- src/bitmessagemain.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 78e1e903..4d68eff1 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -44,7 +44,9 @@ from debug import logger # Helper Functions import helper_bootstrap import helper_generic - + +# singleton lock instance +thisapp = None def connectToStream(streamNumber): shared.streamsInWhichIAmParticipating[streamNumber] = 'no data' @@ -139,6 +141,8 @@ if shared.useVeryEasyProofOfWorkForTesting: class Main: def start(self, daemon=False): + global thisapp + _fixWinsock() shared.daemon = daemon From cee1a59a8659da093bf281e365a3886a52003113 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Wed, 18 Nov 2015 16:22:17 +0100 Subject: [PATCH 149/210] Improved logging Fixes #118 - changed almost all "print" into logger - threads have nicer names - logger can have configuration in "logger.dat" in the same directory as "keys.dat", and the logger will pick the one named "default" to replace the "console" and "file" that are in PyBitmessage otherwise Example file for logging to syslog: [loggers] keys = root,syslog [logger_root] level=NOTSET handlers=syslog [logger_syslog] level=DEBUG handlers=syslog qualname=default [handlers] keys = syslog [handler_syslog] class = handlers.SysLogHandler formatter = syslog level = DEBUG args=(('localhost', handlers.SYSLOG_UDP_PORT), handlers.SysLogHandler.LOG_LOCAL7) [formatters] keys = syslog [formatter_syslog] format=%(asctime)s %(threadName)s %(filename)s@%(lineno)d %(message)s datefmt=%b %d %H:%M:%S --- src/addresses.py | 12 +-- src/bitmessageqt/__init__.py | 45 +++++------ src/class_objectProcessor.py | 5 +- src/class_outgoingSynSender.py | 41 ++++------ src/class_receiveDataThread.py | 140 +++++++++++++-------------------- src/class_sendDataThread.py | 46 ++++------- src/class_singleCleaner.py | 6 +- src/class_singleListener.py | 14 ++-- src/class_singleWorker.py | 122 +++++++++++----------------- src/class_sqlThread.py | 2 +- src/debug.py | 40 ++++++++-- src/helper_bootstrap.py | 14 ++-- 12 files changed, 208 insertions(+), 279 deletions(-) diff --git a/src/addresses.py b/src/addresses.py index f6fbbf73..017eaa82 100644 --- a/src/addresses.py +++ b/src/addresses.py @@ -55,7 +55,7 @@ def decodeBase58(string, alphabet=ALPHABET): def encodeVarint(integer): if integer < 0: - print 'varint cannot be < 0' + logger.error('varint cannot be < 0') raise SystemExit if integer < 253: return pack('>B',integer) @@ -66,7 +66,7 @@ def encodeVarint(integer): if integer >= 4294967296 and integer < 18446744073709551616: return pack('>B',255) + pack('>Q',integer) if integer >= 18446744073709551616: - print 'varint cannot be >= 18446744073709551616' + logger.error('varint cannot be >= 18446744073709551616') raise SystemExit class varintDecodeError(Exception): @@ -185,25 +185,25 @@ def decodeAddress(address): try: addressVersionNumber, bytesUsedByVersionNumber = decodeVarint(data[:9]) except varintDecodeError as e: - print e + logger.error(str(e)) status = 'varintmalformed' return status,0,0,"" #print 'addressVersionNumber', addressVersionNumber #print 'bytesUsedByVersionNumber', bytesUsedByVersionNumber if addressVersionNumber > 4: - print 'cannot decode address version numbers this high' + logger.error('cannot decode address version numbers this high') status = 'versiontoohigh' return status,0,0,"" elif addressVersionNumber == 0: - print 'cannot decode address version numbers of zero.' + logger.error('cannot decode address version numbers of zero.') status = 'versiontoohigh' return status,0,0,"" try: streamNumber, bytesUsedByStreamNumber = decodeVarint(data[bytesUsedByVersionNumber:]) except varintDecodeError as e: - print e + logger.error(str(e)) status = 'varintmalformed' return status,0,0,"" #print streamNumber diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 64a8d0bf..1e0ae17b 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -13,14 +13,14 @@ try: from PyQt4.QtNetwork import QLocalSocket, QLocalServer except Exception as err: - print 'PyBitmessage requires PyQt unless you want to run it as a daemon and interact with it using the API. You can download it from http://www.riverbankcomputing.com/software/pyqt/download or by searching Google for \'PyQt Download\' (without quotes).' - print 'Error message:', err + logger.error( 'PyBitmessage requires PyQt unless you want to run it as a daemon and interact with it using the API. You can download it from http://www.riverbankcomputing.com/software/pyqt/download or by searching Google for \'PyQt Download\' (without quotes).') + logger.error('Error message: ' + str(err)) sys.exit() try: _encoding = QtGui.QApplication.UnicodeUTF8 except AttributeError: - print 'QtGui.QApplication.UnicodeUTF8 error:', err + logger.error('QtGui.QApplication.UnicodeUTF8 error: ' + str(err)) from addresses import * import shared @@ -491,7 +491,6 @@ class MyForm(settingsmixin.SMainWindow): if len(db[toAddress]) > 0: j = 0 for f, c in db[toAddress].iteritems(): - print "adding %s, %i" % (f, c) subwidget = Ui_FolderWidget(widget, j, toAddress, f, c) j += 1 widget.setUnreadCount(unread) @@ -730,7 +729,7 @@ class MyForm(settingsmixin.SMainWindow): if nc.test()[0] == 'failed': self.ui.pushButtonFetchNamecoinID.hide() except: - print 'There was a problem testing for a Namecoin daemon. Hiding the Fetch Namecoin ID button' + logger.error('There was a problem testing for a Namecoin daemon. Hiding the Fetch Namecoin ID button') self.ui.pushButtonFetchNamecoinID.hide() def updateTTL(self, sliderPosition): @@ -1270,7 +1269,7 @@ class MyForm(settingsmixin.SMainWindow): # has messageing menu been installed if not withMessagingMenu: - print 'WARNING: MessagingMenu is not available. Is libmessaging-menu-dev installed?' + logger.warning('WARNING: MessagingMenu is not available. Is libmessaging-menu-dev installed?') return # create the menu server @@ -1283,7 +1282,7 @@ class MyForm(settingsmixin.SMainWindow): self.ubuntuMessagingMenuUnread(True) except Exception: withMessagingMenu = False - print 'WARNING: messaging menu disabled' + logger.warning('WARNING: messaging menu disabled') # update the Ubuntu messaging menu def ubuntuMessagingMenuUpdate(self, drawAttention, newItem, toLabel): @@ -1295,7 +1294,7 @@ class MyForm(settingsmixin.SMainWindow): # has messageing menu been installed if not withMessagingMenu: - print 'WARNING: messaging menu disabled or libmessaging-menu-dev not installed' + logger.warning('WARNING: messaging menu disabled or libmessaging-menu-dev not installed') return # remember this item to that the messaging menu can find it @@ -1389,7 +1388,7 @@ class MyForm(settingsmixin.SMainWindow): stdout=subprocess.PIPE) gst_available=True except: - print "WARNING: gst123 must be installed in order to play mp3 sounds" + logger.warning("WARNING: gst123 must be installed in order to play mp3 sounds") if not gst_available: try: subprocess.call(["mpg123", soundFilename], @@ -1397,14 +1396,14 @@ class MyForm(settingsmixin.SMainWindow): stdout=subprocess.PIPE) gst_available=True except: - print "WARNING: mpg123 must be installed in order to play mp3 sounds" + logger.warning("WARNING: mpg123 must be installed in order to play mp3 sounds") else: try: subprocess.call(["aplay", soundFilename], stdin=subprocess.PIPE, stdout=subprocess.PIPE) except: - print "WARNING: aplay must be installed in order to play WAV sounds" + logger.warning("WARNING: aplay must be installed in order to play WAV sounds") elif sys.platform[0:3] == 'win': # use winsound on Windows import winsound @@ -1521,7 +1520,7 @@ class MyForm(settingsmixin.SMainWindow): shared.apiAddressGeneratorReturnQueue.queue.clear() shared.addressGeneratorQueue.put(('createChan', 4, 1, self.str_chan + ' ' + str(self.newChanDialogInstance.ui.lineEditChanNameCreate.text().toUtf8()), self.newChanDialogInstance.ui.lineEditChanNameCreate.text().toUtf8())) addressGeneratorReturnValue = shared.apiAddressGeneratorReturnQueue.get() - print 'addressGeneratorReturnValue', addressGeneratorReturnValue + logger.debug('addressGeneratorReturnValue ' + addressGeneratorReturnValue) if len(addressGeneratorReturnValue) == 0: QMessageBox.about(self, _translate("MainWindow", "Address already present"), _translate( "MainWindow", "Could not add chan because it appears to already be one of your identities.")) @@ -1546,7 +1545,7 @@ class MyForm(settingsmixin.SMainWindow): shared.apiAddressGeneratorReturnQueue.queue.clear() shared.addressGeneratorQueue.put(('joinChan', addBMIfNotPresent(self.newChanDialogInstance.ui.lineEditChanBitmessageAddress.text()), self.str_chan + ' ' + str(self.newChanDialogInstance.ui.lineEditChanNameJoin.text().toUtf8()), self.newChanDialogInstance.ui.lineEditChanNameJoin.text().toUtf8())) addressGeneratorReturnValue = shared.apiAddressGeneratorReturnQueue.get() - print 'addressGeneratorReturnValue', addressGeneratorReturnValue + logger.debug('addressGeneratorReturnValue ' + addressGeneratorReturnValue) if addressGeneratorReturnValue == 'chan name does not match address': QMessageBox.about(self, _translate("MainWindow", "Address does not match chan name"), _translate( "MainWindow", "Although the Bitmessage address you entered was valid, it doesn\'t match the chan name.")) @@ -2113,13 +2112,12 @@ class MyForm(settingsmixin.SMainWindow): acct.createMessage(toAddress, fromAddress, subject, message) subject = acct.subject toAddress = acct.toAddress - print "Subject: %s" % (subject) - print "address: %s" % (toAddress) + logger.debug("Subject: %s" % (subject)) + logger.debug("address: %s" % (toAddress)) status, addressVersionNumber, streamNumber, ripe = decodeAddress( toAddress) if status != 'success': - with shared.printLock: - print 'Error: Could not decode', toAddress, ':', status + logger.error('Error: Could not decode ' + toAddress + ':' + status) if status == 'missingbm': self.statusBar().showMessage(_translate( @@ -2487,7 +2485,7 @@ class MyForm(settingsmixin.SMainWindow): shared.objectProcessorQueue.put((objectType,payload)) def click_pushButtonStatusIcon(self): - print 'click_pushButtonStatusIcon' + logger.debug('click_pushButtonStatusIcon') self.iconGlossaryInstance = iconGlossaryDialog(self) if self.iconGlossaryInstance.exec_(): pass @@ -2798,12 +2796,10 @@ class MyForm(settingsmixin.SMainWindow): if acct.type != 'normal': return if self.dialog.ui.radioButtonUnregister.isChecked() and isinstance(acct, GatewayAccount): - print "unregister" acct.unregister() shared.config.remove_option(addressAtCurrentRow, 'gateway') shared.writeKeysFile() elif self.dialog.ui.radioButtonRegister.isChecked(): - print "register" email = str(self.dialog.ui.lineEditEmail.text().toUtf8()) acct = MailchuckAccount(addressAtCurrentRow) acct.register(email) @@ -2852,7 +2848,7 @@ class MyForm(settingsmixin.SMainWindow): shared.addressGeneratorQueue.put(('createDeterministicAddresses', 4, streamNumberForAddress, "unused deterministic address", self.dialog.ui.spinBoxNumberOfAddressesToMake.value( ), self.dialog.ui.lineEditPassphrase.text().toUtf8(), self.dialog.ui.checkBoxEighteenByteRipe.isChecked())) else: - print 'new address dialog box rejected' + logger.debug('new address dialog box rejected') # Quit selected from menu or application indicator def quit(self): @@ -3040,7 +3036,7 @@ class MyForm(settingsmixin.SMainWindow): # If the previous message was to a chan then we should send our reply to the chan rather than to the particular person who sent the message. if shared.config.has_section(toAddressAtCurrentInboxRow): if shared.safeConfigGetBoolean(toAddressAtCurrentInboxRow, 'chan'): - print 'original sent to a chan. Setting the to address in the reply to the chan address.' + logger.debug('original sent to a chan. Setting the to address in the reply to the chan address.') self.ui.lineEditTo.setText(str(toAddressAtCurrentInboxRow)) listOfAddressesInComboBoxSendFrom = [str(widget['from'].itemData(i).toPyObject()) for i in range(widget['from'].count())] @@ -3715,7 +3711,7 @@ class MyForm(settingsmixin.SMainWindow): if sourcefile != '': copied = QtCore.QFile.copy(sourcefile, destination) if not copied: - print 'couldn\'t copy :(' + logger.error('couldn\'t copy :(') # set the icon self.rerenderTabTreeMessages() self.rerenderTabTreeSubscriptions() @@ -3953,8 +3949,7 @@ class MyForm(settingsmixin.SMainWindow): def updateStatusBar(self, data): if data != "": - with shared.printLock: - print 'Status bar:', data + logger.info('Status bar: ' + data) self.statusBar().showMessage(data) diff --git a/src/class_objectProcessor.py b/src/class_objectProcessor.py index f9972fe6..f7b0e893 100644 --- a/src/class_objectProcessor.py +++ b/src/class_objectProcessor.py @@ -29,7 +29,7 @@ class objectProcessor(threading.Thread): objecs (msg, broadcast, pubkey, getpubkey) from the receiveDataThreads. """ def __init__(self): - threading.Thread.__init__(self) + threading.Thread.__init__(self, name="objectProcessor") """ It may be the case that the last time Bitmessage was running, the user closed it before it finished processing everything in the @@ -741,8 +741,7 @@ class objectProcessor(threading.Thread): fromAddress = encodeAddress( sendersAddressVersion, sendersStream, calculatedRipe) - with shared.printLock: - print 'fromAddress:', fromAddress + logger.debug('fromAddress: ' + fromAddress) if messageEncodingType == 2: subject, body = self.decodeType2Message(message) diff --git a/src/class_outgoingSynSender.py b/src/class_outgoingSynSender.py index 453acfe1..732f6c9f 100644 --- a/src/class_outgoingSynSender.py +++ b/src/class_outgoingSynSender.py @@ -16,7 +16,7 @@ from class_receiveDataThread import * class outgoingSynSender(threading.Thread): def __init__(self): - threading.Thread.__init__(self) + threading.Thread.__init__(self, name="outgoingSynSender") def setup(self, streamNumber, selfInitiatedConnections): self.streamNumber = streamNumber @@ -40,6 +40,7 @@ class outgoingSynSender(threading.Thread): while shared.safeConfigGetBoolean('bitmessagesettings', 'dontconnect'): time.sleep(2) while shared.safeConfigGetBoolean('bitmessagesettings', 'sendoutgoingconnections'): + self.name = "outgoingSynSender" maximumConnections = 1 if shared.trustedPeer else 8 # maximum number of outgoing connections = 8 while len(self.selfInitiatedConnections[self.streamNumber]) >= maximumConnections: time.sleep(10) @@ -64,6 +65,7 @@ class outgoingSynSender(threading.Thread): shared.alreadyAttemptedConnectionsListLock.acquire() shared.alreadyAttemptedConnectionsList[peer] = 0 shared.alreadyAttemptedConnectionsListLock.release() + self.name = "outgoingSynSender-" + peer.host if peer.host.find(':') == -1: address_family = socket.AF_INET else: @@ -86,22 +88,19 @@ class outgoingSynSender(threading.Thread): except: pass shared.knownNodesLock.release() - with shared.printLock: - print 'deleting ', peer, 'from shared.knownNodes because it caused a socks.socksocket exception. We must not be 64-bit compatible.' + logger.debug('deleting ' + str(peer) + ' from shared.knownNodes because it caused a socks.socksocket exception. We must not be 64-bit compatible.') continue # This option apparently avoids the TIME_WAIT state so that we # can rebind faster sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(20) if shared.config.get('bitmessagesettings', 'socksproxytype') == 'none' and shared.verbose >= 2: - with shared.printLock: - print 'Trying an outgoing connection to', peer + logger.debug('Trying an outgoing connection to ' + str(peer)) # sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) elif shared.config.get('bitmessagesettings', 'socksproxytype') == 'SOCKS4a': if shared.verbose >= 2: - with shared.printLock: - print '(Using SOCKS4a) Trying an outgoing connection to', peer + logger.debug ('(Using SOCKS4a) Trying an outgoing connection to ' + str(peer)) proxytype = socks.PROXY_TYPE_SOCKS4 sockshostname = shared.config.get( @@ -121,8 +120,7 @@ class outgoingSynSender(threading.Thread): proxytype, sockshostname, socksport, rdns) elif shared.config.get('bitmessagesettings', 'socksproxytype') == 'SOCKS5': if shared.verbose >= 2: - with shared.printLock: - print '(Using SOCKS5) Trying an outgoing connection to', peer + logger.debug ('(Using SOCKS5) Trying an outgoing connection to ' + str(peer)) proxytype = socks.PROXY_TYPE_SOCKS5 sockshostname = shared.config.get( @@ -155,8 +153,7 @@ class outgoingSynSender(threading.Thread): self.selfInitiatedConnections, sendDataThreadQueue) rd.start() - with shared.printLock: - print self, 'connected to', peer, 'during an outgoing attempt.' + logger.debug(str(self) + ' connected to ' + str(peer) + ' during an outgoing attempt.') sd = sendDataThread(sendDataThreadQueue) @@ -167,8 +164,7 @@ class outgoingSynSender(threading.Thread): except socks.GeneralProxyError as err: if shared.verbose >= 2: - with shared.printLock: - print 'Could NOT connect to', peer, 'during outgoing attempt.', err + logger.debug('Could NOT connect to ' + str(peer) + ' during outgoing attempt. ' + str(err)) deletedPeer = None with shared.knownNodesLock: @@ -186,8 +182,7 @@ class outgoingSynSender(threading.Thread): del shared.knownNodes[self.streamNumber][peer] deletedPeer = peer if deletedPeer: - with shared.printLock: - print 'deleting', peer, 'from shared.knownNodes because it is more than 48 hours old and we could not connect to it.' + str ('deleting ' + str(peer) + ' from shared.knownNodes because it is more than 48 hours old and we could not connect to it.') except socks.Socks5AuthError as err: shared.UISignalQueue.put(( @@ -195,16 +190,15 @@ class outgoingSynSender(threading.Thread): "MainWindow", "SOCKS5 Authentication problem: %1").arg(str(err)))) except socks.Socks5Error as err: pass - print 'SOCKS5 error. (It is possible that the server wants authentication).)', str(err) + logger.error('SOCKS5 error. (It is possible that the server wants authentication).) ' + str(err)) except socks.Socks4Error as err: - print 'Socks4Error:', err + logger.error('Socks4Error: ' + str(err)) except socket.error as err: if shared.config.get('bitmessagesettings', 'socksproxytype')[0:5] == 'SOCKS': - print 'Bitmessage MIGHT be having trouble connecting to the SOCKS server. ' + str(err) + logger.error('Bitmessage MIGHT be having trouble connecting to the SOCKS server. ' + str(err)) else: if shared.verbose >= 1: - with shared.printLock: - print 'Could NOT connect to', peer, 'during outgoing attempt.', err + logger.error('Could NOT connect to ' + str(peer) + 'during outgoing attempt. ' + str(err)) deletedPeer = None with shared.knownNodesLock: @@ -222,12 +216,9 @@ class outgoingSynSender(threading.Thread): del shared.knownNodes[self.streamNumber][peer] deletedPeer = peer if deletedPeer: - with shared.printLock: - print 'deleting', peer, 'from shared.knownNodes because it is more than 48 hours old and we could not connect to it.' + logger.debug('deleting ' + str(peer) + ' from shared.knownNodes because it is more than 48 hours old and we could not connect to it.') except Exception as err: - sys.stderr.write( - 'An exception has occurred in the outgoingSynSender thread that was not caught by other exception types: ') import traceback - traceback.print_exc() + logger.exception('An exception has occurred in the outgoingSynSender thread that was not caught by other exception types:') time.sleep(0.1) diff --git a/src/class_receiveDataThread.py b/src/class_receiveDataThread.py index 582ac5b4..fa165401 100644 --- a/src/class_receiveDataThread.py +++ b/src/class_receiveDataThread.py @@ -29,7 +29,7 @@ from debug import logger class receiveDataThread(threading.Thread): def __init__(self): - threading.Thread.__init__(self) + threading.Thread.__init__(self, name="receiveData") self.data = '' self.verackSent = False self.verackReceived = False @@ -46,6 +46,7 @@ class receiveDataThread(threading.Thread): self.sock = sock self.peer = shared.Peer(HOST, port) + self.name = "receiveData-" + self.peer.host self.streamNumber = streamNumber self.objectsThatWeHaveYetToGetFromThisPeer = {} self.selfInitiatedConnections = selfInitiatedConnections @@ -62,8 +63,7 @@ class receiveDataThread(threading.Thread): self.someObjectsOfWhichThisRemoteNodeIsAlreadyAware = someObjectsOfWhichThisRemoteNodeIsAlreadyAware def run(self): - with shared.printLock: - print 'receiveDataThread starting. ID', str(id(self)) + '. The size of the shared.connectedHostsList is now', len(shared.connectedHostsList) + logger.debug('receiveDataThread starting. ID ' + str(id(self)) + '. The size of the shared.connectedHostsList is now ' + str(len(shared.connectedHostsList))) while True: if shared.config.getint('bitmessagesettings', 'maxdownloadrate') == 0: @@ -89,36 +89,31 @@ class receiveDataThread(threading.Thread): shared.numberOfBytesReceived += len(dataRecv) # for the 'network status' UI tab. The UI clears this value whenever it updates. shared.numberOfBytesReceivedLastSecond += len(dataRecv) # for the download rate limit except socket.timeout: - with shared.printLock: - print 'Timeout occurred waiting for data from', self.peer, '. Closing receiveData thread. (ID:', str(id(self)) + ')' + logger.error ('Timeout occurred waiting for data from ' + str(self.peer) + '. Closing receiveData thread. (ID: ' + str(id(self)) + ')') break except Exception as err: if (sys.platform == 'win32' and err.errno in ([2, 10035])) or (sys.platform != 'win32' and err.errno == errno.EWOULDBLOCK): select.select([self.sslSock], [], []) continue - with shared.printLock: - print 'sock.recv error. Closing receiveData thread (' + str(self.peer) + ', Thread ID:', str(id(self)) + ').', str(err.errno), "/", err + logger.error('sock.recv error. Closing receiveData thread (' + str(self.peer) + ', Thread ID: ' + str(id(self)) + ').' + str(err.errno) + "/" + str(err)) break # print 'Received', repr(self.data) if len(self.data) == dataLen: # If self.sock.recv returned no data: - with shared.printLock: - print 'Connection to', self.peer, 'closed. Closing receiveData thread. (ID:', str(id(self)) + ')' + logger.debug('Connection to ' + str(self.peer) + ' closed. Closing receiveData thread. (ID: ' + str(id(self)) + ')') break else: self.processData() try: del self.selfInitiatedConnections[self.streamNumber][self] - with shared.printLock: - print 'removed self (a receiveDataThread) from selfInitiatedConnections' + logger.debug('removed self (a receiveDataThread) from selfInitiatedConnections') except: pass self.sendDataThreadQueue.put((0, 'shutdown','no data')) # commands the corresponding sendDataThread to shut itself down. try: del shared.connectedHostsList[self.peer.host] except Exception as err: - with shared.printLock: - print 'Could not delete', self.peer.host, 'from shared.connectedHostsList.', err + logger.error('Could not delete ' + str(self.peer.host) + ' from shared.connectedHostsList.' + str(err)) try: del shared.numberOfObjectsThatWeHaveYetToGetPerPeer[ @@ -126,8 +121,7 @@ class receiveDataThread(threading.Thread): except: pass shared.UISignalQueue.put(('updateNetworkStatusTab', 'no data')) - with shared.printLock: - print 'receiveDataThread ending. ID', str(id(self)) + '. The size of the shared.connectedHostsList is now', len(shared.connectedHostsList) + logger.debug('receiveDataThread ending. ID ' + str(id(self)) + '. The size of the shared.connectedHostsList is now ' + str(len(shared.connectedHostsList))) def processData(self): @@ -148,7 +142,7 @@ class receiveDataThread(threading.Thread): return payload = self.data[shared.Header.size:payloadLength + shared.Header.size] if checksum != hashlib.sha512(payload).digest()[0:4]: # test the checksum in the message. - print 'Checksum incorrect. Clearing this message.' + logger.error('Checksum incorrect. Clearing this message.') self.data = self.data[payloadLength + shared.Header.size:] del magic,command,payloadLength,checksum,payload # better to clean up before the recursive call self.processData() @@ -163,8 +157,7 @@ class receiveDataThread(threading.Thread): #Strip the nulls command = command.rstrip('\x00') - with shared.printLock: - print 'remoteCommand', repr(command), ' from', self.peer + logger.debug('remoteCommand ' + repr(command) + ' from ' + str(self.peer)) try: #TODO: Use a dispatcher here @@ -202,14 +195,12 @@ class receiveDataThread(threading.Thread): objectHash, = random.sample( self.objectsThatWeHaveYetToGetFromThisPeer, 1) if objectHash in shared.inventory: - with shared.printLock: - print 'Inventory (in memory) already has object listed in inv message.' + logger.debug('Inventory (in memory) already has object listed in inv message.') del self.objectsThatWeHaveYetToGetFromThisPeer[ objectHash] elif shared.isInSqlInventory(objectHash): if shared.verbose >= 3: - with shared.printLock: - print 'Inventory (SQL on disk) already has object listed in inv message.' + logger.debug('Inventory (SQL on disk) already has object listed in inv message.') del self.objectsThatWeHaveYetToGetFromThisPeer[ objectHash] else: @@ -218,8 +209,7 @@ class receiveDataThread(threading.Thread): del self.objectsThatWeHaveYetToGetFromThisPeer[ objectHash] # It is possible that the remote node might not respond with the object. In that case, we'll very likely get it from someone else anyway. if len(self.objectsThatWeHaveYetToGetFromThisPeer) == 0: - with shared.printLock: - print '(concerning', str(self.peer) + ')', 'number of objectsThatWeHaveYetToGetFromThisPeer is now 0' + logger.debug('(concerning' + str(self.peer) + ') number of objectsThatWeHaveYetToGetFromThisPeer is now 0') try: del shared.numberOfObjectsThatWeHaveYetToGetPerPeer[ self.peer] # this data structure is maintained so that we can keep track of how many total objects, across all connections, are currently outstanding. If it goes too high it can indicate that we are under attack by multiple nodes working together. @@ -228,16 +218,14 @@ class receiveDataThread(threading.Thread): break if len(self.objectsThatWeHaveYetToGetFromThisPeer) == 0: # We had objectsThatWeHaveYetToGetFromThisPeer but the loop ran, they were all in our inventory, and now we don't have any to get anymore. - with shared.printLock: - print '(concerning', str(self.peer) + ')', 'number of objectsThatWeHaveYetToGetFromThisPeer is now 0' + logger.debug('(concerning' + str(self.peer) + ') number of objectsThatWeHaveYetToGetFromThisPeer is now 0') try: del shared.numberOfObjectsThatWeHaveYetToGetPerPeer[ self.peer] # this data structure is maintained so that we can keep track of how many total objects, across all connections, are currently outstanding. If it goes too high it can indicate that we are under attack by multiple nodes working together. except: pass if len(self.objectsThatWeHaveYetToGetFromThisPeer) > 0: - with shared.printLock: - print '(concerning', str(self.peer) + ')', 'number of objectsThatWeHaveYetToGetFromThisPeer is now', len(self.objectsThatWeHaveYetToGetFromThisPeer) + logger.debug('(concerning' + str(self.peer) + ') number of objectsThatWeHaveYetToGetFromThisPeer is now ' + str(len(self.objectsThatWeHaveYetToGetFromThisPeer))) shared.numberOfObjectsThatWeHaveYetToGetPerPeer[self.peer] = len( self.objectsThatWeHaveYetToGetFromThisPeer) # this data structure is maintained so that we can keep track of how many total objects, across all connections, are currently outstanding. If it goes too high it can indicate that we are under attack by multiple nodes working together. @@ -245,14 +233,12 @@ class receiveDataThread(threading.Thread): def sendpong(self): - with shared.printLock: - print 'Sending pong' + logger.debug('Sending pong') self.sendDataThreadQueue.put((0, 'sendRawData', shared.CreatePacket('pong'))) def recverack(self): - with shared.printLock: - print 'verack received' + logger.debug('verack received') self.verackReceived = True if self.verackSent: # We have thus both sent and received a verack. @@ -289,11 +275,10 @@ class receiveDataThread(threading.Thread): self.sock.settimeout( 600) # We'll send out a pong every 5 minutes to make sure the connection stays alive if there has been no other traffic to send lately. shared.UISignalQueue.put(('updateNetworkStatusTab', 'no data')) - with shared.printLock: - print 'Connection fully established with', self.peer - print 'The size of the connectedHostsList is now', len(shared.connectedHostsList) - print 'The length of sendDataQueues is now:', len(shared.sendDataQueues) - print 'broadcasting addr from within connectionFullyEstablished function.' + logger.debug('Connection fully established with ' + str(self.peer) + "\n" + \ + 'The size of the connectedHostsList is now ' + str(len(shared.connectedHostsList)) + "\n" + \ + 'The length of sendDataQueues is now: ' + str(len(shared.sendDataQueues)) + "\n" + \ + 'broadcasting addr from within connectionFullyEstablished function.') # Let all of our peers know about this new node. dataToSend = (int(time.time()), self.streamNumber, 1, self.peer.host, self.remoteNodeIncomingPort) @@ -302,8 +287,7 @@ class receiveDataThread(threading.Thread): self.sendaddr() # This is one large addr message to this one peer. if not self.initiatedConnection and len(shared.connectedHostsList) > 200: - with shared.printLock: - print 'We are connected to too many people. Closing connection.' + logger.info ('We are connected to too many people. Closing connection.') self.sendDataThreadQueue.put((0, 'shutdown','no data')) return @@ -349,8 +333,7 @@ class receiveDataThread(threading.Thread): # function for broadcasting invs to everyone in our stream. def sendinvMessageToJustThisOnePeer(self, numberOfObjects, payload): payload = encodeVarint(numberOfObjects) + payload - with shared.printLock: - print 'Sending huge inv message with', numberOfObjects, 'objects to just this one peer' + logger.debug('Sending huge inv message with ' + str(numberOfObjects) + ' objects to just this one peer') self.sendDataThreadQueue.put((0, 'sendRawData', shared.CreatePacket('inv', payload))) def _sleepForTimingAttackMitigation(self, sleepTime): @@ -358,8 +341,7 @@ class receiveDataThread(threading.Thread): # only connected to the trusted peer because we can trust the # peer not to attack if sleepTime > 0 and doTimingAttackMitigation and shared.trustedPeer == None: - with shared.printLock: - print 'Timing attack mitigation: Sleeping for', sleepTime, 'seconds.' + logger.debug('Timing attack mitigation: Sleeping for ' + str(sleepTime) + ' seconds.') time.sleep(sleepTime) def recerror(self, data): @@ -417,30 +399,27 @@ class receiveDataThread(threading.Thread): if len(shared.numberOfObjectsThatWeHaveYetToGetPerPeer) > 0: for key, value in shared.numberOfObjectsThatWeHaveYetToGetPerPeer.items(): totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers += value - with shared.printLock: - print 'number of keys(hosts) in shared.numberOfObjectsThatWeHaveYetToGetPerPeer:', len(shared.numberOfObjectsThatWeHaveYetToGetPerPeer) - print 'totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers = ', totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers + logger.debug('number of keys(hosts) in shared.numberOfObjectsThatWeHaveYetToGetPerPeer: ' + str(len(shared.numberOfObjectsThatWeHaveYetToGetPerPeer)) + "\n" + \ + 'totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers = ' + str(totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers)) numberOfItemsInInv, lengthOfVarint = decodeVarint(data[:10]) if numberOfItemsInInv > 50000: sys.stderr.write('Too many items in inv message!') return if len(data) < lengthOfVarint + (numberOfItemsInInv * 32): - print 'inv message doesn\'t contain enough data. Ignoring.' + logger.info('inv message doesn\'t contain enough data. Ignoring.') return if numberOfItemsInInv == 1: # we'll just request this data from the person who advertised the object. if totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers > 200000 and len(self.objectsThatWeHaveYetToGetFromThisPeer) > 1000: # inv flooding attack mitigation - with shared.printLock: - print 'We already have', totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers, 'items yet to retrieve from peers and over 1000 from this node in particular. Ignoring this inv message.' + logger.debug('We already have ' + str(totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers) + ' items yet to retrieve from peers and over 1000 from this node in particular. Ignoring this inv message.') return self.someObjectsOfWhichThisRemoteNodeIsAlreadyAware[ data[lengthOfVarint:32 + lengthOfVarint]] = 0 shared.numberOfInventoryLookupsPerformed += 1 if data[lengthOfVarint:32 + lengthOfVarint] in shared.inventory: - with shared.printLock: - print 'Inventory (in memory) has inventory item already.' + logger.debug('Inventory (in memory) has inventory item already.') elif shared.isInSqlInventory(data[lengthOfVarint:32 + lengthOfVarint]): - print 'Inventory (SQL on disk) has inventory item already.' + logger.debug('Inventory (SQL on disk) has inventory item already.') else: self.sendgetdata(data[lengthOfVarint:32 + lengthOfVarint]) else: @@ -455,8 +434,7 @@ class receiveDataThread(threading.Thread): logger.info('inv message lists %s objects. Of those %s are new to me. It took %s seconds to figure that out.', numberOfItemsInInv, len(objectsNewToMe), time.time()-startTime) for item in objectsNewToMe: if totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers > 200000 and len(self.objectsThatWeHaveYetToGetFromThisPeer) > 1000: # inv flooding attack mitigation - with shared.printLock: - print 'We already have', totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers, 'items yet to retrieve from peers and over', len(self.objectsThatWeHaveYetToGetFromThisPeer), 'from this node in particular. Ignoring the rest of this inv message.' + logger.debug('We already have ' + str(totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers) + ' items yet to retrieve from peers and over ' + str(len(self.objectsThatWeHaveYetToGetFromThisPeer)), ' from this node in particular. Ignoring the rest of this inv message.') break self.someObjectsOfWhichThisRemoteNodeIsAlreadyAware[item] = 0 # helps us keep from sending inv messages to peers that already know about the objects listed therein self.objectsThatWeHaveYetToGetFromThisPeer[item] = 0 # upon finishing dealing with an incoming message, the receiveDataThread will request a random object of from peer out of this data structure. This way if we get multiple inv messages from multiple peers which list mostly the same objects, we will make getdata requests for different random objects from the various peers. @@ -467,8 +445,7 @@ class receiveDataThread(threading.Thread): # Send a getdata message to our peer to request the object with the given # hash def sendgetdata(self, hash): - with shared.printLock: - print 'sending getdata to retrieve object with hash:', hash.encode('hex') + logger.debug('sending getdata to retrieve object with hash: ' + hash.encode('hex')) payload = '\x01' + hash self.sendDataThreadQueue.put((0, 'sendRawData', shared.CreatePacket('getdata', payload))) @@ -478,13 +455,12 @@ class receiveDataThread(threading.Thread): numberOfRequestedInventoryItems, lengthOfVarint = decodeVarint( data[:10]) if len(data) < lengthOfVarint + (32 * numberOfRequestedInventoryItems): - print 'getdata message does not contain enough data. Ignoring.' + logger.debug('getdata message does not contain enough data. Ignoring.') return for i in xrange(numberOfRequestedInventoryItems): hash = data[lengthOfVarint + ( i * 32):32 + lengthOfVarint + (i * 32)] - with shared.printLock: - print 'received getdata request for item:', hash.encode('hex') + logger.debug('received getdata request for item:' + hash.encode('hex')) shared.numberOfInventoryLookupsPerformed += 1 shared.inventoryLock.acquire() @@ -507,36 +483,35 @@ class receiveDataThread(threading.Thread): # Our peer has requested (in a getdata message) that we send an object. def sendObject(self, payload): - with shared.printLock: - print 'sending an object.' + logger.debug('sending an object.') self.sendDataThreadQueue.put((0, 'sendRawData', shared.CreatePacket('object',payload))) def _checkIPv4Address(self, host, hostStandardFormat): # print 'hostStandardFormat', hostStandardFormat if host[0] == '\x7F': # 127/8 - print 'Ignoring IP address in loopback range:', hostStandardFormat + logger.debug('Ignoring IP address in loopback range: ' + hostStandardFormat) return False if host[0] == '\x0A': # 10/8 - print 'Ignoring IP address in private range:', hostStandardFormat + logger.debug('Ignoring IP address in private range: ' + hostStandardFormat) return False if host[0:2] == '\xC0\xA8': # 192.168/16 - print 'Ignoring IP address in private range:', hostStandardFormat + logger.debug('Ignoring IP address in private range: ' + hostStandardFormat) return False if host[0:2] >= '\xAC\x10' and host[0:2] < '\xAC\x20': # 172.16/12 - print 'Ignoring IP address in private range:', hostStandardFormat + logger.debug('Ignoring IP address in private range:' + hostStandardFormat) return False return True def _checkIPv6Address(self, host, hostStandardFormat): if host == ('\x00' * 15) + '\x01': - print 'Ignoring loopback address:', hostStandardFormat + logger.debug('Ignoring loopback address: ' + hostStandardFormat) return False if host[0] == '\xFE' and (ord(host[1]) & 0xc0) == 0x80: - print 'Ignoring local address:', hostStandardFormat + logger.debug ('Ignoring local address: ' + hostStandardFormat) return False if (ord(host[0]) & 0xfe) == 0xfc: - print 'Ignoring unique local address:', hostStandardFormat + logger.debug ('Ignoring unique local address: ' + hostStandardFormat) return False return True @@ -546,13 +521,12 @@ class receiveDataThread(threading.Thread): data[:10]) if shared.verbose >= 1: - with shared.printLock: - print 'addr message contains', numberOfAddressesIncluded, 'IP addresses.' + logger.debug('addr message contains ' + str(numberOfAddressesIncluded) + ' IP addresses.') if numberOfAddressesIncluded > 1000 or numberOfAddressesIncluded == 0: return if len(data) != lengthOfNumberOfAddresses + (38 * numberOfAddressesIncluded): - print 'addr message does not contain the correct amount of data. Ignoring.' + logger.debug('addr message does not contain the correct amount of data. Ignoring.') return for i in range(0, numberOfAddressesIncluded): @@ -590,8 +564,7 @@ class receiveDataThread(threading.Thread): if len(shared.knownNodes[recaddrStream]) < 20000 and timeSomeoneElseReceivedMessageFromThisNode > (int(time.time()) - 10800) and timeSomeoneElseReceivedMessageFromThisNode < (int(time.time()) + 10800): # If we have more than 20000 nodes in our list already then just forget about adding more. Also, make sure that the time that someone else received a message from this node is within three hours from now. with shared.knownNodesLock: shared.knownNodes[recaddrStream][peerFromAddrMessage] = timeSomeoneElseReceivedMessageFromThisNode - with shared.printLock: - print 'added new node', peerFromAddrMessage, 'to knownNodes in stream', recaddrStream + logger.debug('added new node ' + str(peerFromAddrMessage) + ' to knownNodes in stream ' + str(recaddrStream)) shared.needToWriteKnownNodesToDisk = True hostDetails = ( @@ -606,8 +579,7 @@ class receiveDataThread(threading.Thread): with shared.knownNodesLock: shared.knownNodes[recaddrStream][peerFromAddrMessage] = timeSomeoneElseReceivedMessageFromThisNode - with shared.printLock: - print 'knownNodes currently has', len(shared.knownNodes[self.streamNumber]), 'nodes for this stream.' + logger.debug('knownNodes currently has ' + str(len(shared.knownNodes[self.streamNumber])) + ' nodes for this stream.') # Send a huge addr message to our peer. This is only used @@ -706,8 +678,7 @@ class receiveDataThread(threading.Thread): self.services, = unpack('>q', data[4:12]) if self.remoteProtocolVersion < 3: self.sendDataThreadQueue.put((0, 'shutdown','no data')) - with shared.printLock: - print 'Closing connection to old protocol version', self.remoteProtocolVersion, 'node: ', self.peer + logger.debug ('Closing connection to old protocol version ' + str(self.remoteProtocolVersion) + ' node: ' + str(self.peer)) return timestamp, = unpack('>Q', data[12:20]) timeOffset = timestamp - int(time.time()) @@ -748,13 +719,11 @@ class receiveDataThread(threading.Thread): readPosition += lengthOfNumberOfStreamsInVersionMessage self.streamNumber, lengthOfRemoteStreamNumber = decodeVarint( data[readPosition:]) - with shared.printLock: - print 'Remote node useragent:', useragent, ' stream number:', self.streamNumber, ' time offset:', timeOffset, 'seconds.' + logger.debug('Remote node useragent: ' + useragent + ' stream number:' + str(self.streamNumber) + ' time offset: ' + str(timeOffset) + ' seconds.') if self.streamNumber != 1: self.sendDataThreadQueue.put((0, 'shutdown','no data')) - with shared.printLock: - print 'Closed connection to', self.peer, 'because they are interested in stream', self.streamNumber, '.' + logger.debug ('Closed connection to ' + str(self.peer) + ' because they are interested in stream ' + str(self.streamNumber) + '.') return shared.connectedHostsList[ self.peer.host] = 1 # We use this data structure to not only keep track of what hosts we are connected to so that we don't try to connect to them again, but also to list the connections count on the Network Status tab. @@ -764,8 +733,7 @@ class receiveDataThread(threading.Thread): self.sendDataThreadQueue.put((0, 'setStreamNumber', self.streamNumber)) if data[72:80] == shared.eightBytesOfRandomDataUsedToDetectConnectionsToSelf: self.sendDataThreadQueue.put((0, 'shutdown','no data')) - with shared.printLock: - print 'Closing connection to myself: ', self.peer + logger.debug('Closing connection to myself: ' + str(self.peer)) return # The other peer's protocol version is of interest to the sendDataThread but we learn of it @@ -782,16 +750,14 @@ class receiveDataThread(threading.Thread): # Sends a version message def sendversion(self): - with shared.printLock: - print 'Sending version message' + logger.debug('Sending version message') self.sendDataThreadQueue.put((0, 'sendRawData', shared.assembleVersionMessage( self.peer.host, self.peer.port, self.streamNumber))) # Sends a verack message def sendverack(self): - with shared.printLock: - print 'Sending verack' + logger.debug('Sending verack') self.sendDataThreadQueue.put((0, 'sendRawData', shared.CreatePacket('verack'))) self.verackSent = True if self.verackReceived: diff --git a/src/class_sendDataThread.py b/src/class_sendDataThread.py index 4ee88399..1a839d50 100644 --- a/src/class_sendDataThread.py +++ b/src/class_sendDataThread.py @@ -11,13 +11,14 @@ import socket from helper_generic import addDataPadding from class_objectHashHolder import * from addresses import * +from debug import logger # Every connection to a peer has a sendDataThread (and also a # receiveDataThread). class sendDataThread(threading.Thread): def __init__(self, sendDataThreadQueue): - threading.Thread.__init__(self) + threading.Thread.__init__(self, name="sendData") self.sendDataThreadQueue = sendDataThreadQueue shared.sendDataQueues.append(self.sendDataThreadQueue) self.data = '' @@ -35,6 +36,7 @@ class sendDataThread(threading.Thread): someObjectsOfWhichThisRemoteNodeIsAlreadyAware): self.sock = sock self.peer = shared.Peer(HOST, PORT) + self.name = "sendData-" + self.peer.host self.streamNumber = streamNumber self.services = 0 self.remoteProtocolVersion = - \ @@ -42,23 +44,20 @@ class sendDataThread(threading.Thread): self.lastTimeISentData = int( time.time()) # If this value increases beyond five minutes ago, we'll send a pong message to keep the connection alive. self.someObjectsOfWhichThisRemoteNodeIsAlreadyAware = someObjectsOfWhichThisRemoteNodeIsAlreadyAware - with shared.printLock: - print 'The streamNumber of this sendDataThread (ID:', str(id(self)) + ') at setup() is', self.streamNumber + logger.debug('The streamNumber of this sendDataThread (ID: ' + str(id(self)) + ') at setup() is' + str(self.streamNumber)) def sendVersionMessage(self): datatosend = shared.assembleVersionMessage( self.peer.host, self.peer.port, self.streamNumber) # the IP and port of the remote host, and my streamNumber. - with shared.printLock: - print 'Sending version packet: ', repr(datatosend) + logger.debug('Sending version packet: ' + repr(datatosend)) try: self.sendBytes(datatosend) except Exception as err: # if not 'Bad file descriptor' in err: - with shared.printLock: - sys.stderr.write('sock.sendall error: %s\n' % err) + logger.error('sock.sendall error: %s\n' % err) self.versionSent = 1 @@ -94,15 +93,13 @@ class sendDataThread(threading.Thread): def run(self): - with shared.printLock: - print 'sendDataThread starting. ID:', str(id(self))+'. Number of queues in sendDataQueues:', len(shared.sendDataQueues) + logger.debug('sendDataThread starting. ID: ' + str(id(self)) + '. Number of queues in sendDataQueues: ' + str(len(shared.sendDataQueues))) while True: deststream, command, data = self.sendDataThreadQueue.get() if deststream == self.streamNumber or deststream == 0: if command == 'shutdown': - with shared.printLock: - print 'sendDataThread (associated with', self.peer, ') ID:', id(self), 'shutting down now.' + logger.debug('sendDataThread (associated with ' + str(self.peer) + ') ID: ' + str(id(self)) + ' shutting down now.') break # When you receive an incoming connection, a sendDataThread is # created even though you don't yet know what stream number the @@ -112,12 +109,10 @@ class sendDataThread(threading.Thread): # streamNumber of this send data thread here: elif command == 'setStreamNumber': self.streamNumber = data - with shared.printLock: - print 'setting the stream number in the sendData thread (ID:', id(self), ') to', self.streamNumber + logger.debug('setting the stream number in the sendData thread (ID: ' + str(id(self)) + ') to ' + str(self.streamNumber)) elif command == 'setRemoteProtocolVersion': specifiedRemoteProtocolVersion = data - with shared.printLock: - print 'setting the remote node\'s protocol version in the sendDataThread (ID:', id(self), ') to', specifiedRemoteProtocolVersion + logger.debug('setting the remote node\'s protocol version in the sendDataThread (ID: ' + str(id(self)) + ') to ' + str(specifiedRemoteProtocolVersion)) self.remoteProtocolVersion = specifiedRemoteProtocolVersion elif command == 'advertisepeer': self.objectHashHolderInstance.holdPeer(data) @@ -140,8 +135,7 @@ class sendDataThread(threading.Thread): try: self.sendBytes(packet) except: - with shared.printLock: - print 'sendaddr: self.sock.sendall failed' + logger.error('sendaddr: self.sock.sendall failed') break elif command == 'advertiseobject': self.objectHashHolderInstance.holdHash(data) @@ -157,35 +151,30 @@ class sendDataThread(threading.Thread): try: self.sendBytes(packet) except: - with shared.printLock: - print 'sendinv: self.sock.sendall failed' + logger.error('sendinv: self.sock.sendall failed') break elif command == 'pong': self.someObjectsOfWhichThisRemoteNodeIsAlreadyAware.clear() # To save memory, let us clear this data structure from time to time. As its function is to help us keep from sending inv messages to peers which sent us the same inv message mere seconds earlier, it will be fine to clear this data structure from time to time. if self.lastTimeISentData < (int(time.time()) - 298): # Send out a pong message to keep the connection alive. - with shared.printLock: - print 'Sending pong to', self.peer, 'to keep connection alive.' + logger.debug('Sending pong to ' + str(self.peer) + ' to keep connection alive.') packet = shared.CreatePacket('pong') try: self.sendBytes(packet) except: - with shared.printLock: - print 'send pong failed' + logger.error('send pong failed') break elif command == 'sendRawData': try: self.sendBytes(data) except: - with shared.printLock: - print 'Sending of data to', self.peer, 'failed. sendDataThread thread', self, 'ending now.' + logger.error('Sending of data to ' + str(self.peer) + ' failed. sendDataThread thread ' + str(self) + ' ending now.') break elif command == 'connectionIsOrWasFullyEstablished': self.connectionIsOrWasFullyEstablished = True self.services, self.sslSock = data else: - with shared.printLock: - print 'sendDataThread ID:', id(self), 'ignoring command', command, 'because the thread is not in stream', deststream + logger.error('sendDataThread ID: ' + id(self) + ' ignoring command ' + command + ' because the thread is not in stream' + deststream) try: self.sock.shutdown(socket.SHUT_RDWR) @@ -193,6 +182,5 @@ class sendDataThread(threading.Thread): except: pass shared.sendDataQueues.remove(self.sendDataThreadQueue) - with shared.printLock: - print 'sendDataThread ending. ID:', str(id(self))+'. Number of queues in sendDataQueues:', len(shared.sendDataQueues) + logger.info('sendDataThread ending. ID: ' + str(id(self)) + '. Number of queues in sendDataQueues: ' + str(len(shared.sendDataQueues))) self.objectHashHolderInstance.close() diff --git a/src/class_singleCleaner.py b/src/class_singleCleaner.py index b441d11c..bccf4a62 100644 --- a/src/class_singleCleaner.py +++ b/src/class_singleCleaner.py @@ -31,7 +31,7 @@ resends msg messages in 5 days (then 10 days, then 20 days, etc...) class singleCleaner(threading.Thread): def __init__(self): - threading.Thread.__init__(self) + threading.Thread.__init__(self, name="singleCleaner") def run(self): timeWeLastClearedInventoryAndPubkeysTables = 0 @@ -83,9 +83,7 @@ class singleCleaner(threading.Thread): int(time.time()) - shared.maximumLengthOfTimeToBotherResendingMessages) for row in queryreturn: if len(row) < 2: - with shared.printLock: - sys.stderr.write( - 'Something went wrong in the singleCleaner thread: a query did not return the requested fields. ' + repr(row)) + logger.error('Something went wrong in the singleCleaner thread: a query did not return the requested fields. ' + repr(row)) time.sleep(3) break toAddress, ackData, status = row diff --git a/src/class_singleListener.py b/src/class_singleListener.py index a33158a3..77e6eee6 100644 --- a/src/class_singleListener.py +++ b/src/class_singleListener.py @@ -18,7 +18,7 @@ import re class singleListener(threading.Thread): def __init__(self): - threading.Thread.__init__(self) + threading.Thread.__init__(self, name="singleListener") def setup(self, selfInitiatedConnections): self.selfInitiatedConnections = selfInitiatedConnections @@ -54,8 +54,7 @@ class singleListener(threading.Thread): while shared.config.get('bitmessagesettings', 'socksproxytype')[0:5] == 'SOCKS' and not shared.config.getboolean('bitmessagesettings', 'sockslisten'): time.sleep(5) - with shared.printLock: - print 'Listening for incoming connections.' + logger.info('Listening for incoming connections.') # First try listening on an IPv6 socket. This should also be # able to accept connections on IPv4. If that's not available @@ -82,8 +81,7 @@ class singleListener(threading.Thread): while shared.config.get('bitmessagesettings', 'socksproxytype')[0:5] == 'SOCKS' and not shared.config.getboolean('bitmessagesettings', 'sockslisten'): time.sleep(10) while len(shared.connectedHostsList) > 220: - with shared.printLock: - print 'We are connected to too many people. Not accepting further incoming connections for ten seconds.' + logger.info('We are connected to too many people. Not accepting further incoming connections for ten seconds.') time.sleep(10) @@ -104,8 +102,7 @@ class singleListener(threading.Thread): # connection flooding. if HOST in shared.connectedHostsList: socketObject.close() - with shared.printLock: - print 'We are already connected to', HOST + '. Ignoring connection.' + logger.info('We are already connected to ' + str(HOST) + '. Ignoring connection.') else: break @@ -124,6 +121,5 @@ class singleListener(threading.Thread): socketObject, HOST, PORT, -1, someObjectsOfWhichThisRemoteNodeIsAlreadyAware, self.selfInitiatedConnections, sendDataThreadQueue) rd.start() - with shared.printLock: - print self, 'connected to', HOST, 'during INCOMING request.' + logger.info('connected to ' + HOST + ' during INCOMING request.') diff --git a/src/class_singleWorker.py b/src/class_singleWorker.py index 888d96f6..de8848b2 100644 --- a/src/class_singleWorker.py +++ b/src/class_singleWorker.py @@ -25,7 +25,7 @@ class singleWorker(threading.Thread): def __init__(self): # QThread.__init__(self, parent) - threading.Thread.__init__(self) + threading.Thread.__init__(self, name="singleWorker") def run(self): @@ -49,7 +49,7 @@ class singleWorker(threading.Thread): '''SELECT ackdata FROM sent where (status='msgsent' OR status='doingmsgpow')''') for row in queryreturn: ackdata, = row - print 'Watching for ackdata', ackdata.encode('hex') + logger.info('Watching for ackdata ' + ackdata.encode('hex')) shared.ackdataForWhichImWatching[ackdata] = 0 time.sleep( @@ -81,9 +81,7 @@ class singleWorker(threading.Thread): elif command == 'sendOutOrStoreMyV4Pubkey': self.sendOutOrStoreMyV4Pubkey(data) else: - with shared.printLock: - sys.stderr.write( - 'Probable programming error: The command sent to the workerThread is weird. It is: %s\n' % command) + logger.error('Probable programming error: The command sent to the workerThread is weird. It is: %s\n' % command) shared.workerQueue.task_done() @@ -114,9 +112,7 @@ class singleWorker(threading.Thread): privEncryptionKeyBase58 = shared.config.get( myAddress, 'privencryptionkey') except Exception as err: - with shared.printLock: - sys.stderr.write( - 'Error within doPOWForMyV2Pubkey. Could not read the keys from the keys.dat file for a requested address. %s\n' % err) + logger.error('Error within doPOWForMyV2Pubkey. Could not read the keys from the keys.dat file for a requested address. %s\n' % err) return privSigningKeyHex = shared.decodeWalletImportFormat( @@ -133,10 +129,10 @@ class singleWorker(threading.Thread): # Do the POW for this pubkey message target = 2 ** 64 / (shared.networkDefaultProofOfWorkNonceTrialsPerByte*(len(payload) + 8 + shared.networkDefaultPayloadLengthExtraBytes + ((TTL*(len(payload)+8+shared.networkDefaultPayloadLengthExtraBytes))/(2 ** 16)))) - print '(For pubkey message) Doing proof of work...' + logger.info('(For pubkey message) Doing proof of work...') initialHash = hashlib.sha512(payload).digest() trialValue, nonce = proofofwork.run(target, initialHash) - print '(For pubkey message) Found proof of work', trialValue, 'Nonce:', nonce + logger.info('(For pubkey message) Found proof of work ' + str(trialValue), ' Nonce: ', str(nonce)) payload = pack('>Q', nonce) + payload inventoryHash = calculateInventoryHash(payload) @@ -145,8 +141,7 @@ class singleWorker(threading.Thread): objectType, streamNumber, payload, embeddedTime,'') shared.inventorySets[streamNumber].add(inventoryHash) - with shared.printLock: - print 'broadcasting inv with hash:', inventoryHash.encode('hex') + logger.info('broadcasting inv with hash: ' + inventoryHash.encode('hex')) shared.broadcastToSendDataQueues(( streamNumber, 'advertiseobject', inventoryHash)) @@ -171,8 +166,7 @@ class singleWorker(threading.Thread): #The address has been deleted. return if shared.safeConfigGetBoolean(myAddress, 'chan'): - with shared.printLock: - print 'This is a chan address. Not sending pubkey.' + logger.info('This is a chan address. Not sending pubkey.') return status, addressVersionNumber, streamNumber, hash = decodeAddress( myAddress) @@ -199,9 +193,7 @@ class singleWorker(threading.Thread): privEncryptionKeyBase58 = shared.config.get( myAddress, 'privencryptionkey') except Exception as err: - with shared.printLock: - sys.stderr.write( - 'Error within sendOutOrStoreMyV3Pubkey. Could not read the keys from the keys.dat file for a requested address. %s\n' % err) + logger.error('Error within sendOutOrStoreMyV3Pubkey. Could not read the keys from the keys.dat file for a requested address. %s\n' % err) return @@ -228,12 +220,10 @@ class singleWorker(threading.Thread): # Do the POW for this pubkey message target = 2 ** 64 / (shared.networkDefaultProofOfWorkNonceTrialsPerByte*(len(payload) + 8 + shared.networkDefaultPayloadLengthExtraBytes + ((TTL*(len(payload)+8+shared.networkDefaultPayloadLengthExtraBytes))/(2 ** 16)))) - with shared.printLock: - print '(For pubkey message) Doing proof of work...' + logger.info('(For pubkey message) Doing proof of work...') initialHash = hashlib.sha512(payload).digest() trialValue, nonce = proofofwork.run(target, initialHash) - with shared.printLock: - print '(For pubkey message) Found proof of work. Nonce:', nonce + logger.info('(For pubkey message) Found proof of work. Nonce: ' + str(nonce)) payload = pack('>Q', nonce) + payload inventoryHash = calculateInventoryHash(payload) @@ -242,8 +232,7 @@ class singleWorker(threading.Thread): objectType, streamNumber, payload, embeddedTime,'') shared.inventorySets[streamNumber].add(inventoryHash) - with shared.printLock: - print 'broadcasting inv with hash:', inventoryHash.encode('hex') + logger.info('broadcasting inv with hash: ' + inventoryHash.encode('hex')) shared.broadcastToSendDataQueues(( streamNumber, 'advertiseobject', inventoryHash)) @@ -264,8 +253,7 @@ class singleWorker(threading.Thread): #The address has been deleted. return if shared.safeConfigGetBoolean(myAddress, 'chan'): - with shared.printLock: - print 'This is a chan address. Not sending pubkey.' + logger.info('This is a chan address. Not sending pubkey.') return status, addressVersionNumber, streamNumber, hash = decodeAddress( myAddress) @@ -285,9 +273,7 @@ class singleWorker(threading.Thread): privEncryptionKeyBase58 = shared.config.get( myAddress, 'privencryptionkey') except Exception as err: - with shared.printLock: - sys.stderr.write( - 'Error within sendOutOrStoreMyV4Pubkey. Could not read the keys from the keys.dat file for a requested address. %s\n' % err) + logger.error('Error within sendOutOrStoreMyV4Pubkey. Could not read the keys from the keys.dat file for a requested address. %s\n' % err) return privSigningKeyHex = shared.decodeWalletImportFormat( @@ -326,10 +312,10 @@ class singleWorker(threading.Thread): # Do the POW for this pubkey message target = 2 ** 64 / (shared.networkDefaultProofOfWorkNonceTrialsPerByte*(len(payload) + 8 + shared.networkDefaultPayloadLengthExtraBytes + ((TTL*(len(payload)+8+shared.networkDefaultPayloadLengthExtraBytes))/(2 ** 16)))) - print '(For pubkey message) Doing proof of work...' + logger.info('(For pubkey message) Doing proof of work...') initialHash = hashlib.sha512(payload).digest() trialValue, nonce = proofofwork.run(target, initialHash) - print '(For pubkey message) Found proof of work', trialValue, 'Nonce:', nonce + logger.info('(For pubkey message) Found proof of work ' + str(trialValue) + 'Nonce: ' + str(nonce)) payload = pack('>Q', nonce) + payload inventoryHash = calculateInventoryHash(payload) @@ -338,8 +324,7 @@ class singleWorker(threading.Thread): objectType, streamNumber, payload, embeddedTime, doubleHashOfAddressData[32:]) shared.inventorySets[streamNumber].add(inventoryHash) - with shared.printLock: - print 'broadcasting inv with hash:', inventoryHash.encode('hex') + logger.info('broadcasting inv with hash: ' + inventoryHash.encode('hex')) shared.broadcastToSendDataQueues(( streamNumber, 'advertiseobject', inventoryHash)) @@ -359,9 +344,7 @@ class singleWorker(threading.Thread): status, addressVersionNumber, streamNumber, ripe = decodeAddress( fromaddress) if addressVersionNumber <= 1: - with shared.printLock: - sys.stderr.write( - 'Error: In the singleWorker thread, the sendBroadcast function doesn\'t understand the address version.\n') + logger.error('Error: In the singleWorker thread, the sendBroadcast function doesn\'t understand the address version.\n') return # We need to convert our private keys to public keys in order # to include them. @@ -442,12 +425,12 @@ class singleWorker(threading.Thread): dataToEncrypt, pubEncryptionKey.encode('hex')) target = 2 ** 64 / (shared.networkDefaultProofOfWorkNonceTrialsPerByte*(len(payload) + 8 + shared.networkDefaultPayloadLengthExtraBytes + ((TTL*(len(payload)+8+shared.networkDefaultPayloadLengthExtraBytes))/(2 ** 16)))) - print '(For broadcast message) Doing proof of work...' + logger.info('(For broadcast message) Doing proof of work...') shared.UISignalQueue.put(('updateSentItemStatusByAckdata', ( ackdata, tr.translateText("MainWindow", "Doing work necessary to send broadcast...")))) initialHash = hashlib.sha512(payload).digest() trialValue, nonce = proofofwork.run(target, initialHash) - print '(For broadcast message) Found proof of work', trialValue, 'Nonce:', nonce + logger.info('(For broadcast message) Found proof of work ' + str(trialValue) + ' Nonce: ' + str(nonce)) payload = pack('>Q', nonce) + payload @@ -463,8 +446,7 @@ class singleWorker(threading.Thread): shared.inventory[inventoryHash] = ( objectType, streamNumber, payload, embeddedTime, tag) shared.inventorySets[streamNumber].add(inventoryHash) - with shared.printLock: - print 'sending inv (within sendBroadcast function) for object:', inventoryHash.encode('hex') + logger.info('sending inv (within sendBroadcast function) for object: ' + inventoryHash.encode('hex')) shared.broadcastToSendDataQueues(( streamNumber, 'advertiseobject', inventoryHash)) @@ -612,8 +594,8 @@ class singleWorker(threading.Thread): shared.ackdataForWhichImWatching[ackdata] = 0 shared.UISignalQueue.put(('updateSentItemStatusByAckdata', ( ackdata, tr.translateText("MainWindow", "Looking up the receiver\'s public key")))) - with shared.printLock: - print 'Sending a message. First 150 characters of message:', repr(message[:150]) + logger.info('Sending a message.') + logger.debug('First 150 characters of message: ' + repr(message[:150])) # Let us fetch the recipient's public key out of our database. If # the required proof of work difficulty is too hard then we'll @@ -685,8 +667,8 @@ class singleWorker(threading.Thread): requiredPayloadLengthExtraBytes) / shared.networkDefaultPayloadLengthExtraBytes)).arg(l10n.formatTimestamp())))) continue else: # if we are sending a message to ourselves or a chan.. - with shared.printLock: - print 'Sending a message. First 150 characters of message:', repr(message[:150]) + logger.info('Sending a message.') + logger.debug('First 150 characters of message: ' + repr(message[:150])) behaviorBitfield = '\x00\x00\x00\x01' try: @@ -694,9 +676,7 @@ class singleWorker(threading.Thread): toaddress, 'privencryptionkey') except Exception as err: shared.UISignalQueue.put(('updateSentItemStatusByAckdata',(ackdata,tr.translateText("MainWindow",'Problem: You are trying to send a message to yourself or a chan but your encryption key could not be found in the keys.dat file. Could not encrypt message. %1').arg(l10n.formatTimestamp())))) - with shared.printLock: - sys.stderr.write( - 'Error within sendMsg. Could not read the keys from the keys.dat file for our own address. %s\n' % err) + logger.error('Error within sendMsg. Could not read the keys from the keys.dat file for our own address. %s\n' % err) continue privEncryptionKeyHex = shared.decodeWalletImportFormat( privEncryptionKeyBase58).encode('hex') @@ -761,12 +741,10 @@ class singleWorker(threading.Thread): payload += encodeVarint(len(messageToTransmit)) payload += messageToTransmit if shared.config.has_section(toaddress): - with shared.printLock: - print 'Not bothering to include ackdata because we are sending to ourselves or a chan.' + logger.info('Not bothering to include ackdata because we are sending to ourselves or a chan.') fullAckPayload = '' elif not shared.isBitSetWithinBitfield(behaviorBitfield,31): - with shared.printLock: - print 'Not bothering to include ackdata because the receiver said that they won\'t relay it anyway.' + logger.info('Not bothering to include ackdata because the receiver said that they won\'t relay it anyway.') fullAckPayload = '' else: fullAckPayload = self.generateFullAckMessage( @@ -791,18 +769,16 @@ class singleWorker(threading.Thread): encryptedPayload += encodeVarint(1) # msg version encryptedPayload += encodeVarint(toStreamNumber) + encrypted target = 2 ** 64 / (requiredAverageProofOfWorkNonceTrialsPerByte*(len(encryptedPayload) + 8 + requiredPayloadLengthExtraBytes + ((TTL*(len(encryptedPayload)+8+requiredPayloadLengthExtraBytes))/(2 ** 16)))) - with shared.printLock: - print '(For msg message) Doing proof of work. Total required difficulty:', float(requiredAverageProofOfWorkNonceTrialsPerByte) / shared.networkDefaultProofOfWorkNonceTrialsPerByte, 'Required small message difficulty:', float(requiredPayloadLengthExtraBytes) / shared.networkDefaultPayloadLengthExtraBytes + logger.info('(For msg message) Doing proof of work. Total required difficulty: %f. Required small message difficulty: %f.', float(requiredAverageProofOfWorkNonceTrialsPerByte) / shared.networkDefaultProofOfWorkNonceTrialsPerByte, float(requiredPayloadLengthExtraBytes) / shared.networkDefaultPayloadLengthExtraBytes) powStartTime = time.time() initialHash = hashlib.sha512(encryptedPayload).digest() trialValue, nonce = proofofwork.run(target, initialHash) - with shared.printLock: - print '(For msg message) Found proof of work', trialValue, 'Nonce:', nonce - try: - print 'POW took', int(time.time() - powStartTime), 'seconds.', nonce / (time.time() - powStartTime), 'nonce trials per second.' - except: - pass + logger.info('(For msg message) Found proof of work ' + str(trialValue) + ' Nonce: ' + str(nonce)) + try: + logger.info('POW took ' + str(int(time.time() - powStartTime)) + ' seconds. ' + str(nonce / (time.time() - powStartTime)) + ' nonce trials per second.') + except: + pass encryptedPayload = pack('>Q', nonce) + encryptedPayload @@ -823,7 +799,7 @@ class singleWorker(threading.Thread): else: # not sending to a chan or one of my addresses shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr.translateText("MainWindow", "Message sent. Waiting for acknowledgement. Sent on %1").arg(l10n.formatTimestamp())))) - print 'Broadcasting inv for my msg(within sendmsg function):', inventoryHash.encode('hex') + logger.info('Broadcasting inv for my msg(within sendmsg function):' + inventoryHash.encode('hex')) shared.broadcastToSendDataQueues(( toStreamNumber, 'advertiseobject', inventoryHash)) @@ -871,8 +847,7 @@ class singleWorker(threading.Thread): toStatus, addressVersionNumber, streamNumber, ripe = decodeAddress( toAddress) if toStatus != 'success': - with shared.printLock: - sys.stderr.write('Very abnormal error occurred in requestPubKey. toAddress is: ' + repr( + logger.error('Very abnormal error occurred in requestPubKey. toAddress is: ' + repr( toAddress) + '. Please report this error to Atheros.') return @@ -907,12 +882,10 @@ class singleWorker(threading.Thread): payload += encodeVarint(streamNumber) if addressVersionNumber <= 3: payload += ripe - with shared.printLock: - print 'making request for pubkey with ripe:', ripe.encode('hex') + logger.info('making request for pubkey with ripe:', ripe.encode('hex')) else: payload += tag - with shared.printLock: - print 'making request for v4 pubkey with tag:', tag.encode('hex') + logger.info('making request for v4 pubkey with tag:', tag.encode('hex')) # print 'trial value', trialValue statusbar = 'Doing the computations necessary to request the recipient\'s public key.' @@ -923,8 +896,7 @@ class singleWorker(threading.Thread): target = 2 ** 64 / (shared.networkDefaultProofOfWorkNonceTrialsPerByte*(len(payload) + 8 + shared.networkDefaultPayloadLengthExtraBytes + ((TTL*(len(payload)+8+shared.networkDefaultPayloadLengthExtraBytes))/(2 ** 16)))) initialHash = hashlib.sha512(payload).digest() trialValue, nonce = proofofwork.run(target, initialHash) - with shared.printLock: - print 'Found proof of work', trialValue, 'Nonce:', nonce + logger.info('Found proof of work ' + str(trialValue) + ' Nonce: ' + str(nonce)) payload = pack('>Q', nonce) + payload inventoryHash = calculateInventoryHash(payload) @@ -932,7 +904,7 @@ class singleWorker(threading.Thread): shared.inventory[inventoryHash] = ( objectType, streamNumber, payload, embeddedTime, '') shared.inventorySets[streamNumber].add(inventoryHash) - print 'sending inv (for the getpubkey message)' + logger.info('sending inv (for the getpubkey message)') shared.broadcastToSendDataQueues(( streamNumber, 'advertiseobject', inventoryHash)) @@ -976,18 +948,16 @@ class singleWorker(threading.Thread): payload += encodeVarint(toStreamNumber) + ackdata target = 2 ** 64 / (shared.networkDefaultProofOfWorkNonceTrialsPerByte*(len(payload) + 8 + shared.networkDefaultPayloadLengthExtraBytes + ((TTL*(len(payload)+8+shared.networkDefaultPayloadLengthExtraBytes))/(2 ** 16)))) - with shared.printLock: - print '(For ack message) Doing proof of work. TTL set to', TTL + logger.info('(For ack message) Doing proof of work. TTL set to ' + str(TTL)) powStartTime = time.time() initialHash = hashlib.sha512(payload).digest() trialValue, nonce = proofofwork.run(target, initialHash) - with shared.printLock: - print '(For ack message) Found proof of work', trialValue, 'Nonce:', nonce - try: - print 'POW took', int(time.time() - powStartTime), 'seconds.', nonce / (time.time() - powStartTime), 'nonce trials per second.' - except: - pass + logger.info('(For ack message) Found proof of work ' + str(trialValue) + ' Nonce: ' + str(nonce)) + try: + logger.info('POW took ' + str(time.time() - powStartTime) + ' seconds. ' + str(nonce / (time.time() - powStartTime)) + ' nonce trials per second.') + except: + pass payload = pack('>Q', nonce) + payload return shared.CreatePacket('object', payload) diff --git a/src/class_sqlThread.py b/src/class_sqlThread.py index 9de44a42..63621c89 100644 --- a/src/class_sqlThread.py +++ b/src/class_sqlThread.py @@ -19,7 +19,7 @@ import tr#anslate class sqlThread(threading.Thread): def __init__(self): - threading.Thread.__init__(self) + threading.Thread.__init__(self, name="SQL") def run(self): self.conn = sqlite3.connect(shared.appdata + 'messages.dat') diff --git a/src/debug.py b/src/debug.py index 30c64ea4..d430e5e3 100644 --- a/src/debug.py +++ b/src/debug.py @@ -18,15 +18,33 @@ Use: `from debug import logger` to import this facility into whatever module you ''' import logging import logging.config +import os import shared import sys +import traceback import helper_startup helper_startup.loadConfig() # TODO(xj9): Get from a config file. log_level = 'DEBUG' +def log_uncaught_exceptions(ex_cls, ex, tb): + logging.critical(''.join(traceback.format_tb(tb))) + logging.critical('{0}: {1}'.format(ex_cls, ex)) + def configureLogging(): + have_logging = False + try: + logging.config.fileConfig(os.path.join (shared.appdata, 'logging.dat')) + have_logging = True + except: + pass + + sys.excepthook = log_uncaught_exceptions + + if have_logging: + return False + logging.config.dictConfig({ 'version': 1, 'formatters': { @@ -69,13 +87,17 @@ def configureLogging(): 'handlers': ['console'], }, }) + return True + # TODO (xj9): Get from a config file. #logger = logging.getLogger('console_only') -configureLogging() -if '-c' in sys.argv: - logger = logging.getLogger('file_only') +if configureLogging(): + if '-c' in sys.argv: + logger = logging.getLogger('file_only') + else: + logger = logging.getLogger('both') else: - logger = logging.getLogger('both') + logger = logging.getLogger('default') def restartLoggingInUpdatedAppdataLocation(): global logger @@ -83,9 +105,11 @@ def restartLoggingInUpdatedAppdataLocation(): logger.removeHandler(i) i.flush() i.close() - configureLogging() - if '-c' in sys.argv: - logger = logging.getLogger('file_only') + if configureLogging(): + if '-c' in sys.argv: + logger = logging.getLogger('file_only') + else: + logger = logging.getLogger('both') else: - logger = logging.getLogger('both') + logger = logging.getLogger('default') diff --git a/src/helper_bootstrap.py b/src/helper_bootstrap.py index edeeb984..ae1adacb 100644 --- a/src/helper_bootstrap.py +++ b/src/helper_bootstrap.py @@ -4,6 +4,8 @@ import defaultKnownNodes import pickle import time +from debug import logger + def knownNodes(): try: # We shouldn't have to use the shared.knownNodesLock because this had @@ -26,7 +28,7 @@ def knownNodes(): except: shared.knownNodes = defaultKnownNodes.createDefaultKnownNodes(shared.appdata) if shared.config.getint('bitmessagesettings', 'settingsversion') > 10: - print 'Bitmessage cannot read future versions of the keys file (keys.dat). Run the newer version of Bitmessage.' + logger.error('Bitmessage cannot read future versions of the keys file (keys.dat). Run the newer version of Bitmessage.') raise SystemExit def dns(): @@ -39,16 +41,16 @@ def dns(): if shared.config.get('bitmessagesettings', 'socksproxytype') == 'none': try: for item in socket.getaddrinfo('bootstrap8080.bitmessage.org', 80): - print 'Adding', item[4][0], 'to knownNodes based on DNS boostrap method' + logger.info('Adding ' + item[4][0] + ' to knownNodes based on DNS bootstrap method') shared.knownNodes[1][shared.Peer(item[4][0], 8080)] = int(time.time()) except: - print 'bootstrap8080.bitmessage.org DNS bootstrapping failed.' + logger.error('bootstrap8080.bitmessage.org DNS bootstrapping failed.') try: for item in socket.getaddrinfo('bootstrap8444.bitmessage.org', 80): - print 'Adding', item[4][0], 'to knownNodes based on DNS boostrap method' + logger.info ('Adding ' + item[4][0] + ' to knownNodes based on DNS bootstrap method') shared.knownNodes[1][shared.Peer(item[4][0], 8444)] = int(time.time()) except: - print 'bootstrap8444.bitmessage.org DNS bootstrapping failed.' + logger.error('bootstrap8444.bitmessage.org DNS bootstrapping failed.') else: - print 'DNS bootstrap skipped because SOCKS is used.' + logger.info('DNS bootstrap skipped because SOCKS is used.') From 164ae6b31f365462b1342ff016663f37610f1d8b Mon Sep 17 00:00:00 2001 From: mailchuck Date: Thu, 19 Nov 2015 04:05:58 +0100 Subject: [PATCH 150/210] Smoother rerenderTabSubscriptions Does not reset it all the time but adds/removes Fixes #109 --- src/bitmessageqt/__init__.py | 84 +++++++++++++++++++++++++----------- src/bitmessageqt/account.py | 20 +++++++++ 2 files changed, 80 insertions(+), 24 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 1e0ae17b..cb0caa13 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -378,37 +378,73 @@ class MyForm(settingsmixin.SMainWindow): def rerenderTabTreeSubscriptions(self): treeWidget = self.ui.treeWidgetSubscriptions folders = ['inbox', 'sent', 'trash'] - treeWidget.clear() - treeWidget.setSortingEnabled(False) - treeWidget.header().setSortIndicator(0, Qt.AscendingOrder) - db = {} - queryreturn = sqlQuery('''SELECT fromaddress, folder, count(msgid) as cnt - FROM inbox, subscriptions - WHERE read = 0 AND subscriptions.address = inbox.fromaddress - GROUP BY inbox.fromaddress, folder''') - for row in queryreturn: - fromaddress, folder, cnt = row - if fromaddress not in db: - db[fromaddress] = {} - db[fromaddress][folder] = cnt - queryreturn = sqlQuery('SELECT label, address, enabled FROM subscriptions') - for row in queryreturn: - label, address, enabled = row - newItem = Ui_SubscriptionWidget(treeWidget, 0, address, 0, label, enabled) + # sort ascending when creating + if treeWidget.topLevelItemCount() == 0: + treeWidget.header().setSortIndicator(0, Qt.AscendingOrder) + # init dictionary + + db = getSortedSubscriptions(True) + + if treeWidget.isSortingEnabled(): + treeWidget.setSortingEnabled(False) + + widgets = {} + i = 0 + while i < treeWidget.topLevelItemCount(): + widget = treeWidget.topLevelItem(i) + if widget is not None: + toAddress = widget.address + else: + toAddress = None + + if not toAddress in db: + treeWidget.takeTopLevelItem(i) + # no increment + continue + unread = 0 + j = 0 + while j < widget.childCount(): + subwidget = widget.child(j) + try: + subwidget.setUnreadCount(db[toAddress][subwidget.folderName]['count']) + unread += db[toAddress][subwidget.folderName]['count'] + db[toAddress].pop(subwidget.folderName, None) + except: + widget.takeChild(j) + # no increment + continue + j += 1 + + # add missing folders + if len(db[toAddress]) > 0: + j = 0 + for f, c in db[toAddress].iteritems(): + subwidget = Ui_FolderWidget(widget, j, toAddress, f, c['count']) + j += 1 + widget.setUnreadCount(unread) + db.pop(toAddress, None) + i += 1 + + i = 0 + for toAddress in db: + widget = Ui_SubscriptionWidget(treeWidget, i, toAddress, db[toAddress]["inbox"]['count'], db[toAddress]["inbox"]['label'], db[toAddress]["inbox"]['enabled']) + j = 0 unread = 0 for folder in folders: try: - newSubItem = Ui_FolderWidget(newItem, 0, address, folder, db[address][folder]) - unread += db[address][folder] + subwidget = Ui_FolderWidget(widget, j, toAddress, folder, db[toAddress][folder]['count']) + unread += db[toAddress][folder]['count'] except KeyError: - newSubItem = Ui_FolderWidget(newItem, 0, address, folder, 0) - - newItem.setUnreadCount(unread) - newItem.setFlags (newItem.flags() | QtCore.Qt.ItemIsEditable) - + subwidget = Ui_FolderWidget(widget, j, toAddress, folder, 0) + j += 1 + widget.setUnreadCount(unread) + widget.setFlags (widget.flags() | QtCore.Qt.ItemIsEditable) + i += 1 + treeWidget.setSortingEnabled(True) + def rerenderTabTreeMessages(self): self.rerenderTabTree('messages') diff --git a/src/bitmessageqt/account.py b/src/bitmessageqt/account.py index 6e1083f6..83ce76c9 100644 --- a/src/bitmessageqt/account.py +++ b/src/bitmessageqt/account.py @@ -18,6 +18,26 @@ def getSortedAccounts(): ) return configSections +def getSortedSubscriptions(count = False): + queryreturn = sqlQuery('SELECT label, address, enabled FROM subscriptions ORDER BY label COLLATE NOCASE ASC') + ret = {} + for row in queryreturn: + label, address, enabled = row + ret[address] = {} + ret[address]["inbox"] = {} + ret[address]["inbox"]['label'] = label + ret[address]["inbox"]['enabled'] = enabled + ret[address]["inbox"]['count'] = 0 + if count: + queryreturn = sqlQuery('''SELECT fromaddress, folder, count(msgid) as cnt + FROM inbox, subscriptions + WHERE read = 0 AND subscriptions.address = inbox.fromaddress + GROUP BY inbox.fromaddress, folder''') + for row in queryreturn: + address, folder, cnt = row + ret[address][folder]['count'] = cnt + return ret + def accountClass(address): if not shared.config.has_section(address): if address == str_broadcast_subscribers: From 159b68fa175c082752a9c53f4ef65b98a87b351a Mon Sep 17 00:00:00 2001 From: mailchuck Date: Thu, 19 Nov 2015 04:14:13 +0100 Subject: [PATCH 151/210] Missing folders in rerenderTabTreeSubscriptions I optimised it too much and sometimes folders were missing in the tree. --- src/bitmessageqt/__init__.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index cb0caa13..b266c6d8 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -385,6 +385,10 @@ class MyForm(settingsmixin.SMainWindow): # init dictionary db = getSortedSubscriptions(True) + for address in db: + for folder in folders: + if not folder in db[address]: + db[address][folder] = {} if treeWidget.isSortingEnabled(): treeWidget.setSortingEnabled(False) @@ -420,7 +424,10 @@ class MyForm(settingsmixin.SMainWindow): if len(db[toAddress]) > 0: j = 0 for f, c in db[toAddress].iteritems(): - subwidget = Ui_FolderWidget(widget, j, toAddress, f, c['count']) + try: + subwidget = Ui_FolderWidget(widget, j, toAddress, f, c['count']) + except KeyError: + subwidget = Ui_FolderWidget(widget, j, toAddress, f, 0) j += 1 widget.setUnreadCount(unread) db.pop(toAddress, None) From ebcb06982038f33856b1391ea1687085e0068d96 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Thu, 19 Nov 2015 17:37:34 +0100 Subject: [PATCH 152/210] Delete key and shift modifier Addresses #114 - delete from sent works when pressing delete key - delete from trash deletes rather than moves - delete with shift deletes rather than moves --- src/bitmessageqt/__init__.py | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index b266c6d8..fc20162a 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -1053,7 +1053,7 @@ class MyForm(settingsmixin.SMainWindow): tableWidget.setSortingEnabled(False) tableWidget.horizontalHeader().setSortIndicator(3, Qt.DescendingOrder) - tableWidget.keyPressEvent = self.tableWidgetInboxKeyPressEvent + tableWidget.keyPressEvent = self.tableWidgetSentKeyPressEvent # Load messages from database file def loadMessagelist(self, tableWidget, account, folder="inbox", where="", what=""): @@ -1485,6 +1485,12 @@ class MyForm(settingsmixin.SMainWindow): self.on_action_InboxTrash() return QtGui.QTableWidget.keyPressEvent(self.getCurrentMessagelist(), event) + # set delete key in inbox + def tableWidgetSentKeyPressEvent(self, event): + if event.key() == QtCore.Qt.Key_Delete: + self.on_action_SentTrash() + return QtGui.QTableWidget.keyPressEvent(self.getCurrentMessagelist(), event) + # menu button 'manage keys' def click_actionManageKeys(self): if 'darwin' in sys.platform or 'linux' in sys.platform: @@ -3149,11 +3155,18 @@ class MyForm(settingsmixin.SMainWindow): return unread = False currentRow = 0 + folder = self.getCurrentFolder() + shifted = QtGui.QApplication.queryKeyboardModifiers() & QtCore.Qt.ShiftModifier while tableWidget.selectedIndexes(): currentRow = tableWidget.selectedIndexes()[0].row() inventoryHashToTrash = str(tableWidget.item( currentRow, 3).data(Qt.UserRole).toPyObject()) - sqlExecute('''UPDATE inbox SET folder='trash' WHERE msgid=?''', inventoryHashToTrash) + if folder == "trash" or shifted: + logger.debug ("deleting") + sqlExecute('''DELETE FROM inbox WHERE msgid=?''', inventoryHashToTrash) + else: + logger.debug ("moving to trash, because folder = \"%s\" and shifted = %r %x", folder, shifted) + sqlExecute('''UPDATE inbox SET folder='trash' WHERE msgid=?''', inventoryHashToTrash) if tableWidget.item(currentRow, 0).font().bold(): unread = True self.getCurrentMessageTextedit().setText("") @@ -3229,11 +3242,16 @@ class MyForm(settingsmixin.SMainWindow): tableWidget = self.getCurrentMessagelist() if not tableWidget: return + folder = self.getCurrentFolder() + shifted = (QtGui.QApplication.queryKeyboardModifiers() & QtCore.Qt.ShiftModifier) > 0 while tableWidget.selectedIndexes() != []: currentRow = tableWidget.selectedIndexes()[0].row() ackdataToTrash = str(tableWidget.item( currentRow, 3).data(Qt.UserRole).toPyObject()) - sqlExecute('''UPDATE sent SET folder='trash' WHERE ackdata=?''', ackdataToTrash) + if folder == "trash" or shifted: + sqlExecute('''DELETE FROM sent WHERE ackdata=?''', ackdataToTrash) + else: + sqlExecute('''UPDATE sent SET folder='trash' WHERE ackdata=?''', ackdataToTrash) if tableWidget.item(currentRow, 0).font().bold(): unread = True self.getCurrentMessageTextedit().setPlainText("") From 90303b461947eec4c55f2b0165b9a2904707d872 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Thu, 19 Nov 2015 17:42:04 +0100 Subject: [PATCH 153/210] Remove extra debug logging --- src/bitmessageqt/__init__.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index fc20162a..f3781cef 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -3162,10 +3162,8 @@ class MyForm(settingsmixin.SMainWindow): inventoryHashToTrash = str(tableWidget.item( currentRow, 3).data(Qt.UserRole).toPyObject()) if folder == "trash" or shifted: - logger.debug ("deleting") sqlExecute('''DELETE FROM inbox WHERE msgid=?''', inventoryHashToTrash) else: - logger.debug ("moving to trash, because folder = \"%s\" and shifted = %r %x", folder, shifted) sqlExecute('''UPDATE inbox SET folder='trash' WHERE msgid=?''', inventoryHashToTrash) if tableWidget.item(currentRow, 0).font().bold(): unread = True From b5fb485eb2b6d58a1afacdc534efd8f0f5f7d7c4 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Thu, 19 Nov 2015 18:45:46 +0100 Subject: [PATCH 154/210] Messagelist address tooltip Fixes #108 --- src/bitmessageqt/__init__.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index f3781cef..ca0d30ef 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -980,7 +980,7 @@ class MyForm(settingsmixin.SMainWindow): tableWidget.insertRow(0) toAddressItem = QtGui.QTableWidgetItem(unicode(acct.toLabel, 'utf-8')) - toAddressItem.setToolTip(unicode(acct.toLabel, 'utf-8')) + toAddressItem.setToolTip(unicode(acct.toLabel, 'utf-8') + " (" + str(acct.toAddress) + ")") toAddressItem.setIcon(avatarize(toAddress)) toAddressItem.setData(Qt.UserRole, str(toAddress)) toAddressItem.setTextColor(AccountColor(toAddress).accountColor()) @@ -989,7 +989,7 @@ class MyForm(settingsmixin.SMainWindow): tableWidget.setItem(0, 0, toAddressItem) fromAddressItem = QtGui.QTableWidgetItem(unicode(acct.fromLabel, 'utf-8')) - fromAddressItem.setToolTip(unicode(acct.fromLabel, 'utf-8')) + fromAddressItem.setToolTip(unicode(acct.fromLabel, 'utf-8') + " (" + str(acct.fromAddress) + ")") fromAddressItem.setIcon(avatarize(fromAddress)) fromAddressItem.setData(Qt.UserRole, str(fromAddress)) fromAddressItem.setTextColor(AccountColor(fromAddress).accountColor()) @@ -1115,7 +1115,7 @@ class MyForm(settingsmixin.SMainWindow): tableWidget.insertRow(0) # to to_item = QtGui.QTableWidgetItem(unicode(acct.toLabel, 'utf-8')) - to_item.setToolTip(unicode(acct.toLabel, 'utf-8')) + to_item.setToolTip(unicode(acct.toLabel, 'utf-8') + " (" + str(acct.toAddress) + ")") to_item.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) if not read: @@ -1126,7 +1126,7 @@ class MyForm(settingsmixin.SMainWindow): tableWidget.setItem(0, 0, to_item) # from from_item = QtGui.QTableWidgetItem(unicode(acct.fromLabel, 'utf-8')) - from_item.setToolTip(unicode(acct.fromLabel, 'utf-8')) + from_item.setToolTip(unicode(acct.fromLabel, 'utf-8') + " (" + str(fromAddress) + ")") from_item.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) if not read: @@ -2387,12 +2387,12 @@ class MyForm(settingsmixin.SMainWindow): sent.setSortingEnabled(False) sent.insertRow(0) newItem = QtGui.QTableWidgetItem(unicode(acct.toLabel, 'utf-8')) - newItem.setToolTip(unicode(acct.toLabel, 'utf-8')) + newItem.setToolTip(unicode(acct.toLabel, 'utf-8') + " (" + str(acct.toAddress) + ")") newItem.setData(Qt.UserRole, str(toAddress)) newItem.setIcon(avatarize(toAddress)) sent.setItem(0, 0, newItem) newItem = QtGui.QTableWidgetItem(unicode(acct.fromLabel, 'utf-8')) - newItem.setToolTip(unicode(acct.fromLabel, 'utf-8')) + newItem.setToolTip(unicode(acct.fromLabel, 'utf-8') + " (" + str(acct.fromAddress) + ")") newItem.setData(Qt.UserRole, str(fromAddress)) newItem.setIcon(avatarize(fromAddress)) sent.setItem(0, 1, newItem) @@ -2434,7 +2434,7 @@ class MyForm(settingsmixin.SMainWindow): font.setBold(True) inbox.setSortingEnabled(False) newItem = QtGui.QTableWidgetItem(unicode(acct.toLabel, 'utf-8')) - newItem.setToolTip(unicode(acct.toLabel, 'utf-8')) + newItem.setToolTip(unicode(acct.toLabel, 'utf-8') + " (" + str(acct.toAddress) + ")") newItem.setFont(font) newItem.setData(Qt.UserRole, str(toAddress)) newItem.setTextColor(AccountColor(toAddress).accountColor()) @@ -2443,7 +2443,7 @@ class MyForm(settingsmixin.SMainWindow): inbox.setItem(0, 0, newItem) newItem = QtGui.QTableWidgetItem(unicode(acct.fromLabel, 'utf-8')) - newItem.setToolTip(unicode(acct.fromLabel, 'utf-8')) + newItem.setToolTip(unicode(acct.fromLabel, 'utf-8') + " (" + str(acct.fromAddress) + ")") newItem.setData(Qt.UserRole, str(fromAddress)) newItem.setFont(font) newItem.setTextColor(AccountColor(fromAddress).accountColor()) From 5b07d8800aecf0e53e53a52b1971d6d0f2069b53 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Thu, 19 Nov 2015 19:17:26 +0100 Subject: [PATCH 155/210] Update addressbook on changes Fixes #99 - enable/disable new/delete chan/subscription - also found and fixed a logger crash --- src/bitmessageqt/__init__.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index ca0d30ef..f807b215 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -1569,7 +1569,7 @@ class MyForm(settingsmixin.SMainWindow): shared.apiAddressGeneratorReturnQueue.queue.clear() shared.addressGeneratorQueue.put(('createChan', 4, 1, self.str_chan + ' ' + str(self.newChanDialogInstance.ui.lineEditChanNameCreate.text().toUtf8()), self.newChanDialogInstance.ui.lineEditChanNameCreate.text().toUtf8())) addressGeneratorReturnValue = shared.apiAddressGeneratorReturnQueue.get() - logger.debug('addressGeneratorReturnValue ' + addressGeneratorReturnValue) + logger.debug('addressGeneratorReturnValue ' + str(addressGeneratorReturnValue)) if len(addressGeneratorReturnValue) == 0: QMessageBox.about(self, _translate("MainWindow", "Address already present"), _translate( "MainWindow", "Could not add chan because it appears to already be one of your identities.")) @@ -1594,7 +1594,7 @@ class MyForm(settingsmixin.SMainWindow): shared.apiAddressGeneratorReturnQueue.queue.clear() shared.addressGeneratorQueue.put(('joinChan', addBMIfNotPresent(self.newChanDialogInstance.ui.lineEditChanBitmessageAddress.text()), self.str_chan + ' ' + str(self.newChanDialogInstance.ui.lineEditChanNameJoin.text().toUtf8()), self.newChanDialogInstance.ui.lineEditChanNameJoin.text().toUtf8())) addressGeneratorReturnValue = shared.apiAddressGeneratorReturnQueue.get() - logger.debug('addressGeneratorReturnValue ' + addressGeneratorReturnValue) + logger.debug('addressGeneratorReturnValue ' + str(addressGeneratorReturnValue)) if addressGeneratorReturnValue == 'chan name does not match address': QMessageBox.about(self, _translate("MainWindow", "Address does not match chan name"), _translate( "MainWindow", "Although the Bitmessage address you entered was valid, it doesn\'t match the chan name.")) @@ -1607,6 +1607,7 @@ class MyForm(settingsmixin.SMainWindow): QMessageBox.about(self, _translate("MainWindow", "Success"), _translate( "MainWindow", "Successfully joined chan. ")) self.ui.tabWidget.setCurrentIndex(3) + self.rerenderAddressBook() def showConnectDialog(self): self.connectDialogInstance = connectDialog(self) @@ -2069,7 +2070,7 @@ class MyForm(settingsmixin.SMainWindow): addresses = getSortedAccounts() for address in addresses: account = accountClass(address) - if (account.type == 'chan'): + if (account.type == 'chan' and shared.safeConfigGetBoolean(address, 'enabled')): addRow(address, account.getLabel(), 'chan') # normal accounts @@ -2500,7 +2501,7 @@ class MyForm(settingsmixin.SMainWindow): sqlExecute('''INSERT INTO subscriptions VALUES (?,?,?)''',str(label),address,True) self.rerenderInboxFromLabels() shared.reloadBroadcastSendersForWhichImWatching() - + self.rerenderAddressBook() self.rerenderTabTreeSubscriptions() def click_pushButtonAddSubscription(self): @@ -3384,6 +3385,7 @@ class MyForm(settingsmixin.SMainWindow): address) self.rerenderTabTreeSubscriptions() self.rerenderInboxFromLabels() + self.rerenderAddressBook() shared.reloadBroadcastSendersForWhichImWatching() def on_action_SubscriptionsClipboard(self): @@ -3398,6 +3400,7 @@ class MyForm(settingsmixin.SMainWindow): address) account = self.getCurrentItem() account.setEnabled(True) + self.rerenderAddressBook() shared.reloadBroadcastSendersForWhichImWatching() def on_action_SubscriptionsDisable(self): @@ -3407,6 +3410,7 @@ class MyForm(settingsmixin.SMainWindow): address) account = self.getCurrentItem() account.setEnabled(False) + self.rerenderAddressBook() shared.reloadBroadcastSendersForWhichImWatching() def on_context_menuSubscriptions(self, point): @@ -3666,6 +3670,7 @@ class MyForm(settingsmixin.SMainWindow): return shared.writeKeysFile() shared.reloadMyAddressHashes() + self.rerenderAddressBook() if account.type == "normal": self.rerenderTabTreeMessages() elif account.type == "chan": @@ -3681,6 +3686,7 @@ class MyForm(settingsmixin.SMainWindow): shared.config.set(address, 'enabled', 'true') shared.writeKeysFile() shared.reloadMyAddressHashes() + self.rerenderAddressBook() def on_action_Disable(self): address = self.getCurrentAccount() @@ -3692,6 +3698,7 @@ class MyForm(settingsmixin.SMainWindow): shared.config.set(str(address), 'enabled', 'false') shared.writeKeysFile() shared.reloadMyAddressHashes() + self.rerenderAddressBook() def on_action_Clipboard(self): address = self.getCurrentAccount() From e62b9b59e6e041e93156ac4ede3cf536a8782f0d Mon Sep 17 00:00:00 2001 From: mailchuck Date: Thu, 19 Nov 2015 19:28:32 +0100 Subject: [PATCH 156/210] Ask before deleting a subscription Fixes #120 --- src/bitmessageqt/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index f807b215..baf2dc84 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -3380,6 +3380,8 @@ class MyForm(settingsmixin.SMainWindow): self.click_pushButtonAddSubscription() def on_action_SubscriptionsDelete(self): + if QtGui.QMessageBox.question(self, "Delete subscription?", _translate("MainWindow", "If you delete the subscription, messages that you already received will become inaccessible. Maybe you can consider disabling the subscription instead. Disabled subscriptions will not receive new messages, but you can still view messages you already received.\n\nAre you sure you want to delete the subscription?"), QMessageBox.Yes|QMessageBox.No) != QMessageBox.Yes: + return address = self.getCurrentAccount() sqlExecute('''DELETE FROM subscriptions WHERE address=?''', address) From 4fc2f441a348bba41147de95d026875c916c3695 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Thu, 19 Nov 2015 19:48:18 +0100 Subject: [PATCH 157/210] Trusted peer flood protection Is not needed. Fixed Bitmessage#786 --- src/class_receiveDataThread.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/class_receiveDataThread.py b/src/class_receiveDataThread.py index fa165401..c227648a 100644 --- a/src/class_receiveDataThread.py +++ b/src/class_receiveDataThread.py @@ -410,7 +410,7 @@ class receiveDataThread(threading.Thread): logger.info('inv message doesn\'t contain enough data. Ignoring.') return if numberOfItemsInInv == 1: # we'll just request this data from the person who advertised the object. - if totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers > 200000 and len(self.objectsThatWeHaveYetToGetFromThisPeer) > 1000: # inv flooding attack mitigation + if totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers > 200000 and len(self.objectsThatWeHaveYetToGetFromThisPeer) > 1000 and shared.trustedPeer == None: # inv flooding attack mitigation logger.debug('We already have ' + str(totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers) + ' items yet to retrieve from peers and over 1000 from this node in particular. Ignoring this inv message.') return self.someObjectsOfWhichThisRemoteNodeIsAlreadyAware[ @@ -433,7 +433,7 @@ class receiveDataThread(threading.Thread): objectsNewToMe = advertisedSet - shared.inventorySets[self.streamNumber] logger.info('inv message lists %s objects. Of those %s are new to me. It took %s seconds to figure that out.', numberOfItemsInInv, len(objectsNewToMe), time.time()-startTime) for item in objectsNewToMe: - if totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers > 200000 and len(self.objectsThatWeHaveYetToGetFromThisPeer) > 1000: # inv flooding attack mitigation + if totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers > 200000 and len(self.objectsThatWeHaveYetToGetFromThisPeer) > 1000 and shared.trustedPeer == None: # inv flooding attack mitigation logger.debug('We already have ' + str(totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers) + ' items yet to retrieve from peers and over ' + str(len(self.objectsThatWeHaveYetToGetFromThisPeer)), ' from this node in particular. Ignoring the rest of this inv message.') break self.someObjectsOfWhichThisRemoteNodeIsAlreadyAware[item] = 0 # helps us keep from sending inv messages to peers that already know about the objects listed therein From 8e9cdc8ff143759e800eabb1c33fd6f8654ed987 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Fri, 20 Nov 2015 08:49:44 +0100 Subject: [PATCH 158/210] Logging updates Came over BM (from "general" channel), I slightly modified it. --- src/bitmessageqt/__init__.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index baf2dc84..5eb7632e 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -1,3 +1,4 @@ +from debug import logger withMessagingMenu = False try: from gi.repository import MessagingMenu @@ -13,14 +14,14 @@ try: from PyQt4.QtNetwork import QLocalSocket, QLocalServer except Exception as err: - logger.error( 'PyBitmessage requires PyQt unless you want to run it as a daemon and interact with it using the API. You can download it from http://www.riverbankcomputing.com/software/pyqt/download or by searching Google for \'PyQt Download\' (without quotes).') - logger.error('Error message: ' + str(err)) + logmsg = 'PyBitmessage requires PyQt unless you want to run it as a daemon and interact with it using the API. You can download it from http://www.riverbankcomputing.com/software/pyqt/download or by searching Google for \'PyQt Download\' (without quotes).' + logger.critical(logmsg, exc_info=True) sys.exit() try: _encoding = QtGui.QApplication.UnicodeUTF8 except AttributeError: - logger.error('QtGui.QApplication.UnicodeUTF8 error: ' + str(err)) + logger.exception('QtGui.QApplication.UnicodeUTF8 error', exc_info=True) from addresses import * import shared @@ -52,7 +53,6 @@ import pickle import platform import textwrap import debug -from debug import logger import subprocess import datetime from helper_sql import * @@ -3231,7 +3231,7 @@ class MyForm(settingsmixin.SMainWindow): f.write(message) f.close() except Exception, e: - sys.stderr.write('Write error: '+ e) + logger.exception('Message not saved', exc_info=True) self.statusBar().showMessage(_translate("MainWindow", "Write error.")) # Send item on the Sent tab to trash From 7ec64a171935ee4a41b1127278b45483a8fec63c Mon Sep 17 00:00:00 2001 From: mailchuck Date: Fri, 20 Nov 2015 19:33:21 +0100 Subject: [PATCH 159/210] Remove ebuildpackage Fixes Bitmessage#331 --- ebuildpackage/pybitmessage-0.3.5-1.ebuild | 32 ----------------------- 1 file changed, 32 deletions(-) delete mode 100755 ebuildpackage/pybitmessage-0.3.5-1.ebuild diff --git a/ebuildpackage/pybitmessage-0.3.5-1.ebuild b/ebuildpackage/pybitmessage-0.3.5-1.ebuild deleted file mode 100755 index 01eddc4f..00000000 --- a/ebuildpackage/pybitmessage-0.3.5-1.ebuild +++ /dev/null @@ -1,32 +0,0 @@ -# $Header: $ - -EAPI=5 - -inherit git-2 python-r1 - -PYTHON_COMPAT=( python2_7 ) -PYTHON_REQ_USE="sqlite" -REQUIRED_USE="${PYTHON_REQUIRED_USE}" -DESCRIPTION="Bitmessage is a P2P communications protocol used to send encrypted messages to another person or to many subscribers. It is decentralized and trustless, meaning that you need-not inherently trust any entities like root certificate authorities. It uses strong authentication which means that the sender of a message cannot be spoofed, and it aims to hide "non-content" data, like the sender and receiver of messages, from passive eavesdroppers like those running warrantless wiretapping programs." -HOMEPAGE="https://github.com/Bitmessage/PyBitmessage" -EGIT_REPO_URI="https://github.com/Bitmessage/PyBitmessage.git" -LICENSE="MIT" -SLOT="0" -KEYWORDS="x86" -DEPEND="dev-libs/popt - ${PYTHON_DEPS}" -RDEPEND="${DEPEND} - dev-libs/openssl - dev-python/PyQt4[]" - -src_configure() { - econf --with-popt -} - -src_compile() { :; } - -src_install() { - emake DESTDIR="${D}" PREFIX="/usr" install - # Install README and (Debian) changelog - dodoc README.md debian/changelog -} From 51a152e44e648b4817473ef03aecf1c10c1f323e Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 21 Nov 2015 00:39:23 +0100 Subject: [PATCH 160/210] Cleanup UPnP --- src/upnp.py | 78 ++++++++++++++++++++++++++++------------------------- 1 file changed, 41 insertions(+), 37 deletions(-) diff --git a/src/upnp.py b/src/upnp.py index d0ec5b01..b3ce48f1 100644 --- a/src/upnp.py +++ b/src/upnp.py @@ -7,7 +7,6 @@ from struct import unpack, pack import shared routers = [] -recurDepth = 0 def searchRouter(): from debug import logger @@ -133,6 +132,20 @@ class Router: if service.childNodes[0].data.find('WANIPConnection') > 0: self.path = service.parentNode.getElementsByTagName('controlURL')[0].childNodes[0].data + # get local IP + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + try: + logger.debug("Connecting to %s:%i", self.address, self.routerPath.port) + s.connect ((self.address, self.routerPath.port)) + except: + pass + self.localAddress = s.getsockname()[0] + logger.debug("Local IP: %s", self.localAddress) + try: + s.close() + except: + pass + def AddPortMapping(self, externalPort, internalPort, internalClient, protocol, description, leaseDuration = 0, enabled = 1): resp = self.soapRequest('WANIPConnection:1', 'AddPortMapping', [ ('NewExternalPort', str(externalPort)), @@ -143,6 +156,7 @@ class Router: ('NewPortMappingDescription', str(description)), ('NewLeaseDuration', str(leaseDuration)) ]) + self.extPort = externalPort return resp def DeletePortMapping(self, externalPort, protocol): @@ -160,6 +174,7 @@ class Router: def soapRequest(self, service, action, arguments=[]): from xml.dom.minidom import parseString + from debug import logger conn = httplib.HTTPConnection(self.routerPath.hostname, self.routerPath.port) conn.request( 'POST', @@ -174,52 +189,41 @@ class Router: dom = parseString(resp) errinfo = dom.getElementsByTagName('errorDescription') if len(errinfo) > 0: + logger.error("UPnP error: %s", resp) raise UPnPError(errinfo[0].childNodes[0].data) return resp +def createPortMappingInternal(router): + from debug import logger + + for i in range(0, 50): + try: + routerIP, = unpack('>I', socket.inet_aton(router.address)) + localIP = router.localAddress + localPort = shared.config.getint('bitmessagesettings', 'port') + if i == 0: + extPort = localPort # try same port first + else: + extPort = randint(32767, 65535) + logger.debug("Requesting UPnP mapping for %s:%i on external port %i", localIP, localPort, extPort) + router.AddPortMapping(extPort, localPort, localIP, 'TCP', 'BitMessage') + logger.info("Successfully established UPnP mapping for %s:%i on external port %i", localIP, localPort, extPort) + shared.extPort = extPort + break + except UPnPError: + logger.debug("UPnP error: ", exc_info=True) def createPortMapping(): from debug import logger global routers - global recurDepth - - if recurDepth > 50: - logger.error("Too many failures requesting UPnP port mapping, giving up.") - return - + routers = searchRouter() - localIPs = socket.gethostbyname_ex(socket.gethostname())[2] logger.debug("Found %i UPnP routers", len(routers)) - for i in range(len(localIPs)): - localIPs[i], = unpack('>I', socket.inet_aton(localIPs[i])) - try: - #add port mapping for each router - for router in routers: - routerIP, = unpack('>I', socket.inet_aton(router.address)) - localIP = None - minDiff = 0xFFFFFFFF - #find nearest localIP as clientIP to specified router - for IP in localIPs: - if IP ^ routerIP < minDiff: - minDiff = IP ^ routerIP - localIP = IP - - localIP = socket.inet_ntoa(pack('>I', localIP)) - localPort = shared.config.getint('bitmessagesettings', 'port') - if recurDepth == 0: - extPort = localPort # try same port first - else: - extPort = str(randint(32767, 65535)) - logger.debug("Requesting UPnP mapping on external port %i", extPort) - router.AddPortMapping(extPort, localPort, localIP, 'TCP', 'BitMessage') - logger.info("Successfully established UPnP mapping on external port %i", extPort) - shared.extPort = extPort - except UPnPError: - recurDepth += 1 - createPortMapping() - recurDepth -= 1 + for router in routers: + createPortMappingInternal(router) def deletePortMapping(): for router in routers: - router.DeletePortMapping(shared.extPort, 'TCP') + if hasattr(router, "extPort"): + router.DeletePortMapping(router.extPort, 'TCP') From 678bdf9b8256260a9758f84a257e6e77e2af8f3f Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sat, 21 Nov 2015 00:41:08 +0100 Subject: [PATCH 161/210] Logging fix --- src/class_sendDataThread.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/class_sendDataThread.py b/src/class_sendDataThread.py index 1a839d50..32049ec3 100644 --- a/src/class_sendDataThread.py +++ b/src/class_sendDataThread.py @@ -174,7 +174,7 @@ class sendDataThread(threading.Thread): self.connectionIsOrWasFullyEstablished = True self.services, self.sslSock = data else: - logger.error('sendDataThread ID: ' + id(self) + ' ignoring command ' + command + ' because the thread is not in stream' + deststream) + logger.error('sendDataThread ID: ' + id(self) + ' ignoring command ' + command + ' because the thread is not in stream' + str(deststream)) try: self.sock.shutdown(socket.SHUT_RDWR) From 1a80db354b13e50ab5353f5534393de59dbb9ba3 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sat, 21 Nov 2015 10:14:30 +0100 Subject: [PATCH 162/210] Logging fix --- src/class_sendDataThread.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/class_sendDataThread.py b/src/class_sendDataThread.py index 32049ec3..665bbb1b 100644 --- a/src/class_sendDataThread.py +++ b/src/class_sendDataThread.py @@ -174,7 +174,7 @@ class sendDataThread(threading.Thread): self.connectionIsOrWasFullyEstablished = True self.services, self.sslSock = data else: - logger.error('sendDataThread ID: ' + id(self) + ' ignoring command ' + command + ' because the thread is not in stream' + str(deststream)) + logger.error('sendDataThread ID: ' + str(id(self)) + ' ignoring command ' + command + ' because the thread is not in stream' + str(deststream)) try: self.sock.shutdown(socket.SHUT_RDWR) From 8f652ef0e60a5bcf4d2db80e9c527667992b34ad Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sat, 21 Nov 2015 10:15:20 +0100 Subject: [PATCH 163/210] UPnP logging improvlement --- src/upnp.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/upnp.py b/src/upnp.py index b3ce48f1..ec09bc32 100644 --- a/src/upnp.py +++ b/src/upnp.py @@ -25,6 +25,7 @@ def searchRouter(): sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2) sock.settimeout(2) + logger.debug("Sending UPnP query") sock.sendto(ssdpRequest, (SSDP_ADDR, SSDP_PORT)) except: logger.exception("UPnP sock failed") @@ -160,10 +161,12 @@ class Router: return resp def DeletePortMapping(self, externalPort, protocol): + from debug import logger resp = self.soapRequest('WANIPConnection:1', 'DeletePortMapping', [ ('NewExternalPort', str(externalPort)), ('NewProtocol', protocol), ]) + logger.info("Removed UPnP mapping on external port %i", extPort) return resp def GetExternalIPAddress(self): From 8075e375b5db264461c7b0b4dd1ce5076d780ae7 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sat, 21 Nov 2015 10:17:27 +0100 Subject: [PATCH 164/210] Typo fix --- src/upnp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/upnp.py b/src/upnp.py index ec09bc32..b4648e02 100644 --- a/src/upnp.py +++ b/src/upnp.py @@ -166,7 +166,7 @@ class Router: ('NewExternalPort', str(externalPort)), ('NewProtocol', protocol), ]) - logger.info("Removed UPnP mapping on external port %i", extPort) + logger.info("Removed UPnP mapping on external port %i", externalPort) return resp def GetExternalIPAddress(self): From 24d8ffcd548e25ac0f7ab3baa874bed11ace65c7 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sat, 21 Nov 2015 11:59:44 +0100 Subject: [PATCH 165/210] Make UPnP into a thread - UPnP is now a separate thread that will continue to setup UPnP - shutdown waits for threads that shutdown correctly (Addresses Bitmessage#549) --- src/bitmessagemain.py | 9 +-- src/shared.py | 10 +-- src/upnp.py | 149 ++++++++++++++++++++++++------------------ 3 files changed, 98 insertions(+), 70 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 6078cabd..d29d0928 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -149,10 +149,6 @@ class Main: # is the application already running? If yes then exit. thisapp = singleton.singleinstance("", daemon) - if shared.safeConfigGetBoolean('bitmessagesettings','upnp'): - import upnp - upnp.createPortMapping() - # get curses flag curses = False if '-c' in sys.argv: @@ -211,6 +207,11 @@ class Main: singleListenerThread.setup(selfInitiatedConnections) singleListenerThread.daemon = True # close the main program even if there are threads left singleListenerThread.start() + + if shared.safeConfigGetBoolean('bitmessagesettings','upnp'): + import upnp + upnpThread = upnp.uPnPThread() + upnpThread.start() if daemon == False and shared.safeConfigGetBoolean('bitmessagesettings', 'daemon') == False: if curses == False: diff --git a/src/shared.py b/src/shared.py index 47f0ac14..364d7f9c 100644 --- a/src/shared.py +++ b/src/shared.py @@ -386,9 +386,6 @@ def doCleanShutdown(): 'Flushing inventory in memory out to disk. This should normally only take a second...')) flushInventory() - if safeConfigGetBoolean('bitmessagesettings','upnp'): - import upnp - upnp.deletePortMapping() # Verify that the objectProcessor has finished exiting. It should have incremented the # shutdown variable from 1 to 2. This must finish before we command the sqlThread to exit. while shutdown == 1: @@ -402,7 +399,12 @@ def doCleanShutdown(): # Wait long enough to guarantee that any running proof of work worker threads will check the # shutdown variable and exit. If the main thread closes before they do then they won't stop. - time.sleep(.25) + time.sleep(.25) + + for thread in threading.enumerate(): + if thread.name == "uPnPThread" or "outgoingSynSender" in thread.name: + logger.debug("Waiting for thread %s", thread.name) + thread.join() if safeConfigGetBoolean('bitmessagesettings','daemon'): logger.info('Clean shutdown complete.') diff --git a/src/upnp.py b/src/upnp.py index b4648e02..00d27b68 100644 --- a/src/upnp.py +++ b/src/upnp.py @@ -4,42 +4,10 @@ import httplib from random import randint import socket from struct import unpack, pack +import threading +import time import shared -routers = [] - -def searchRouter(): - from debug import logger - SSDP_ADDR = "239.255.255.250" - SSDP_PORT = 1900 - SSDP_MX = 2 - SSDP_ST = "urn:schemas-upnp-org:device:InternetGatewayDevice:1" - ssdpRequest = "M-SEARCH * HTTP/1.1\r\n" + \ - "HOST: %s:%d\r\n" % (SSDP_ADDR, SSDP_PORT) + \ - "MAN: \"ssdp:discover\"\r\n" + \ - "MX: %d\r\n" % (SSDP_MX, ) + \ - "ST: %s\r\n" % (SSDP_ST, ) + "\r\n" - routers = [] - - try: - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2) - sock.settimeout(2) - logger.debug("Sending UPnP query") - sock.sendto(ssdpRequest, (SSDP_ADDR, SSDP_PORT)) - except: - logger.exception("UPnP sock failed") - try: - while True: - resp,(ip,port) = sock.recvfrom(1000) - if resp is None: - continue - routers.append(Router(resp, ip)) - except: - logger.error("Failure running UPnP router search.", exc_info=True) - - return routers - def createRequestXML(service, action, arguments=[]): from xml.dom.minidom import Document @@ -97,6 +65,8 @@ class Router: path = "" address = None routerPath = None + extPort = None + def __init__(self, ssdpResponse, address): import urllib2 from xml.dom.minidom import parseString @@ -148,6 +118,7 @@ class Router: pass def AddPortMapping(self, externalPort, internalPort, internalClient, protocol, description, leaseDuration = 0, enabled = 1): + from debug import logger resp = self.soapRequest('WANIPConnection:1', 'AddPortMapping', [ ('NewExternalPort', str(externalPort)), ('NewProtocol', protocol), @@ -158,6 +129,7 @@ class Router: ('NewLeaseDuration', str(leaseDuration)) ]) self.extPort = externalPort + logger.info("Successfully established UPnP mapping for %s:%i on external port %i", internalClient, internalPort, externalPort) return resp def DeletePortMapping(self, externalPort, protocol): @@ -196,37 +168,90 @@ class Router: raise UPnPError(errinfo[0].childNodes[0].data) return resp -def createPortMappingInternal(router): - from debug import logger +class uPnPThread(threading.Thread): + def __init__ (self): + threading.Thread.__init__(self, name="uPnPThread") + self.localPort = shared.config.getint('bitmessagesettings', 'port') + self.extPort = None + self.routers = [] + self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self.sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2) + self.sock.settimeout(10) + self.sendSleep = 60 + + def run(self): + from debug import logger + + logger.debug("Starting UPnP thread") + lastSent = 0 + while shared.shutdown == 0: + if time.time() - lastSent > self.sendSleep and len(self.routers) == 0: + self.sendSearchRouter() + lastSent = time.time() + try: + while shared.shutdown == 0: + resp,(ip,port) = self.sock.recvfrom(1000) + if resp is None: + continue + newRouter = Router(resp, ip) + for router in self.routers: + if router.location == newRouter.location: + break + else: + logger.debug("Found UPnP router at %s", ip) + self.routers.append(newRouter) + self.createPortMapping(newRouter) + break + except socket.timeout as e: + pass + except: + logger.error("Failure running UPnP router search.", exc_info=True) + for router in self.routers: + if router.extPort is None: + self.createPortMapping(router) + for router in self.routers: + if router.extPort is not None: + self.deletePortMapping(router) + logger.debug("UPnP thread done") + + def sendSearchRouter(self): + from debug import logger + SSDP_ADDR = "239.255.255.250" + SSDP_PORT = 1900 + SSDP_MX = 2 + SSDP_ST = "urn:schemas-upnp-org:device:InternetGatewayDevice:1" + ssdpRequest = "M-SEARCH * HTTP/1.1\r\n" + \ + "HOST: %s:%d\r\n" % (SSDP_ADDR, SSDP_PORT) + \ + "MAN: \"ssdp:discover\"\r\n" + \ + "MX: %d\r\n" % (SSDP_MX, ) + \ + "ST: %s\r\n" % (SSDP_ST, ) + "\r\n" - for i in range(0, 50): try: - routerIP, = unpack('>I', socket.inet_aton(router.address)) - localIP = router.localAddress - localPort = shared.config.getint('bitmessagesettings', 'port') - if i == 0: - extPort = localPort # try same port first - else: - extPort = randint(32767, 65535) - logger.debug("Requesting UPnP mapping for %s:%i on external port %i", localIP, localPort, extPort) - router.AddPortMapping(extPort, localPort, localIP, 'TCP', 'BitMessage') - logger.info("Successfully established UPnP mapping for %s:%i on external port %i", localIP, localPort, extPort) - shared.extPort = extPort - break - except UPnPError: - logger.debug("UPnP error: ", exc_info=True) + logger.debug("Sending UPnP query") + self.sock.sendto(ssdpRequest, (SSDP_ADDR, SSDP_PORT)) + except: + logger.exception("UPnP send query failed") -def createPortMapping(): - from debug import logger - global routers + def createPortMapping(self, router): + from debug import logger + + for i in range(50): + try: + routerIP, = unpack('>I', socket.inet_aton(router.address)) + localIP = router.localAddress + if i == 0: + extPort = self.localPort # try same port first + else: + extPort = randint(32767, 65535) + logger.debug("Requesting UPnP mapping for %s:%i on external port %i", localIP, self.localPort, extPort) + router.AddPortMapping(extPort, self.localPort, localIP, 'TCP', 'BitMessage') + shared.extPort = extPort + break + except UPnPError: + logger.debug("UPnP error: ", exc_info=True) + + def deletePortMapping(self, router): + router.DeletePortMapping(router.extPort, 'TCP') - routers = searchRouter() - logger.debug("Found %i UPnP routers", len(routers)) - for router in routers: - createPortMappingInternal(router) -def deletePortMapping(): - for router in routers: - if hasattr(router, "extPort"): - router.DeletePortMapping(router.extPort, 'TCP') From 84b47877b143e73cd5d63e128ec272e710507c3a Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sun, 22 Nov 2015 01:07:25 +0100 Subject: [PATCH 166/210] Window resize on minimize bugfix Fixes Bitmessage#243 --- src/bitmessageqt/__init__.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 5eb7632e..dc9fe339 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -798,10 +798,6 @@ class MyForm(settingsmixin.SMainWindow): if not self.actionShow.isChecked(): self.hide() else: - if sys.platform[0:3] == 'win': - self.setWindowFlags(Qt.Window) - # else: - # self.showMaximized() self.show() self.setWindowState( self.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive) @@ -1637,9 +1633,7 @@ class MyForm(settingsmixin.SMainWindow): if event.type() == QtCore.QEvent.WindowStateChange: if self.windowState() & QtCore.Qt.WindowMinimized: if shared.config.getboolean('bitmessagesettings', 'minimizetotray') and not 'darwin' in sys.platform: - self.appIndicatorHide() - if 'win32' in sys.platform or 'win64' in sys.platform: - self.setWindowFlags(Qt.ToolTip) + QTimer.singleShot(0, self.appIndicatorHide) elif event.oldState() & QtCore.Qt.WindowMinimized: # The window state has just been changed to # Normal/Maximised/FullScreen From 3dcd8be9937a1ff34d9b3784ebd06593bcbde102 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sun, 22 Nov 2015 01:10:02 +0100 Subject: [PATCH 167/210] UPnP fixes --- src/upnp.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/upnp.py b/src/upnp.py index 00d27b68..0f34e7fd 100644 --- a/src/upnp.py +++ b/src/upnp.py @@ -209,6 +209,7 @@ class uPnPThread(threading.Thread): for router in self.routers: if router.extPort is None: self.createPortMapping(router) + self.sock.close() for router in self.routers: if router.extPort is not None: self.deletePortMapping(router) From 34dd5b3793112c562cda4d16e1740d5ec146ef94 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sun, 22 Nov 2015 13:02:06 +0100 Subject: [PATCH 168/210] UPnP GUI Settings GUI now contains a checkbox for UPnP and auto starts/stops the thread when changed. Default UPnP socket timeout decreased for faster thread starting/stopping --- src/bitmessageqt/__init__.py | 8 ++++++++ src/bitmessageqt/settings.py | 15 +++++++++++---- src/upnp.py | 6 +++--- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index dc9fe339..11d46c66 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2571,6 +2571,12 @@ class MyForm(settingsmixin.SMainWindow): "MainWindow", "You must restart Bitmessage for the port number change to take effect.")) shared.config.set('bitmessagesettings', 'port', str( self.settingsDialogInstance.ui.lineEditTCPPort.text())) + if self.settingsDialogInstance.ui.checkBoxUPnP.isChecked() != shared.safeConfigGetBoolean('bitmessagesettings', 'upnp'): + shared.config.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 shared.config.get('bitmessagesettings', 'socksproxytype') == 'none' and self.settingsDialogInstance.ui.comboBoxProxyType.currentText()[0:5] == 'SOCKS': @@ -4115,6 +4121,8 @@ class settingsDialog(QtGui.QDialog): # On the Network settings tab: self.ui.lineEditTCPPort.setText(str( shared.config.get('bitmessagesettings', 'port'))) + self.ui.checkBoxUPnP.setChecked( + shared.safeConfigGetBoolean('bitmessagesettings', 'upnp')) self.ui.checkBoxAuthentication.setChecked(shared.config.getboolean( 'bitmessagesettings', 'socksauthentication')) self.ui.checkBoxSocksListen.setChecked(shared.config.getboolean( diff --git a/src/bitmessageqt/settings.py b/src/bitmessageqt/settings.py index b9131315..4a1dfc7c 100644 --- a/src/bitmessageqt/settings.py +++ b/src/bitmessageqt/settings.py @@ -120,15 +120,21 @@ class Ui_settingsDialog(object): self.groupBox1.setObjectName(_fromUtf8("groupBox1")) self.gridLayout_3 = QtGui.QGridLayout(self.groupBox1) self.gridLayout_3.setObjectName(_fromUtf8("gridLayout_3")) - spacerItem = QtGui.QSpacerItem(125, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) - self.gridLayout_3.addItem(spacerItem, 0, 0, 1, 1) + #spacerItem = QtGui.QSpacerItem(125, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) + #self.gridLayout_3.addItem(spacerItem, 0, 0, 1, 1) self.label = QtGui.QLabel(self.groupBox1) self.label.setObjectName(_fromUtf8("label")) - self.gridLayout_3.addWidget(self.label, 0, 1, 1, 1) + 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, 2, 1, 1) + 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")) @@ -449,6 +455,7 @@ class Ui_settingsDialog(object): 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)) diff --git a/src/upnp.py b/src/upnp.py index 0f34e7fd..3d046973 100644 --- a/src/upnp.py +++ b/src/upnp.py @@ -176,7 +176,7 @@ class uPnPThread(threading.Thread): self.routers = [] self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2) - self.sock.settimeout(10) + self.sock.settimeout(2) self.sendSleep = 60 def run(self): @@ -184,12 +184,12 @@ class uPnPThread(threading.Thread): logger.debug("Starting UPnP thread") lastSent = 0 - while shared.shutdown == 0: + while shared.shutdown == 0 and shared.safeConfigGetBoolean('bitmessagesettings', 'upnp'): if time.time() - lastSent > self.sendSleep and len(self.routers) == 0: self.sendSearchRouter() lastSent = time.time() try: - while shared.shutdown == 0: + while shared.shutdown == 0 and shared.safeConfigGetBoolean('bitmessagesettings', 'upnp'): resp,(ip,port) = self.sock.recvfrom(1000) if resp is None: continue From 9065910a38341c6e006a3c3046e9699c8593ca26 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sun, 22 Nov 2015 16:18:59 +0100 Subject: [PATCH 169/210] Threads close better - UPnP and outgoingSynSender threads close slightly better. - extPort initialisation was missing --- src/class_outgoingSynSender.py | 46 +++++++++++++++++++++------------- src/helper_threading.py | 10 ++++++++ src/shared.py | 11 +++++++- src/upnp.py | 5 +++- 4 files changed, 53 insertions(+), 19 deletions(-) create mode 100644 src/helper_threading.py diff --git a/src/class_outgoingSynSender.py b/src/class_outgoingSynSender.py index 732f6c9f..d011ae94 100644 --- a/src/class_outgoingSynSender.py +++ b/src/class_outgoingSynSender.py @@ -2,6 +2,7 @@ import threading import time import random import shared +import select import socks import socket import sys @@ -9,14 +10,16 @@ import tr from class_sendDataThread import * from class_receiveDataThread import * +from helper_threading import * # For each stream to which we connect, several outgoingSynSender threads # will exist and will collectively create 8 connections with peers. -class outgoingSynSender(threading.Thread): +class outgoingSynSender(threading.Thread, StoppableThread): def __init__(self): threading.Thread.__init__(self, name="outgoingSynSender") + self.initStop() def setup(self, streamNumber, selfInitiatedConnections): self.streamNumber = streamNumber @@ -35,15 +38,22 @@ class outgoingSynSender(threading.Thread): shared.knownNodesLock.release() return peer + + def stopThread(self): + super(outgoingSynSender, self).stopThread() + try: + self.sock.shutdown(socket.SHUT_RDWR) + except: + pass def run(self): - while shared.safeConfigGetBoolean('bitmessagesettings', 'dontconnect'): - time.sleep(2) - while shared.safeConfigGetBoolean('bitmessagesettings', 'sendoutgoingconnections'): + while shared.safeConfigGetBoolean('bitmessagesettings', 'dontconnect') and not self._stopped: + self.stop.wait(2) + while shared.safeConfigGetBoolean('bitmessagesettings', 'sendoutgoingconnections') and not self._stopped: self.name = "outgoingSynSender" maximumConnections = 1 if shared.trustedPeer else 8 # maximum number of outgoing connections = 8 while len(self.selfInitiatedConnections[self.streamNumber]) >= maximumConnections: - time.sleep(10) + self.stop.wait(10) if shared.shutdown: break random.seed() @@ -54,7 +64,9 @@ class outgoingSynSender(threading.Thread): # print 'choosing new sample' random.seed() peer = self._getPeer() - time.sleep(1) + self.stop.wait(1) + if shared.shutdown: + break # Clear out the shared.alreadyAttemptedConnectionsList every half # hour so that this program will again attempt a connection # to any nodes, even ones it has already tried. @@ -71,7 +83,7 @@ class outgoingSynSender(threading.Thread): else: address_family = socket.AF_INET6 try: - sock = socks.socksocket(address_family, socket.SOCK_STREAM) + self.sock = socks.socksocket(address_family, socket.SOCK_STREAM) except: """ The line can fail on Windows systems which aren't @@ -92,8 +104,8 @@ class outgoingSynSender(threading.Thread): continue # This option apparently avoids the TIME_WAIT state so that we # can rebind faster - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - sock.settimeout(20) + self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + self.sock.settimeout(20) if shared.config.get('bitmessagesettings', 'socksproxytype') == 'none' and shared.verbose >= 2: logger.debug('Trying an outgoing connection to ' + str(peer)) @@ -113,10 +125,10 @@ class outgoingSynSender(threading.Thread): 'bitmessagesettings', 'socksusername') sockspassword = shared.config.get( 'bitmessagesettings', 'sockspassword') - sock.setproxy( + self.sock.setproxy( proxytype, sockshostname, socksport, rdns, socksusername, sockspassword) else: - sock.setproxy( + self.sock.setproxy( proxytype, sockshostname, socksport, rdns) elif shared.config.get('bitmessagesettings', 'socksproxytype') == 'SOCKS5': if shared.verbose >= 2: @@ -133,19 +145,19 @@ class outgoingSynSender(threading.Thread): 'bitmessagesettings', 'socksusername') sockspassword = shared.config.get( 'bitmessagesettings', 'sockspassword') - sock.setproxy( + self.sock.setproxy( proxytype, sockshostname, socksport, rdns, socksusername, sockspassword) else: - sock.setproxy( + self.sock.setproxy( proxytype, sockshostname, socksport, rdns) try: - sock.connect((peer.host, peer.port)) + self.sock.connect((peer.host, peer.port)) rd = receiveDataThread() rd.daemon = True # close the main program even if there are threads left someObjectsOfWhichThisRemoteNodeIsAlreadyAware = {} # This is not necessairly a complete list; we clear it from time to time to save memory. sendDataThreadQueue = Queue.Queue() # Used to submit information to the send data thread for this connection. - rd.setup(sock, + rd.setup(self.sock, peer.host, peer.port, self.streamNumber, @@ -157,7 +169,7 @@ class outgoingSynSender(threading.Thread): sd = sendDataThread(sendDataThreadQueue) - sd.setup(sock, peer.host, peer.port, self.streamNumber, + sd.setup(self.sock, peer.host, peer.port, self.streamNumber, someObjectsOfWhichThisRemoteNodeIsAlreadyAware) sd.start() sd.sendVersionMessage() @@ -221,4 +233,4 @@ class outgoingSynSender(threading.Thread): except Exception as err: import traceback logger.exception('An exception has occurred in the outgoingSynSender thread that was not caught by other exception types:') - time.sleep(0.1) + self.stop.wait(0.1) diff --git a/src/helper_threading.py b/src/helper_threading.py new file mode 100644 index 00000000..599d297d --- /dev/null +++ b/src/helper_threading.py @@ -0,0 +1,10 @@ +import threading + +class StoppableThread(object): + def initStop(self): + self.stop = threading.Event() + self._stopped = False + + def stopThread(self): + self._stopped = True + self.stop.set() \ No newline at end of file diff --git a/src/shared.py b/src/shared.py index 364d7f9c..75d7eb6c 100644 --- a/src/shared.py +++ b/src/shared.py @@ -32,6 +32,7 @@ import highlevelcrypto import shared #import helper_startup from helper_sql import * +from helper_threading import * config = ConfigParser.SafeConfigParser() @@ -116,6 +117,9 @@ frozen = getattr(sys,'frozen', None) # security. trustedPeer = None +# For UPnP +extPort = None + #Compiled struct for packing/unpacking headers #New code should use CreatePacket instead of Header.pack Header = Struct('!L12sL4s') @@ -160,7 +164,7 @@ def assembleVersionMessage(remoteHost, remotePort, myStreamNumber): payload += pack('>q', 1) # bitflags of the services I offer. payload += '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF' + pack( '>L', 2130706433) # = 127.0.0.1. This will be ignored by the remote host. The actual remote connected IP will be used. - if safeConfigGetBoolean('bitmessagesettings', 'upnp'): + if safeConfigGetBoolean('bitmessagesettings', 'upnp' and extPort): payload += pack('>H', extPort) else: payload += pack('>H', shared.config.getint('bitmessagesettings', 'port')) @@ -401,6 +405,11 @@ def doCleanShutdown(): # shutdown variable and exit. If the main thread closes before they do then they won't stop. time.sleep(.25) + from class_outgoingSynSender import outgoingSynSender + for thread in threading.enumerate(): + if thread.name == "uPnPThread" or "outgoingSynSender" in thread.name: + if thread.isAlive() and isinstance(thread, StoppableThread): + thread.stopThread() for thread in threading.enumerate(): if thread.name == "uPnPThread" or "outgoingSynSender" in thread.name: logger.debug("Waiting for thread %s", thread.name) diff --git a/src/upnp.py b/src/upnp.py index 3d046973..6aef00a6 100644 --- a/src/upnp.py +++ b/src/upnp.py @@ -6,6 +6,7 @@ import socket from struct import unpack, pack import threading import time +from helper_threading import * import shared def createRequestXML(service, action, arguments=[]): @@ -168,7 +169,7 @@ class Router: raise UPnPError(errinfo[0].childNodes[0].data) return resp -class uPnPThread(threading.Thread): +class uPnPThread(threading.Thread, StoppableThread): def __init__ (self): threading.Thread.__init__(self, name="uPnPThread") self.localPort = shared.config.getint('bitmessagesettings', 'port') @@ -178,6 +179,7 @@ class uPnPThread(threading.Thread): self.sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2) self.sock.settimeout(2) self.sendSleep = 60 + self.initStop() def run(self): from debug import logger @@ -213,6 +215,7 @@ class uPnPThread(threading.Thread): for router in self.routers: if router.extPort is not None: self.deletePortMapping(router) + shared.extPort = None logger.debug("UPnP thread done") def sendSearchRouter(self): From 97586c50b77f218d68a1741fe501767de9f864da Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sun, 22 Nov 2015 16:42:34 +0100 Subject: [PATCH 170/210] outgoingSynSender thread reaping disabled It was causing delays on shutdown, worse on linux/osx --- src/shared.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/shared.py b/src/shared.py index 75d7eb6c..49cdbae3 100644 --- a/src/shared.py +++ b/src/shared.py @@ -411,7 +411,8 @@ def doCleanShutdown(): if thread.isAlive() and isinstance(thread, StoppableThread): thread.stopThread() for thread in threading.enumerate(): - if thread.name == "uPnPThread" or "outgoingSynSender" in thread.name: + if thread.name == "uPnPThread": + #or "outgoingSynSender" in thread.name: logger.debug("Waiting for thread %s", thread.name) thread.join() From 3e723969a7964d17490ea0b7eec12278c19c0c89 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sun, 22 Nov 2015 16:43:53 +0100 Subject: [PATCH 171/210] SSL fixes Handle old Python compatibility better. --- src/class_receiveDataThread.py | 9 ++++++--- src/class_sendDataThread.py | 7 +++++-- src/shared.py | 13 ++++++++++--- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/class_receiveDataThread.py b/src/class_receiveDataThread.py index c227648a..c745569e 100644 --- a/src/class_receiveDataThread.py +++ b/src/class_receiveDataThread.py @@ -81,7 +81,9 @@ class receiveDataThread(threading.Thread): shared.numberOfBytesReceivedLastSecond = 0 dataLen = len(self.data) try: - if (self.services & shared.NODE_SSL == shared.NODE_SSL) and self.connectionIsOrWasFullyEstablished: + if ((self.services & shared.NODE_SSL == shared.NODE_SSL) and + self.connectionIsOrWasFullyEstablished and + shared.haveSSL(not self.initiatedConnection)): dataRecv = self.sslSock.recv(1024) else: dataRecv = self.sock.recv(1024) @@ -251,7 +253,8 @@ class receiveDataThread(threading.Thread): self.connectionIsOrWasFullyEstablished = True self.sslSock = self.sock - if (self.services & shared.NODE_SSL == shared.NODE_SSL and (self.initiatedConnection or sys.version_info >= (2, 7, 9))): + if (self.services & shared.NODE_SSL == shared.NODE_SSL and + shared.haveSSL(not self.initiatedConnection)): self.sslSock = ssl.wrap_socket(self.sock, keyfile = os.path.join(shared.codePath(), 'sslkeys', 'key.pem'), certfile = os.path.join(shared.codePath(), 'sslkeys', 'cert.pem'), server_side = not self.initiatedConnection, ssl_version=ssl.PROTOCOL_TLSv1, do_handshake_on_connect=False, ciphers='AECDH-AES256-SHA') if hasattr(self.sslSock, "context"): self.sslSock.context.set_ecdh_curve("secp256k1") @@ -267,7 +270,7 @@ class receiveDataThread(threading.Thread): except: break # Command the corresponding sendDataThread to set its own connectionIsOrWasFullyEstablished variable to True also - self.sendDataThreadQueue.put((0, 'connectionIsOrWasFullyEstablished', (self.services, self.sslSock))) + self.sendDataThreadQueue.put((0, 'connectionIsOrWasFullyEstablished', (self.services, self.sslSock, self.initiatedConnection))) if not self.initiatedConnection: shared.clientHasReceivedIncomingConnections = True diff --git a/src/class_sendDataThread.py b/src/class_sendDataThread.py index 665bbb1b..869f7425 100644 --- a/src/class_sendDataThread.py +++ b/src/class_sendDataThread.py @@ -39,6 +39,7 @@ class sendDataThread(threading.Thread): self.name = "sendData-" + self.peer.host self.streamNumber = streamNumber self.services = 0 + self.initiatedConnection = False self.remoteProtocolVersion = - \ 1 # This must be set using setRemoteProtocolVersion command which is sent through the self.sendDataThreadQueue queue. self.lastTimeISentData = int( @@ -82,7 +83,9 @@ class sendDataThread(threading.Thread): uploadRateLimitBytes = 999999999 # float("inf") doesn't work else: uploadRateLimitBytes = shared.config.getint('bitmessagesettings', 'maxuploadrate') * 1000 - if self.services & shared.NODE_SSL == shared.NODE_SSL and self.connectionIsOrWasFullyEstablished: + if ((self.services & shared.NODE_SSL == shared.NODE_SSL) and + self.connectionIsOrWasFullyEstablished and + shared.haveSSL(not self.initiatedConnection)): amountSent = self.sslSock.send(data[:1000]) else: amountSent = self.sock.send(data[:1000]) @@ -172,7 +175,7 @@ class sendDataThread(threading.Thread): break elif command == 'connectionIsOrWasFullyEstablished': self.connectionIsOrWasFullyEstablished = True - self.services, self.sslSock = data + self.services, self.sslSock, self.initiatedConnection = data else: logger.error('sendDataThread ID: ' + str(id(self)) + ' ignoring command ' + command + ' because the thread is not in stream' + str(deststream)) diff --git a/src/shared.py b/src/shared.py index 49cdbae3..8eb56e0f 100644 --- a/src/shared.py +++ b/src/shared.py @@ -149,11 +149,18 @@ def encodeHost(host): else: return socket.inet_pton(socket.AF_INET6, host) -def assembleVersionMessage(remoteHost, remotePort, myStreamNumber): +def haveSSL(server = False): + # python < 2.7.9's ssl library does not support ECDSA server due to missing initialisation of available curves, but client works ok + if server == False: + return True + elif sys.version_info >= (2,7,9): + return True + return False + +def assembleVersionMessage(remoteHost, remotePort, myStreamNumber, server = False): payload = '' payload += pack('>L', 3) # protocol version. - payload += pack('>q', NODE_NETWORK|(NODE_SSL if sys.version_info >= (2, 7, 9) else 0)) # bitflags of the services I offer. - # python < 2.7.9's ssl library does not support ECDSA server due to missing initialisation of available curves, but client works ok + payload += pack('>q', NODE_NETWORK|(NODE_SSL if haveSSL(server) else 0)) # bitflags of the services I offer. payload += pack('>q', int(time.time())) payload += pack( From fd902fe739fdb35a45959aaf7b965f1c51e4214c Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sun, 22 Nov 2015 17:04:51 +0100 Subject: [PATCH 172/210] SSL fixes --- src/class_receiveDataThread.py | 2 +- src/class_sendDataThread.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/class_receiveDataThread.py b/src/class_receiveDataThread.py index c745569e..a8996260 100644 --- a/src/class_receiveDataThread.py +++ b/src/class_receiveDataThread.py @@ -755,7 +755,7 @@ class receiveDataThread(threading.Thread): def sendversion(self): logger.debug('Sending version message') self.sendDataThreadQueue.put((0, 'sendRawData', shared.assembleVersionMessage( - self.peer.host, self.peer.port, self.streamNumber))) + self.peer.host, self.peer.port, self.streamNumber, not self.initiatedConnection))) # Sends a verack message diff --git a/src/class_sendDataThread.py b/src/class_sendDataThread.py index 869f7425..99c51f61 100644 --- a/src/class_sendDataThread.py +++ b/src/class_sendDataThread.py @@ -50,7 +50,7 @@ class sendDataThread(threading.Thread): def sendVersionMessage(self): datatosend = shared.assembleVersionMessage( - self.peer.host, self.peer.port, self.streamNumber) # the IP and port of the remote host, and my streamNumber. + self.peer.host, self.peer.port, self.streamNumber, not self.initiatedConnection) # the IP and port of the remote host, and my streamNumber. logger.debug('Sending version packet: ' + repr(datatosend)) From 2104b24fc39c04f508d66f69f0b3a105860ebc2e Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sun, 22 Nov 2015 22:44:58 +0100 Subject: [PATCH 173/210] SSL fixes --- src/class_receiveDataThread.py | 5 +++-- src/class_sendDataThread.py | 6 +++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/class_receiveDataThread.py b/src/class_receiveDataThread.py index a8996260..58a54346 100644 --- a/src/class_receiveDataThread.py +++ b/src/class_receiveDataThread.py @@ -253,8 +253,9 @@ class receiveDataThread(threading.Thread): self.connectionIsOrWasFullyEstablished = True self.sslSock = self.sock - if (self.services & shared.NODE_SSL == shared.NODE_SSL and + if ((self.services & shared.NODE_SSL == shared.NODE_SSL) and shared.haveSSL(not self.initiatedConnection)): + logger.debug("Initialising TLS") self.sslSock = ssl.wrap_socket(self.sock, keyfile = os.path.join(shared.codePath(), 'sslkeys', 'key.pem'), certfile = os.path.join(shared.codePath(), 'sslkeys', 'cert.pem'), server_side = not self.initiatedConnection, ssl_version=ssl.PROTOCOL_TLSv1, do_handshake_on_connect=False, ciphers='AECDH-AES256-SHA') if hasattr(self.sslSock, "context"): self.sslSock.context.set_ecdh_curve("secp256k1") @@ -270,7 +271,7 @@ class receiveDataThread(threading.Thread): except: break # Command the corresponding sendDataThread to set its own connectionIsOrWasFullyEstablished variable to True also - self.sendDataThreadQueue.put((0, 'connectionIsOrWasFullyEstablished', (self.services, self.sslSock, self.initiatedConnection))) + self.sendDataThreadQueue.put((0, 'connectionIsOrWasFullyEstablished', (self.services, self.sslSock))) if not self.initiatedConnection: shared.clientHasReceivedIncomingConnections = True diff --git a/src/class_sendDataThread.py b/src/class_sendDataThread.py index 99c51f61..f151f55b 100644 --- a/src/class_sendDataThread.py +++ b/src/class_sendDataThread.py @@ -45,6 +45,10 @@ class sendDataThread(threading.Thread): self.lastTimeISentData = int( time.time()) # If this value increases beyond five minutes ago, we'll send a pong message to keep the connection alive. self.someObjectsOfWhichThisRemoteNodeIsAlreadyAware = someObjectsOfWhichThisRemoteNodeIsAlreadyAware + if self.streamNumber == -1: # This was an incoming connection. + self.initiatedConnection = False + else: + self.initiatedConnection = True logger.debug('The streamNumber of this sendDataThread (ID: ' + str(id(self)) + ') at setup() is' + str(self.streamNumber)) @@ -175,7 +179,7 @@ class sendDataThread(threading.Thread): break elif command == 'connectionIsOrWasFullyEstablished': self.connectionIsOrWasFullyEstablished = True - self.services, self.sslSock, self.initiatedConnection = data + self.services, self.sslSock = data else: logger.error('sendDataThread ID: ' + str(id(self)) + ' ignoring command ' + command + ' because the thread is not in stream' + str(deststream)) From 4d03b7e98e76338f700b62c4d4988cf70c176cd6 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sun, 22 Nov 2015 23:13:26 +0100 Subject: [PATCH 174/210] UPnP cleaner shutdown --- src/upnp.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/upnp.py b/src/upnp.py index 6aef00a6..47706637 100644 --- a/src/upnp.py +++ b/src/upnp.py @@ -114,6 +114,7 @@ class Router: self.localAddress = s.getsockname()[0] logger.debug("Local IP: %s", self.localAddress) try: + s.shutdown(socket.SHUT.RDWR) s.close() except: pass @@ -211,6 +212,7 @@ class uPnPThread(threading.Thread, StoppableThread): for router in self.routers: if router.extPort is None: self.createPortMapping(router) + self.sock.shutdown(socket.SHUT.RDWR) self.sock.close() for router in self.routers: if router.extPort is not None: From 2ab4c3d85c3cb8ce6ff6ba5e0f3284e308023b0b Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sun, 22 Nov 2015 23:41:03 +0100 Subject: [PATCH 175/210] UPnP notifications UPnP will notify you through window status bar. --- src/upnp.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/upnp.py b/src/upnp.py index 47706637..2ad1406b 100644 --- a/src/upnp.py +++ b/src/upnp.py @@ -8,6 +8,7 @@ import threading import time from helper_threading import * import shared +import tr def createRequestXML(service, action, arguments=[]): from xml.dom.minidom import Document @@ -204,6 +205,7 @@ class uPnPThread(threading.Thread, StoppableThread): logger.debug("Found UPnP router at %s", ip) self.routers.append(newRouter) self.createPortMapping(newRouter) + shared.UISignalQueue.put(('updateStatusBar', tr.translateText("MainWindow",'UPnP port mapping established'))) break except socket.timeout as e: pass @@ -214,10 +216,14 @@ class uPnPThread(threading.Thread, StoppableThread): self.createPortMapping(router) self.sock.shutdown(socket.SHUT.RDWR) self.sock.close() + deleted = False for router in self.routers: if router.extPort is not None: + deleted = True self.deletePortMapping(router) shared.extPort = None + if deleted: + shared.UISignalQueue.put(('updateStatusBar', tr.translateText("MainWindow",'UPnP port mapping removed'))) logger.debug("UPnP thread done") def sendSearchRouter(self): From 0c2dfe22bbafecb16c7e8d089ab91b0e9840dfd9 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Mon, 23 Nov 2015 00:03:49 +0100 Subject: [PATCH 176/210] Suppress error on releasing unlocked lock --- src/class_outgoingSynSender.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/class_outgoingSynSender.py b/src/class_outgoingSynSender.py index d011ae94..0f201747 100644 --- a/src/class_outgoingSynSender.py +++ b/src/class_outgoingSynSender.py @@ -76,7 +76,10 @@ class outgoingSynSender(threading.Thread, StoppableThread): time.time()) shared.alreadyAttemptedConnectionsListLock.acquire() shared.alreadyAttemptedConnectionsList[peer] = 0 - shared.alreadyAttemptedConnectionsListLock.release() + try: + shared.alreadyAttemptedConnectionsListLock.release() + except ThreadError as e: + pass self.name = "outgoingSynSender-" + peer.host if peer.host.find(':') == -1: address_family = socket.AF_INET From 23dfe0a03ed33d168facc39c3366dd686ae827f8 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Mon, 23 Nov 2015 00:05:49 +0100 Subject: [PATCH 177/210] Typo --- src/class_outgoingSynSender.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/class_outgoingSynSender.py b/src/class_outgoingSynSender.py index 0f201747..9b7600cd 100644 --- a/src/class_outgoingSynSender.py +++ b/src/class_outgoingSynSender.py @@ -78,7 +78,7 @@ class outgoingSynSender(threading.Thread, StoppableThread): shared.alreadyAttemptedConnectionsList[peer] = 0 try: shared.alreadyAttemptedConnectionsListLock.release() - except ThreadError as e: + except threading.ThreadError as e: pass self.name = "outgoingSynSender-" + peer.host if peer.host.find(':') == -1: From c48b2f3c0844858dbaf2d0e61b0746f26f3674da Mon Sep 17 00:00:00 2001 From: mailchuck Date: Mon, 23 Nov 2015 00:08:26 +0100 Subject: [PATCH 178/210] Typo --- src/upnp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/upnp.py b/src/upnp.py index 2ad1406b..fc93fd63 100644 --- a/src/upnp.py +++ b/src/upnp.py @@ -115,7 +115,7 @@ class Router: self.localAddress = s.getsockname()[0] logger.debug("Local IP: %s", self.localAddress) try: - s.shutdown(socket.SHUT.RDWR) + s.shutdown(socket.SHUT_RDWR) s.close() except: pass From 15d435a8e7a617e240f3159c0ce5f0391480d7c3 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Mon, 23 Nov 2015 00:26:18 +0100 Subject: [PATCH 179/210] Typo --- src/upnp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/upnp.py b/src/upnp.py index fc93fd63..6449b7fd 100644 --- a/src/upnp.py +++ b/src/upnp.py @@ -214,7 +214,7 @@ class uPnPThread(threading.Thread, StoppableThread): for router in self.routers: if router.extPort is None: self.createPortMapping(router) - self.sock.shutdown(socket.SHUT.RDWR) + self.sock.shutdown(socket.SHUT_RDWR) self.sock.close() deleted = False for router in self.routers: From d86fbfffc3f646b1ebd4a37ca052cb63437509f0 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Mon, 23 Nov 2015 01:35:11 +0100 Subject: [PATCH 180/210] Exception handling --- src/upnp.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/upnp.py b/src/upnp.py index 6449b7fd..e14d1100 100644 --- a/src/upnp.py +++ b/src/upnp.py @@ -214,8 +214,14 @@ class uPnPThread(threading.Thread, StoppableThread): for router in self.routers: if router.extPort is None: self.createPortMapping(router) - self.sock.shutdown(socket.SHUT_RDWR) - self.sock.close() + try: + self.sock.shutdown(socket.SHUT_RDWR) + except: + pass + try: + self.sock.close() + except: + pass deleted = False for router in self.routers: if router.extPort is not None: From 8f7bd6f4a0c383e7cfc8d6cf3e292f9d267c3400 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Mon, 23 Nov 2015 06:58:25 +0100 Subject: [PATCH 181/210] Typo --- src/shared.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared.py b/src/shared.py index 8eb56e0f..350c4a2f 100644 --- a/src/shared.py +++ b/src/shared.py @@ -171,7 +171,7 @@ def assembleVersionMessage(remoteHost, remotePort, myStreamNumber, server = Fals payload += pack('>q', 1) # bitflags of the services I offer. payload += '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF' + pack( '>L', 2130706433) # = 127.0.0.1. This will be ignored by the remote host. The actual remote connected IP will be used. - if safeConfigGetBoolean('bitmessagesettings', 'upnp' and extPort): + if safeConfigGetBoolean('bitmessagesettings', 'upnp') and extPort: payload += pack('>H', extPort) else: payload += pack('>H', shared.config.getint('bitmessagesettings', 'port')) From 8931f69fb7826cfed31466bc1e615993c03c86c4 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Mon, 23 Nov 2015 07:19:59 +0100 Subject: [PATCH 182/210] Version bump --- src/shared.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared.py b/src/shared.py index 350c4a2f..96eefa29 100644 --- a/src/shared.py +++ b/src/shared.py @@ -1,6 +1,6 @@ from __future__ import division -softwareVersion = '0.5.3' +softwareVersion = '0.5.4' verbose = 1 maximumAgeOfAnObjectThatIAmWillingToAccept = 216000 # This is obsolete with the change to protocol v3 but the singleCleaner thread still hasn't been updated so we need this a little longer. lengthOfTimeToHoldOnToAllPubkeys = 2419200 # Equals 4 weeks. You could make this longer if you want but making it shorter would not be advisable because there is a very small possibility that it could keep you from obtaining a needed pubkey for a period of time. From 9f4cdaf82bcf291cc12ffeb1fa5225ec51cd598d Mon Sep 17 00:00:00 2001 From: mailchuck Date: Tue, 24 Nov 2015 01:55:17 +0100 Subject: [PATCH 183/210] Cleaner shutdown Addresses Bitmessage#549 --- src/api.py | 9 ++++++++- src/bitmessagemain.py | 23 +++++++++++++++++------ src/class_addressGenerator.py | 19 +++++++++++++++---- src/class_singleCleaner.py | 10 ++++++---- src/class_singleListener.py | 34 +++++++++++++++++++++++----------- src/class_singleWorker.py | 17 ++++++++++++++--- src/shared.py | 8 +++----- 7 files changed, 86 insertions(+), 34 deletions(-) diff --git a/src/api.py b/src/api.py index 20568ffc..7c355941 100644 --- a/src/api.py +++ b/src/api.py @@ -12,7 +12,7 @@ if __name__ == "__main__": import sys sys.exit(0) -from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler +from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler, SimpleXMLRPCServer import json import shared @@ -43,6 +43,13 @@ class APIError(Exception): def __str__(self): return "API Error %04i: %s" % (self.error_number, self.error_message) + +class StoppableXMLRPCServer(SimpleXMLRPCServer): + def serve_forever(self): + while shared.shutdown == 0: + self.handle_request() + + # This is one of several classes that constitute the API # This class was written by Vaibhav Bhatia. Modified by Jonathan Warren (Atheros). # http://code.activestate.com/recipes/501148-xmlrpc-serverclient-which-does-cookie-handling-and/ diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index d29d0928..2767e465 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -23,8 +23,7 @@ import sys from subprocess import call import time -from SimpleXMLRPCServer import SimpleXMLRPCServer -from api import MySimpleXMLRPCRequestHandler +from api import MySimpleXMLRPCRequestHandler, StoppableXMLRPCServer from helper_startup import isOurOperatingSystemLimitedToHavingVeryFewHalfOpenConnections import shared @@ -44,6 +43,7 @@ from debug import logger # Helper Functions import helper_bootstrap import helper_generic +from helper_threading import * # singleton lock instance thisapp = None @@ -119,13 +119,24 @@ def _fixWinsock(): socket.IPV6_V6ONLY = 27 # This thread, of which there is only one, runs the API. -class singleAPI(threading.Thread): - +class singleAPI(threading.Thread, StoppableThread): def __init__(self): - threading.Thread.__init__(self) + threading.Thread.__init__(self, name="singleAPI") + self.initStop() + + def stopThread(self): + super(singleAPI, self).stopThread() + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + try: + s.connect((shared.config.get('bitmessagesettings', 'apiinterface'), shared.config.getint( + 'bitmessagesettings', 'apiport'))) + s.shutdown(socket.SHUT_RDWR) + s.close() + except: + pass def run(self): - se = SimpleXMLRPCServer((shared.config.get('bitmessagesettings', 'apiinterface'), shared.config.getint( + se = StoppableXMLRPCServer((shared.config.get('bitmessagesettings', 'apiinterface'), shared.config.getint( 'bitmessagesettings', 'apiport')), MySimpleXMLRPCRequestHandler, True, True) se.register_introspection_functions() se.serve_forever() diff --git a/src/class_addressGenerator.py b/src/class_addressGenerator.py index af25b210..5d1598c4 100644 --- a/src/class_addressGenerator.py +++ b/src/class_addressGenerator.py @@ -8,17 +8,26 @@ import hashlib import highlevelcrypto from addresses import * from debug import logger +from helper_threading import * from pyelliptic import arithmetic import tr -class addressGenerator(threading.Thread): +class addressGenerator(threading.Thread, StoppableThread): def __init__(self): # QThread.__init__(self, parent) - threading.Thread.__init__(self) + threading.Thread.__init__(self, name="addressGenerator") + self.initStop() + + def stopThread(self): + try: + shared.addressGeneratorQueue.put(("stopThread", "data")) + except: + pass + super(addressGenerator, self).stopThread() def run(self): - while True: + while shared.shutdown == 0: queueValue = shared.addressGeneratorQueue.get() nonceTrialsPerByte = 0 payloadLengthExtraBytes = 0 @@ -54,6 +63,8 @@ class addressGenerator(threading.Thread): numberOfNullBytesDemandedOnFrontOfRipeHash = 2 else: numberOfNullBytesDemandedOnFrontOfRipeHash = 1 # the default + elif queueValue[0] == 'stopThread': + break else: sys.stderr.write( 'Programming error: A structure with the wrong number of values was passed into the addressGeneratorQueue. Here is the queueValue: %s\n' % repr(queueValue)) @@ -278,4 +289,4 @@ class addressGenerator(threading.Thread): else: raise Exception( "Error in the addressGenerator thread. Thread was given a command it could not understand: " + command) - + shared.apiAddressGeneratorQueue.task_done() diff --git a/src/class_singleCleaner.py b/src/class_singleCleaner.py index bccf4a62..f9e87b1a 100644 --- a/src/class_singleCleaner.py +++ b/src/class_singleCleaner.py @@ -7,6 +7,7 @@ import pickle import tr#anslate from helper_sql import * +from helper_threading import * from debug import logger """ @@ -28,10 +29,11 @@ resends msg messages in 5 days (then 10 days, then 20 days, etc...) """ -class singleCleaner(threading.Thread): +class singleCleaner(threading.Thread, StoppableThread): def __init__(self): threading.Thread.__init__(self, name="singleCleaner") + self.initStop() def run(self): timeWeLastClearedInventoryAndPubkeysTables = 0 @@ -41,7 +43,7 @@ class singleCleaner(threading.Thread): # Either the user hasn't set stopresendingafterxdays and stopresendingafterxmonths yet or the options are missing from the config file. shared.maximumLengthOfTimeToBotherResendingMessages = float('inf') - while True: + while shared.shutdown == 0: shared.UISignalQueue.put(( 'updateStatusBar', 'Doing housekeeping (Flushing inventory in memory to disk...)')) with shared.inventoryLock: # If you use both the inventoryLock and the sqlLock, always use the inventoryLock OUTSIDE of the sqlLock. @@ -84,7 +86,7 @@ class singleCleaner(threading.Thread): for row in queryreturn: if len(row) < 2: logger.error('Something went wrong in the singleCleaner thread: a query did not return the requested fields. ' + repr(row)) - time.sleep(3) + self.stop.wait(3) break toAddress, ackData, status = row if status == 'awaitingpubkey': @@ -121,7 +123,7 @@ class singleCleaner(threading.Thread): os._exit(0) shared.knownNodesLock.release() shared.needToWriteKnownNodesToDisk = False - time.sleep(300) + self.stop.wait(300) def resendPubkeyRequest(address): diff --git a/src/class_singleListener.py b/src/class_singleListener.py index 77e6eee6..a571867a 100644 --- a/src/class_singleListener.py +++ b/src/class_singleListener.py @@ -4,6 +4,7 @@ import socket from class_sendDataThread import * from class_receiveDataThread import * import helper_bootstrap +from helper_threading import * import errno import re @@ -15,10 +16,11 @@ import re # (within the recversion function of the recieveData thread) -class singleListener(threading.Thread): +class singleListener(threading.Thread, StoppableThread): def __init__(self): threading.Thread.__init__(self, name="singleListener") + self.initStop() def setup(self, selfInitiatedConnections): self.selfInitiatedConnections = selfInitiatedConnections @@ -37,6 +39,16 @@ class singleListener(threading.Thread): sock.bind((HOST, PORT)) sock.listen(2) return sock + + def stopThread(self): + super(singleListener, self).stopThread() + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + try: + s.connect(('127.0.0.1', shared.config.getint('bitmessagesettings', 'port'))) + s.shutdown(socket.SHUT_RDWR) + s.close() + except: + pass def run(self): # If there is a trusted peer then we don't want to accept @@ -44,15 +56,15 @@ class singleListener(threading.Thread): if shared.trustedPeer: return - while shared.safeConfigGetBoolean('bitmessagesettings', 'dontconnect'): - time.sleep(1) + while shared.safeConfigGetBoolean('bitmessagesettings', 'dontconnect') and shared.shutdown == 0: + self.stop.wait(1) helper_bootstrap.dns() # We typically don't want to accept incoming connections if the user is using a # SOCKS proxy, unless they have configured otherwise. If they eventually select # proxy 'none' or configure SOCKS listening then this will start listening for # connections. - while shared.config.get('bitmessagesettings', 'socksproxytype')[0:5] == 'SOCKS' and not shared.config.getboolean('bitmessagesettings', 'sockslisten'): - time.sleep(5) + while shared.config.get('bitmessagesettings', 'socksproxytype')[0:5] == 'SOCKS' and not shared.config.getboolean('bitmessagesettings', 'sockslisten') and shared.shutdown == 0: + self.stop.wait(5) logger.info('Listening for incoming connections.') @@ -73,19 +85,19 @@ class singleListener(threading.Thread): # regexp to match an IPv4-mapped IPv6 address mappedAddressRegexp = re.compile(r'^::ffff:([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)$') - while True: + while shared.shutdown == 0: # We typically don't want to accept incoming connections if the user is using a # SOCKS proxy, unless they have configured otherwise. If they eventually select # proxy 'none' or configure SOCKS listening then this will start listening for # connections. - while shared.config.get('bitmessagesettings', 'socksproxytype')[0:5] == 'SOCKS' and not shared.config.getboolean('bitmessagesettings', 'sockslisten'): - time.sleep(10) - while len(shared.connectedHostsList) > 220: + while shared.config.get('bitmessagesettings', 'socksproxytype')[0:5] == 'SOCKS' and not shared.config.getboolean('bitmessagesettings', 'sockslisten') and shared.shutdown == 0: + self.stop.wait(10) + while len(shared.connectedHostsList) > 220 and shared.shutdown == 0: logger.info('We are connected to too many people. Not accepting further incoming connections for ten seconds.') - time.sleep(10) + self.stop.wait(10) - while True: + while shared.shutdown == 0: socketObject, sockaddr = sock.accept() (HOST, PORT) = sockaddr[0:2] diff --git a/src/class_singleWorker.py b/src/class_singleWorker.py index de8848b2..6a827ce2 100644 --- a/src/class_singleWorker.py +++ b/src/class_singleWorker.py @@ -15,17 +15,26 @@ from debug import logger from helper_sql import * import helper_inbox from helper_generic import addDataPadding +from helper_threading import * import l10n # This thread, of which there is only one, does the heavy lifting: # calculating POWs. -class singleWorker(threading.Thread): +class singleWorker(threading.Thread, StoppableThread): def __init__(self): # QThread.__init__(self, parent) threading.Thread.__init__(self, name="singleWorker") + self.initStop() + + def stopThread(self): + try: + shared.workerQueue.put(("stopThread", "data")) + except: + pass + super(singleWorker, self).stopThread() def run(self): @@ -52,7 +61,7 @@ class singleWorker(threading.Thread): logger.info('Watching for ackdata ' + ackdata.encode('hex')) shared.ackdataForWhichImWatching[ackdata] = 0 - time.sleep( + self.stop.wait( 10) # give some time for the GUI to start before we start on existing POW tasks. queryreturn = sqlQuery( @@ -68,7 +77,7 @@ class singleWorker(threading.Thread): # just in case there are any tasks for Broadcasts # that have yet to be sent. - while True: + while shared.shutdown == 0: command, data = shared.workerQueue.get() if command == 'sendmessage': self.sendMsg() @@ -80,6 +89,8 @@ class singleWorker(threading.Thread): self.sendOutOrStoreMyV3Pubkey(data) elif command == 'sendOutOrStoreMyV4Pubkey': self.sendOutOrStoreMyV4Pubkey(data) + elif command == 'stopThread': + return else: logger.error('Probable programming error: The command sent to the workerThread is weird. It is: %s\n' % command) diff --git a/src/shared.py b/src/shared.py index 96eefa29..249bd562 100644 --- a/src/shared.py +++ b/src/shared.py @@ -414,12 +414,10 @@ def doCleanShutdown(): from class_outgoingSynSender import outgoingSynSender for thread in threading.enumerate(): - if thread.name == "uPnPThread" or "outgoingSynSender" in thread.name: - if thread.isAlive() and isinstance(thread, StoppableThread): - thread.stopThread() + if thread.isAlive() and isinstance(thread, StoppableThread): + thread.stopThread() for thread in threading.enumerate(): - if thread.name == "uPnPThread": - #or "outgoingSynSender" in thread.name: + if thread is not threading.currentThread() and isinstance(thread, StoppableThread) and not isinstance(thread, outgoingSynSender): logger.debug("Waiting for thread %s", thread.name) thread.join() From fbd41353a161cd874595a819b5de826637b81606 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Wed, 25 Nov 2015 00:18:31 +0100 Subject: [PATCH 184/210] Account in treeWidget more like a QT class It behaves more like a QT class is supposed to now, it's somewhat cleaner. Fixes #122 --- src/bitmessageqt/__init__.py | 13 +--- src/bitmessageqt/foldertree.py | 105 +++++++++++++++++++-------------- 2 files changed, 61 insertions(+), 57 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 11d46c66..aefc1990 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -3909,14 +3909,10 @@ class MyForm(settingsmixin.SMainWindow): return newLabel = str(item.text(0)) - newLabel = newLabel.replace("(" + str(item.address) + ")", '') - newLabel = newLabel.rstrip() if item.type == "subscription": oldLabel = item.label else: oldLabel = shared.config.get(str(item.address), 'label') - oldLabel = oldLabel.replace("(" + str(item.address) + ")", '') - oldLabel = oldLabel.rstrip() # unchanged, do not do anything either if newLabel == oldLabel: return @@ -3926,14 +3922,7 @@ class MyForm(settingsmixin.SMainWindow): return self.recurDepth += 1 - if item.type == "subscription": - sqlExecute( - '''UPDATE subscriptions SET label=? WHERE address=?''', - newLabel, item.address) - item.setLabel(newLabel) - else: - shared.config.set(str(item.address), 'label', newLabel) - shared.writeKeysFile() + item.setData(0, QtCore.Qt.EditRole, newLabel) item.updateText() if item.type == 'mailinglist': self.rerenderComboBoxSendFromBroadcast() diff --git a/src/bitmessageqt/foldertree.py b/src/bitmessageqt/foldertree.py index 20291c5d..7243bbde 100644 --- a/src/bitmessageqt/foldertree.py +++ b/src/bitmessageqt/foldertree.py @@ -1,5 +1,6 @@ from PyQt4 import QtCore, QtGui +from helper_sql import * from utils import * import shared from settingsmixin import SettingsMixin @@ -130,6 +131,34 @@ class Ui_AddressWidget(QtGui.QTreeWidgetItem, AccountMixin, SettingsMixin): self.initialised = True self.setType() # does updateText + def data(self, column, role): + if column == 0: + if role == QtCore.Qt.DisplayRole: + if self.unreadCount > 0 and not self.isExpanded(): + return unicode(shared.config.get(self.address, 'label'), 'utf-8)') + ' (' + str(self.unreadCount) + ') (' + self.address + ')' + else: + return unicode(shared.config.get(self.address, 'label'), 'utf-8)') + ' (' + self.address + ')' + elif role == QtCore.Qt.EditRole: + return unicode(shared.config.get(self.address, 'label'), 'utf-8') + elif role == QtCore.Qt.ToolTipRole: + return unicode(shared.config.get(self.address, 'label'), 'utf-8)') + ' (' + self.address + ')' + elif role == QtCore.Qt.DecorationRole: + return avatarize(self.address) + elif role == QtCore.Qt.FontRole: + font = QtGui.QFont() + font.setBold(self.unreadCount > 0) + return font + elif role == QtCore.Qt.ForegroundRole: + return self.accountBrush() + return super(Ui_AddressWidget, self).data(column, role) + + def setData(self, column, role, value): + if role == QtCore.Qt.EditRole: + shared.config.set(str(self.address), 'label', str(value.toString())) + shared.writeKeysFile() + return + return super(Ui_AddressWidget, self).setData(column, role, value) + def setAddress(self, address): super(Ui_AddressWidget, self).setAddress(address) self.setData(0, QtCore.Qt.UserRole, self.address) @@ -137,34 +166,12 @@ class Ui_AddressWidget(QtGui.QTreeWidgetItem, AccountMixin, SettingsMixin): def updateText(self): if not self.initialised: return - text = unicode(shared.config.get(self.address, 'label'), 'utf-8)') + ' (' + self.address + ')' - - font = QtGui.QFont() - if self.unreadCount > 0: - # only show message count if the child doesn't show - if not self.isExpanded(): - text += " (" + str(self.unreadCount) + ")" - font.setBold(True) - else: - font.setBold(False) - self.setFont(0, font) - - #set text color - self.setForeground(0, self.accountBrush()) - - self.setIcon(0, avatarize(self.address)) - self.setText(0, text) - self.setToolTip(0, text) -# self.setData(0, QtCore.Qt.UserRole, [self.address, "inbox"]) + self.emitDataChanged() def setExpanded(self, expand): super(Ui_AddressWidget, self).setExpanded(expand) self.updateText() - def edit(self): - self.setText(0, shared.config.get(self.address, 'label')) - super(QtGui.QAbstractItemView, self).edit() - # label (or address) alphabetically, disabled at the end def __lt__(self, other): if (isinstance(other, Ui_AddressWidget)): @@ -210,32 +217,40 @@ class Ui_SubscriptionWidget(Ui_AddressWidget, AccountMixin): def setType(self): self.type = "subscription" - def edit(self): - self.setText(0, self.label) - super(QtGui.QAbstractItemView, self).edit() + def data(self, column, role): + if column == 0: + if role == QtCore.Qt.DisplayRole: + if self.unreadCount > 0 and not self.isExpanded(): + return unicode(self.label, 'utf-8)') + ' (' + str(self.unreadCount) + ') (' + self.address + ')' + else: + return unicode(self.label, 'utf-8)') + ' (' + self.address + ')' + elif role == QtCore.Qt.EditRole: + return unicode(self.label, 'utf-8') + elif role == QtCore.Qt.ToolTipRole: + return unicode(self.label, 'utf-8)') + ' (' + self.address + ')' + elif role == QtCore.Qt.DecorationRole: + return avatarize(self.address) + elif role == QtCore.Qt.FontRole: + font = QtGui.QFont() + font.setBold(self.unreadCount > 0) + return font + elif role == QtCore.Qt.ForegroundRole: + return self.accountBrush() + return super(Ui_SubscriptionWidget, self).data(column, role) + + def setData(self, column, role, value): + if role == QtCore.Qt.EditRole: + self.setLabel(str(value.toString())) + sqlExecute( + '''UPDATE subscriptions SET label=? WHERE address=?''', + self.label, self.address) + return + return super(Ui_SubscriptionWidget, self).setData(column, role, value) def updateText(self): if not self.initialised: return - text = unicode(self.label, 'utf-8)') + ' (' + self.address + ')' - - font = QtGui.QFont() - if self.unreadCount > 0: - # only show message count if the child doesn't show - if not self.isExpanded(): - text += " (" + str(self.unreadCount) + ")" - font.setBold(True) - else: - font.setBold(False) - self.setFont(0, font) - - #set text color - self.setForeground(0, self.accountBrush()) - - self.setIcon(0, avatarize(self.address)) - self.setText(0, text) - self.setToolTip(0, text) -# self.setData(0, QtCore.Qt.UserRole, [self.address, "inbox"]) + self.emitDataChanged() # label (or address) alphabetically, disabled at the end def __lt__(self, other): From c69ac10232599b3e6ce92015827321c3d2246d15 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Wed, 25 Nov 2015 01:02:17 +0100 Subject: [PATCH 185/210] addressGenerator fixes - addressGenerator got stuck (introduced recently when cleaning up shutdown - do not put addresses into API return queue if API is inactive - improve translation --- src/class_addressGenerator.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/class_addressGenerator.py b/src/class_addressGenerator.py index 5d1598c4..c48756e8 100644 --- a/src/class_addressGenerator.py +++ b/src/class_addressGenerator.py @@ -142,7 +142,8 @@ class addressGenerator(threading.Thread, StoppableThread): # The API and the join and create Chan functionality # both need information back from the address generator. - shared.apiAddressGeneratorReturnQueue.put(address) + if shared.safeConfigGetBoolean('bitmessagesettings', 'apienabled'): + shared.apiAddressGeneratorReturnQueue.put(address) shared.UISignalQueue.put(( 'updateStatusBar', tr.translateText("MainWindow", "Done generating address. Doing work necessary to broadcast it..."))) @@ -161,10 +162,8 @@ class addressGenerator(threading.Thread, StoppableThread): sys.stderr.write( 'WARNING: You are creating deterministic address(es) using a blank passphrase. Bitmessage will do it but it is rather stupid.') if command == 'createDeterministicAddresses': - statusbar = 'Generating ' + str( - numberOfAddressesToMake) + ' new addresses.' shared.UISignalQueue.put(( - 'updateStatusBar', statusbar)) + 'updateStatusBar', tr.translateText("MainWindow","Generating %1 new addresses.").arg(str(numberOfAddressesToMake)))) signingKeyNonce = 0 encryptionKeyNonce = 1 listOfNewAddressesToSendOutThroughTheAPI = [ @@ -210,7 +209,8 @@ class addressGenerator(threading.Thread, StoppableThread): # If we are joining an existing chan, let us check to make sure it matches the provided Bitmessage address if command == 'joinChan': if address != chanAddress: - shared.apiAddressGeneratorReturnQueue.put('chan name does not match address') + if shared.safeConfigGetBoolean('bitmessagesettings', 'apienabled'): + shared.apiAddressGeneratorReturnQueue.put('chan name does not match address') saveAddressToDisk = False if command == 'getDeterministicAddress': saveAddressToDisk = False @@ -281,12 +281,13 @@ class addressGenerator(threading.Thread, StoppableThread): # Done generating addresses. - if command == 'createDeterministicAddresses' or command == 'joinChan' or command == 'createChan': - shared.apiAddressGeneratorReturnQueue.put( - listOfNewAddressesToSendOutThroughTheAPI) - elif command == 'getDeterministicAddress': - shared.apiAddressGeneratorReturnQueue.put(address) + if shared.safeConfigGetBoolean('bitmessagesettings', 'apienabled'): + if command == 'createDeterministicAddresses' or command == 'joinChan' or command == 'createChan': + shared.apiAddressGeneratorReturnQueue.put( + listOfNewAddressesToSendOutThroughTheAPI) + elif command == 'getDeterministicAddress': + shared.apiAddressGeneratorReturnQueue.put(address) else: raise Exception( "Error in the addressGenerator thread. Thread was given a command it could not understand: " + command) - shared.apiAddressGeneratorQueue.task_done() + shared.addressGeneratorQueue.task_done() From 60c4c0d0544565901a8657808d9dbdd043e789bd Mon Sep 17 00:00:00 2001 From: mailchuck Date: Thu, 26 Nov 2015 02:34:44 +0100 Subject: [PATCH 186/210] SMP fix for frozen Windows --- src/bitmessagemain.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 2767e465..64e2784d 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -18,6 +18,7 @@ import singleton import os import socket import ctypes +from multiprocessing import Process, freeze_support from struct import pack import sys from subprocess import call @@ -265,6 +266,7 @@ class Main: return {'address':address,'port':port} if __name__ == "__main__": + freeze_support() mainprogram = Main() mainprogram.start() From b22ba6f74d3181a65b208a4c4647f83cc7fb33ae Mon Sep 17 00:00:00 2001 From: mailchuck Date: Thu, 26 Nov 2015 02:35:59 +0100 Subject: [PATCH 187/210] Failure to connect shouln't be an error It resulted in too many errors in the logs --- src/class_outgoingSynSender.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/class_outgoingSynSender.py b/src/class_outgoingSynSender.py index 9b7600cd..738882ef 100644 --- a/src/class_outgoingSynSender.py +++ b/src/class_outgoingSynSender.py @@ -213,7 +213,7 @@ class outgoingSynSender(threading.Thread, StoppableThread): logger.error('Bitmessage MIGHT be having trouble connecting to the SOCKS server. ' + str(err)) else: if shared.verbose >= 1: - logger.error('Could NOT connect to ' + str(peer) + 'during outgoing attempt. ' + str(err)) + logger.debug('Could NOT connect to ' + str(peer) + 'during outgoing attempt. ' + str(err)) deletedPeer = None with shared.knownNodesLock: From 241f847411e8d94f1295f75e5fa5bef6534fecc5 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Thu, 26 Nov 2015 02:37:07 +0100 Subject: [PATCH 188/210] OpenCL GUI settings --- src/bitmessageqt/__init__.py | 14 ++++++++++++++ src/bitmessageqt/settings.py | 7 +++++++ 2 files changed, 21 insertions(+) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index aefc1990..16dea82c 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -57,6 +57,7 @@ import subprocess import datetime from helper_sql import * import l10n +import openclpow import types from utils import * from collections import OrderedDict @@ -2631,6 +2632,9 @@ class MyForm(settingsmixin.SMainWindow): shared.config.set('bitmessagesettings', 'defaultpayloadlengthextrabytes', str(int(float( self.settingsDialogInstance.ui.lineEditSmallMessageDifficulty.text()) * shared.networkDefaultPayloadLengthExtraBytes))) + if openclpow.has_opencl() and self.settingsDialogInstance.ui.checkBoxOpenCL.isChecked != shared.safeConfigGetBoolean("bitmessagesettings", "opencl"): + shared.config.set('bitmessagesettings', 'opencl', str(self.settingsDialogInstance.ui.checkBoxOpenCL.isChecked)) + acceptableDifficultyChanged = False if float(self.settingsDialogInstance.ui.lineEditMaxAcceptableTotalDifficulty.text()) >= 1 or float(self.settingsDialogInstance.ui.lineEditMaxAcceptableTotalDifficulty.text()) == 0: @@ -4158,6 +4162,16 @@ class settingsDialog(QtGui.QDialog): self.ui.lineEditMaxAcceptableSmallMessageDifficulty.setText(str((float(shared.config.getint( 'bitmessagesettings', 'maxacceptablepayloadlengthextrabytes')) / shared.networkDefaultPayloadLengthExtraBytes))) + # OpenCL + if openclpow.has_opencl(): + self.ui.checkBoxOpenCL.setEnabled(True) + else: + self.ui.checkBoxOpenCL.setEnabled(False) + if shared.safeConfigGetBoolean("bitmessagesettings", "opencl"): + self.ui.checkBoxOpenCL.setChecked(True) + else: + self.ui.checkBoxOpenCL.setChecked(False) + # Namecoin integration tab nmctype = shared.config.get('bitmessagesettings', 'namecoinrpctype') self.ui.lineEditNamecoinHost.setText(str( diff --git a/src/bitmessageqt/settings.py b/src/bitmessageqt/settings.py index 4a1dfc7c..efdfc1b7 100644 --- a/src/bitmessageqt/settings.py +++ b/src/bitmessageqt/settings.py @@ -308,6 +308,12 @@ class Ui_settingsDialog(object): 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.checkBoxOpenCL = QtGui.QCheckBox(self.tabMaxAcceptableDifficulty) + self.checkBoxOpenCL.setObjectName = (_fromUtf8("checkBoxOpenCL")) + self.gridLayout_7.addWidget(self.checkBoxOpenCL, 4, 1, 1, 1) self.tabWidgetSettings.addTab(self.tabMaxAcceptableDifficulty, _fromUtf8("")) self.tabNamecoin = QtGui.QWidget() self.tabNamecoin.setObjectName(_fromUtf8("tabNamecoin")) @@ -481,6 +487,7 @@ class Ui_settingsDialog(object): 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", "

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 test.

(Getting your own Bitmessage address into Namecoin is still rather difficult).

Bitmessage can use either namecoind directly or a running nmcontrol instance.

", None)) self.label_17.setText(_translate("settingsDialog", "Host:", None)) self.label_18.setText(_translate("settingsDialog", "Port:", None)) From 1fda2231afe080f5d855df8085f71a1a2acc7fe6 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Thu, 26 Nov 2015 02:37:52 +0100 Subject: [PATCH 189/210] OpenCL kernel change This makes it work on my AMD. --- src/bitmsghash/bitmsghash.cl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/bitmsghash/bitmsghash.cl b/src/bitmsghash/bitmsghash.cl index 1ab5d219..3c8c21a5 100644 --- a/src/bitmsghash/bitmsghash.cl +++ b/src/bitmsghash/bitmsghash.cl @@ -24,9 +24,8 @@ /// Warning: This version of SWAP64(n) is slow and avoid bugs on AMD GPUs(7970) -#define SWAP64(n) as_ulong(as_uchar8(n).s76543210) +// #define SWAP64(n) as_ulong(as_uchar8(n).s76543210) -/* #define SWAP64(n) \ (((n) << 56) \ | (((n) & 0xff00) << 40) \ @@ -36,7 +35,6 @@ | (((n) >> 24) & 0xff0000) \ | (((n) >> 40) & 0xff00) \ | ((n) >> 56)) -*/ From e69e38a764126af467e8bf1f82000e8d907caa86 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Thu, 26 Nov 2015 02:38:55 +0100 Subject: [PATCH 190/210] PoW support code cleanup Is now nicer and reports if OpenCL fails --- src/class_singleWorker.py | 10 ++++++-- src/openclpow.py | 53 ++++++++++++++++++++++++++------------- src/proofofwork.py | 52 ++++++++++++++++++++++++-------------- 3 files changed, 76 insertions(+), 39 deletions(-) diff --git a/src/class_singleWorker.py b/src/class_singleWorker.py index 6a827ce2..6de8703c 100644 --- a/src/class_singleWorker.py +++ b/src/class_singleWorker.py @@ -21,6 +21,12 @@ import l10n # This thread, of which there is only one, does the heavy lifting: # calculating POWs. +def sizeof_fmt(num, suffix='h/s'): + for unit in ['','k','M','G','T','P','E','Z']: + if abs(num) < 1000.0: + return "%3.1f%s%s" % (num, unit, suffix) + num /= 1024.0 + return "%.1f%s%s" % (num, 'Yi', suffix) class singleWorker(threading.Thread, StoppableThread): @@ -787,7 +793,7 @@ class singleWorker(threading.Thread, StoppableThread): trialValue, nonce = proofofwork.run(target, initialHash) logger.info('(For msg message) Found proof of work ' + str(trialValue) + ' Nonce: ' + str(nonce)) try: - logger.info('POW took ' + str(int(time.time() - powStartTime)) + ' seconds. ' + str(nonce / (time.time() - powStartTime)) + ' nonce trials per second.') + logger.info('PoW took %.1f seconds, speed %s.', time.time() - powStartTime, sizeof_fmt(nonce / (time.time() - powStartTime))) except: pass @@ -966,7 +972,7 @@ class singleWorker(threading.Thread, StoppableThread): trialValue, nonce = proofofwork.run(target, initialHash) logger.info('(For ack message) Found proof of work ' + str(trialValue) + ' Nonce: ' + str(nonce)) try: - logger.info('POW took ' + str(time.time() - powStartTime) + ' seconds. ' + str(nonce / (time.time() - powStartTime)) + ' nonce trials per second.') + logger.info('PoW took %.1f seconds, speed %s.', time.time() - powStartTime, sizeof_fmt(nonce / (time.time() - powStartTime))) except: pass diff --git a/src/openclpow.py b/src/openclpow.py index a2cfb068..426913a4 100644 --- a/src/openclpow.py +++ b/src/openclpow.py @@ -5,35 +5,49 @@ import hashlib import random import os +from shared import codePath, safeConfigGetBoolean +from debug import logger + +libAvailable = True ctx = False queue = False program = False +gpus = [] +hash_dt = None try: import numpy import pyopencl as cl - hash_dt = numpy.dtype([('target', numpy.uint64), ('v', numpy.str_, 73)]) - gpus = [] - for platform in cl.get_platforms(): - gpus.extend(platform.get_devices(device_type=cl.device_type.GPU)) - if (len(gpus) > 0): - ctx = cl.Context(devices=gpus) - queue = cl.CommandQueue(ctx) - full_path = os.path.dirname(os.path.realpath(__file__)) - f = open(os.path.join(full_path, "bitmsghash", 'bitmsghash.cl'), 'r') - fstr = ''.join(f.readlines()) - program = cl.Program(ctx, fstr).build(options="") - else: - print "No OpenCL GPUs found" +except: + libAvailable = False + +def initCL(): + global ctx, queue, program, gpus, hash_dt + try: + hash_dt = numpy.dtype([('target', numpy.uint64), ('v', numpy.str_, 73)]) + for platform in cl.get_platforms(): + gpus.extend(platform.get_devices(device_type=cl.device_type.GPU)) + if (len(gpus) > 0): + ctx = cl.Context(devices=gpus) + queue = cl.CommandQueue(ctx) + f = open(os.path.join(codePath(), "bitmsghash", 'bitmsghash.cl'), 'r') + fstr = ''.join(f.readlines()) + program = cl.Program(ctx, fstr).build(options="") + logger.info("Loaded OpenCL kernel") + else: + logger.info("No OpenCL GPUs found") + ctx = False + except Exception as e: + logger.error("OpenCL fail: ", exc_info=True) ctx = False -except Exception as e: - print "opencl fail: " + str(e) - ctx = False def has_opencl(): + global ctx return (ctx != False) def do_opencl_pow(hash, target): + global ctx, queue, program, gpus, hash_dt + output = numpy.zeros(1, dtype=[('v', numpy.uint64, 1)]) if (ctx == False): return output[0][0] @@ -62,11 +76,14 @@ def do_opencl_pow(hash, target): queue.finish() progress += globamt sofar = time.time() - start - print sofar, progress / sofar, "hashes/sec" +# logger.debug("Working for %.3fs, %.2f Mh/s", sofar, (progress / sofar) / 1000000) taken = time.time() - start - print progress, taken +# logger.debug("Took %d tries.", progress) return output[0][0] +if libAvailable: + initCL() + if __name__ == "__main__": target = 54227212183L initialHash = "3758f55b5a8d902fd3597e4ce6a2d3f23daff735f65d9698c270987f4e67ad590b93f3ffeba0ef2fd08a8dc2f87b68ae5a0dc819ab57f22ad2c4c9c8618a43b3".decode("hex") diff --git a/src/proofofwork.py b/src/proofofwork.py index fc779467..8ab04820 100644 --- a/src/proofofwork.py +++ b/src/proofofwork.py @@ -4,9 +4,10 @@ import hashlib from struct import unpack, pack import sys -from shared import config, frozen, codePath -import shared +from debug import logger +from shared import config, frozen, codePath, shutdown, safeConfigGetBoolean, UISignalQueue import openclpow +import tr import os import ctypes @@ -17,12 +18,20 @@ if "win32" == sys.platform: else: bitmsglib = 'bitmsghash64.dll' try: + # MSVS bso = ctypes.WinDLL(os.path.join(codePath(), "bitmsghash", bitmsglib)) + logger.info("Loaded C PoW DLL (stdcall) %s", bitmsglib) except: - bso = None + try: + # MinGW + bso = ctypes.CDLL(os.path.join(codePath(), "bitmsghash", bitmsglib)) + logger.info("Loaded C PoW DLL (cdecl) %s", bitmsglib) + except: + bso = None else: try: bso = ctypes.CDLL(os.path.join(codePath(), "bitmsghash", bitmsglib)) + logger.info("Loaded C PoW DLL %s", bitmsglib) except: bso = None if bso: @@ -58,16 +67,17 @@ def _pool_worker(nonce, initialHash, target, pool_size): return [trialValue, nonce] def _doSafePoW(target, initialHash): - print "Safe POW\n" + logger.debug("Safe PoW start") nonce = 0 trialValue = float('inf') while trialValue > target: nonce += 1 trialValue, = unpack('>Q',hashlib.sha512(hashlib.sha512(pack('>Q',nonce) + initialHash).digest()).digest()[0:8]) + logger.debug("Safe PoW done") return [trialValue, nonce] def _doFastPoW(target, initialHash): - print "Fast POW\n" + logger.debug("Fast PoW start") import time from multiprocessing import Pool, cpu_count try: @@ -85,7 +95,7 @@ def _doFastPoW(target, initialHash): for i in range(pool_size): result.append(pool.apply_async(_pool_worker, args = (i, initialHash, target, pool_size))) while True: - if shared.shutdown >= 1: + if shutdown >= 1: pool.terminate() while True: time.sleep(10) # Don't let this thread return here; it will return nothing and cause an exception in bitmessagemain.py @@ -95,30 +105,38 @@ def _doFastPoW(target, initialHash): result = result[i].get() pool.terminate() pool.join() #Wait for the workers to exit... + logger.debug("Fast PoW done") return result[0], result[1] time.sleep(0.2) + def _doCPoW(target, initialHash): h = initialHash m = target out_h = ctypes.pointer(ctypes.create_string_buffer(h, 64)) out_m = ctypes.c_ulonglong(m) - print "C PoW start" + logger.debug("C PoW start") nonce = bmpow(out_h, out_m) trialValue, = unpack('>Q',hashlib.sha512(hashlib.sha512(pack('>Q',nonce) + initialHash).digest()).digest()[0:8]) - print "C PoW done" + logger.debug("C PoW done") return [trialValue, nonce] def _doGPUPoW(target, initialHash): - print "GPU PoW start" + logger.debug("GPU PoW start") nonce = openclpow.do_opencl_pow(initialHash.encode("hex"), target) trialValue, = unpack('>Q',hashlib.sha512(hashlib.sha512(pack('>Q',nonce) + initialHash).digest()).digest()[0:8]) #print "{} - value {} < {}".format(nonce, trialValue, target) - print "GPU PoW done" + if trialValue > target: + deviceNames = ", ".join(gpu.name for gpu in openclpow.gpus) + UISignalQueue.put(('updateStatusBar', tr.translateText("MainWindow",'Your GPU(s) did not calculate correctly, disabling OpenCL. Please report to the developers.'))) + logger.error("Your GPUs (%s) did not calculate correctly, disabling OpenCL. Please report to the developers.", deviceNames) + openclpow.ctx = False + raise Exception("GPU did not calculate correctly.") + logger.debug("GPU PoW done") return [trialValue, nonce] def run(target, initialHash): target = int(target) - if shared.safeConfigGetBoolean('bitmessagesettings', 'opencl') and openclpow.has_opencl(): + if safeConfigGetBoolean('bitmessagesettings', 'opencl') and openclpow.has_opencl(): # trialvalue1, nonce1 = _doGPUPoW(target, initialHash) # trialvalue, nonce = _doFastPoW(target, initialHash) # print "GPU: %s, %s" % (trialvalue1, nonce1) @@ -133,12 +151,8 @@ def run(target, initialHash): return _doCPoW(target, initialHash) except: pass # fallback - if frozen == "macosx_app" or not frozen: - # on my (Peter Surda) Windows 10, Windows Defender - # does not like this and fights with PyBitmessage - # over CPU, resulting in very slow PoW - try: - return _doFastPoW(target, initialHash) - except: - pass #fallback + try: + return _doFastPoW(target, initialHash) + except: + pass #fallback return _doSafePoW(target, initialHash) From bd2cddf14a97820472d3d8dc1f7a36613558b2ab Mon Sep 17 00:00:00 2001 From: mailchuck Date: Thu, 26 Nov 2015 19:41:20 +0100 Subject: [PATCH 191/210] Avoid strings in account types --- src/bitmessageqt/__init__.py | 40 +++++----- src/bitmessageqt/foldertree.py | 138 ++++++++++++++------------------- 2 files changed, 79 insertions(+), 99 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 16dea82c..f3715b0c 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -887,7 +887,7 @@ class MyForm(settingsmixin.SMainWindow): def updateUnreadCount(item): # if refreshing the account root, we need to rescan folders if type == 0 or (folder is None and isinstance(item, Ui_FolderWidget)): - if addressItem.type == 'subscription' or addressItem.type == 'mailinglist': + if addressItem.type in [AccountMixin.SUBSCRIPTION, AccountMixin.MAILINGLIST]: xAddress = "fromaddress" else: xAddress = "toaddress" @@ -2059,20 +2059,20 @@ class MyForm(settingsmixin.SMainWindow): queryreturn = sqlQuery('SELECT label, address FROM subscriptions WHERE enabled = 1') for row in queryreturn: label, address = row - addRow(address, label, 'subscription') + addRow(address, label, AccountMixin.SUBSCRIPTION) # chans addresses = getSortedAccounts() for address in addresses: account = accountClass(address) - if (account.type == 'chan' and shared.safeConfigGetBoolean(address, 'enabled')): - addRow(address, account.getLabel(), 'chan') + if (account.type == AccountMixin.CHAN and shared.safeConfigGetBoolean(address, 'enabled')): + addRow(address, account.getLabel(), AccountMixin.CHAN) # normal accounts queryreturn = sqlQuery('SELECT * FROM addressbook') for row in queryreturn: label, address = row - addRow(address, label, 'normal') + addRow(address, label, AccountMixin.NORMAL) # sort self.ui.tableWidgetAddressBook.sortItems(0, QtCore.Qt.AscendingOrder) @@ -2847,7 +2847,7 @@ class MyForm(settingsmixin.SMainWindow): addressAtCurrentRow = self.getCurrentAccount() acct = accountClass(addressAtCurrentRow) # no chans / mailinglists - if acct.type != 'normal': + if acct.type != AccountMixin.NORMAL: return if self.dialog.ui.radioButtonUnregister.isChecked() and isinstance(acct, GatewayAccount): acct.unregister() @@ -3371,7 +3371,7 @@ class MyForm(settingsmixin.SMainWindow): currentRow = row.row() type = str(self.ui.tableWidgetAddressBook.item( currentRow, 0).data(Qt.UserRole).toPyObject()) - if type != "normal": + if type != AccountMixin.NORMAL: normal = False if normal: # only if all selected addressbook items are normal, allow delete @@ -3528,9 +3528,9 @@ class MyForm(settingsmixin.SMainWindow): def getAccountTreeWidget(self, account): try: - if account.type == 'chan': + if account.type == AccountMixin.CHAN: return self.ui.treeWidgetChans - elif account.type == 'subscription': + elif account.type == AccountMixin.SUBSCRIPTION: return self.ui.treeWidgetSubscriptions else: return self.ui.treeWidgetYourIdentities @@ -3552,9 +3552,9 @@ class MyForm(settingsmixin.SMainWindow): def getAccountMessagelist(self, account): try: - if account.type == 'chan': + if account.type == AccountMixin.CHAN: return self.ui.tableWidgetInboxChans - elif account.type == 'subscription': + elif account.type == AccountMixin.SUBSCRIPTION: return self.ui.tableWidgetInboxSubscriptions else: return self.ui.tableWidgetInbox @@ -3586,9 +3586,9 @@ class MyForm(settingsmixin.SMainWindow): def getAccountTextedit(self, account): try: - if account.type == 'chan': + if account.type == AccountMixin.CHAN: return self.ui.textEditInboxMessageChans - elif account.type == 'subscription': + elif account.type == AccountMixin.SUBSCRIPTION: return self.ui.textEditInboxSubscriptions else: return self.ui.textEditInboxMessage @@ -3665,9 +3665,9 @@ class MyForm(settingsmixin.SMainWindow): def on_action_YourIdentitiesDelete(self): account = self.getCurrentItem() - if account.type == "normal": + if account.type == AccountMixin.NORMAL: return # maybe in the future - elif account.type == "chan": + elif account.type == AccountMixin.CHAN: if QtGui.QMessageBox.question(self, "Delete channel?", _translate("MainWindow", "If you delete the channel, messages that you already received will become inaccessible. Maybe you can consider disabling the channel instead. Disabled channels will not receive new messages, but you can still view messages you already received.\n\nAre you sure you want to delete the channel?"), QMessageBox.Yes|QMessageBox.No) == QMessageBox.Yes: shared.config.remove_section(str(account.address)) else: @@ -3677,9 +3677,9 @@ class MyForm(settingsmixin.SMainWindow): shared.writeKeysFile() shared.reloadMyAddressHashes() self.rerenderAddressBook() - if account.type == "normal": + if account.type == AccountMixin.NORMAL: self.rerenderTabTreeMessages() - elif account.type == "chan": + elif account.type == AccountMixin.CHAN: self.rerenderTabTreeChans() def on_action_Enable(self): @@ -3913,7 +3913,7 @@ class MyForm(settingsmixin.SMainWindow): return newLabel = str(item.text(0)) - if item.type == "subscription": + if item.type == AccountMixin.SUBSCRIPTION: oldLabel = item.label else: oldLabel = shared.config.get(str(item.address), 'label') @@ -3928,9 +3928,9 @@ class MyForm(settingsmixin.SMainWindow): self.recurDepth += 1 item.setData(0, QtCore.Qt.EditRole, newLabel) item.updateText() - if item.type == 'mailinglist': + if item.type == AccountMixin.MAILINGLIST: self.rerenderComboBoxSendFromBroadcast() - elif item.type != "subscription": + elif item.type != AccountMixin.SUBSCRIPTION: self.rerenderComboBoxSendFrom() self.recurDepth -= 1 diff --git a/src/bitmessageqt/foldertree.py b/src/bitmessageqt/foldertree.py index 7243bbde..9b01b9c3 100644 --- a/src/bitmessageqt/foldertree.py +++ b/src/bitmessageqt/foldertree.py @@ -6,12 +6,18 @@ import shared from settingsmixin import SettingsMixin class AccountMixin (object): + ALL = 0 + NORMAL = 1 + CHAN = 2 + MAILINGLIST = 3 + SUBSCRIPTION = 4 + def accountColor (self): if not self.isEnabled: return QtGui.QColor(128, 128, 128) - elif self.type == "chan": + elif self.type == self.CHAN: return QtGui.QColor(216, 119, 0) - elif self.type == "mailinglist" or self.type == "subscription": + elif self.type in [self.MAILINGLIST, self.SUBSCRIPTION]: return QtGui.QColor(137, 04, 177) else: return QtGui.QApplication.palette().text().color() @@ -51,12 +57,14 @@ class AccountMixin (object): self.updateText() def setType(self): - if shared.safeConfigGetBoolean(self.address, 'chan'): - self.type = "chan" + if self.address is None: + self.type = self.ALL + elif shared.safeConfigGetBoolean(self.address, 'chan'): + self.type = self.CHAN elif shared.safeConfigGetBoolean(self.address, 'mailinglist'): - self.type = "mailinglist" + self.type = self.MAILINGLIST else: - self.type = "normal" + self.type = self.NORMAL def updateText(self): pass @@ -119,7 +127,7 @@ class Ui_FolderWidget(QtGui.QTreeWidgetItem, AccountMixin): class Ui_AddressWidget(QtGui.QTreeWidgetItem, AccountMixin, SettingsMixin): - def __init__(self, parent, pos = 0, address = "", unreadCount = 0, enabled = True): + def __init__(self, parent, pos = 0, address = None, unreadCount = 0, enabled = True): super(QtGui.QTreeWidgetItem, self).__init__() parent.insertTopLevelItem(pos, self) # only set default when creating @@ -131,19 +139,39 @@ class Ui_AddressWidget(QtGui.QTreeWidgetItem, AccountMixin, SettingsMixin): self.initialised = True self.setType() # does updateText + def _getLabel(self): + if self.address is None: + label = QtGui.QApplication.translate("MainWindow", "All accounts") + else: + try: + return unicode(shared.config.get(self.address, 'label'), 'utf-8)') + except: + return self.address + + def _getAddressBracket(self, unreadCount = False): + ret = "" + if unreadCount: + ret += " (" + str(self.unreadCount) + ")" + if self.address is not None: + ret += " (" + self.address + ")" + return ret + def data(self, column, role): if column == 0: if role == QtCore.Qt.DisplayRole: if self.unreadCount > 0 and not self.isExpanded(): - return unicode(shared.config.get(self.address, 'label'), 'utf-8)') + ' (' + str(self.unreadCount) + ') (' + self.address + ')' + return self._getLabel() + self._getAddressBracket(True) else: - return unicode(shared.config.get(self.address, 'label'), 'utf-8)') + ' (' + self.address + ')' + return self._getLabel() + self._getAddressBracket(False) elif role == QtCore.Qt.EditRole: - return unicode(shared.config.get(self.address, 'label'), 'utf-8') + return self._getLabel() elif role == QtCore.Qt.ToolTipRole: - return unicode(shared.config.get(self.address, 'label'), 'utf-8)') + ' (' + self.address + ')' + return self._getLabel() + self._getAddressBracket(False) elif role == QtCore.Qt.DecorationRole: - return avatarize(self.address) + if self.address is None: + return avatarize(self._getLabel()) + else: + return avatarize(self.address) elif role == QtCore.Qt.FontRole: font = QtGui.QFont() font.setBold(self.unreadCount > 0) @@ -171,6 +199,12 @@ class Ui_AddressWidget(QtGui.QTreeWidgetItem, AccountMixin, SettingsMixin): def setExpanded(self, expand): super(Ui_AddressWidget, self).setExpanded(expand) self.updateText() + + def _getSortRank(self): + ret = self.type + if not self.isEnabled: + ret += 5 + return ret # label (or address) alphabetically, disabled at the end def __lt__(self, other): @@ -178,21 +212,11 @@ class Ui_AddressWidget(QtGui.QTreeWidgetItem, AccountMixin, SettingsMixin): reverse = False if self.treeWidget().header().sortIndicatorOrder() == QtCore.Qt.DescendingOrder: reverse = True - if self.isEnabled == other.isEnabled: - if self.type == other.type: - if shared.config.get(self.address, 'label'): - x = shared.config.get(self.address, 'label').decode('utf-8').lower() - else: - x = self.address.decode('utf-8').lower() - if shared.config.get(other.address, 'label'): - y = shared.config.get(other.address, 'label').decode('utf-8').lower() - else: - y = other.address.decode('utf-8').lower() - return x < y - else: - return (reverse if self.type == "mailinglist" else not reverse) -# else: - return (not reverse if self.isEnabled else reverse) + if self._getSortRank() == other._getSortRank(): + x = self._getLabel().decode('utf-8').lower() + y = other._getLabel().decode('utf-8').lower() + return x < y + return (not reverse if self._getSortRank() < other._getSortRank() else reverse) return super(QtGui.QTreeWidgetItem, self).__lt__(other) @@ -213,30 +237,12 @@ class Ui_SubscriptionWidget(Ui_AddressWidget, AccountMixin): def setLabel(self, label): self.label = label + + def _getLabel(self): + return unicode(self.label, 'utf-8)') def setType(self): - self.type = "subscription" - - def data(self, column, role): - if column == 0: - if role == QtCore.Qt.DisplayRole: - if self.unreadCount > 0 and not self.isExpanded(): - return unicode(self.label, 'utf-8)') + ' (' + str(self.unreadCount) + ') (' + self.address + ')' - else: - return unicode(self.label, 'utf-8)') + ' (' + self.address + ')' - elif role == QtCore.Qt.EditRole: - return unicode(self.label, 'utf-8') - elif role == QtCore.Qt.ToolTipRole: - return unicode(self.label, 'utf-8)') + ' (' + self.address + ')' - elif role == QtCore.Qt.DecorationRole: - return avatarize(self.address) - elif role == QtCore.Qt.FontRole: - font = QtGui.QFont() - font.setBold(self.unreadCount > 0) - return font - elif role == QtCore.Qt.ForegroundRole: - return self.accountBrush() - return super(Ui_SubscriptionWidget, self).data(column, role) + self.type = self.SUBSCRIPTION def setData(self, column, role, value): if role == QtCore.Qt.EditRole: @@ -251,39 +257,13 @@ class Ui_SubscriptionWidget(Ui_AddressWidget, AccountMixin): if not self.initialised: return self.emitDataChanged() - - # label (or address) alphabetically, disabled at the end - def __lt__(self, other): - if (isinstance(other, Ui_SubscriptionWidget)): - reverse = False - if self.treeWidget().header().sortIndicatorOrder() == QtCore.Qt.DescendingOrder: - reverse = True - if self.isEnabled == other.isEnabled: - if self.label: - x = self.label.decode('utf-8').lower() - else: - x = self.address.decode('utf-8').lower() - if other.label: - y = other.label.decode('utf-8').lower() - else: - y = other.address.decode('utf-8').lower() - return x < y -# else: - return (not reverse if self.isEnabled else reverse) - - return super(QtGui.QTreeWidgetItem, self).__lt__(other) + class Ui_AddressBookWidgetItem(QtGui.QTableWidgetItem, AccountMixin): - _types = {'normal': 0, 'chan': 1, 'subscription': 2} - - def __init__ (self, text, type = 'normal'): + def __init__ (self, text, type = AccountMixin.NORMAL): super(QtGui.QTableWidgetItem, self).__init__(text) self.label = text self.type = type - try: - self.typeNum = self._types[self.type] - except: - self.type = 0 self.setEnabled(True) self.setForeground(self.accountBrush()) @@ -292,10 +272,10 @@ class Ui_AddressBookWidgetItem(QtGui.QTableWidgetItem, AccountMixin): reverse = False if self.tableWidget().horizontalHeader().sortIndicatorOrder() == QtCore.Qt.DescendingOrder: reverse = True - if self.typeNum == other.typeNum: + if self.type == other.type: return self.label.decode('utf-8').lower() < other.label.decode('utf-8').lower() else: - return (not reverse if self.typeNum < other.typeNum else reverse) + return (not reverse if self.type < other.type else reverse) return super(QtGui.QTableWidgetItem, self).__lt__(other) From 8356ed76d62c3ea130520f4f059d0d9388c3f8b2 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Thu, 26 Nov 2015 23:45:44 +0100 Subject: [PATCH 192/210] DNS bootstrap over Tor If proxy type is SOCKS5, it will try to perform DNS bootstrap using the Tor RESOLVE extension. --- src/helper_bootstrap.py | 60 +++++++++++++++++++++++++++++++---------- src/socks/__init__.py | 50 ++++++++++++++++++++++++++++++++-- 2 files changed, 94 insertions(+), 16 deletions(-) diff --git a/src/helper_bootstrap.py b/src/helper_bootstrap.py index ae1adacb..d0de8622 100644 --- a/src/helper_bootstrap.py +++ b/src/helper_bootstrap.py @@ -5,6 +5,7 @@ import pickle import time from debug import logger +import socks def knownNodes(): try: @@ -37,20 +38,51 @@ def dns(): # 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. - with shared.printLock: - if shared.config.get('bitmessagesettings', 'socksproxytype') == 'none': + if shared.config.get('bitmessagesettings', 'socksproxytype') == 'none': + try: + for item in socket.getaddrinfo('bootstrap8080.bitmessage.org', 80): + logger.info('Adding ' + item[4][0] + ' to knownNodes based on DNS bootstrap method') + shared.knownNodes[1][shared.Peer(item[4][0], 8080)] = int(time.time()) + except: + logger.error('bootstrap8080.bitmessage.org DNS bootstrapping failed.') + try: + for item in socket.getaddrinfo('bootstrap8444.bitmessage.org', 80): + logger.info ('Adding ' + item[4][0] + ' to knownNodes based on DNS bootstrap method') + shared.knownNodes[1][shared.Peer(item[4][0], 8444)] = int(time.time()) + except: + logger.error('bootstrap8444.bitmessage.org DNS bootstrapping failed.') + elif shared.config.get('bitmessagesettings', 'socksproxytype') == 'SOCKS5': + 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 = shared.config.get( + 'bitmessagesettings', 'sockshostname') + socksport = shared.config.getint( + 'bitmessagesettings', 'socksport') + rdns = True # 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. + if shared.config.getboolean('bitmessagesettings', 'socksauthentication'): + socksusername = shared.config.get( + 'bitmessagesettings', 'socksusername') + sockspassword = shared.config.get( + 'bitmessagesettings', 'sockspassword') + sock.setproxy( + proxytype, sockshostname, socksport, rdns, socksusername, sockspassword) + else: + sock.setproxy( + proxytype, sockshostname, socksport, rdns) try: - for item in socket.getaddrinfo('bootstrap8080.bitmessage.org', 80): - logger.info('Adding ' + item[4][0] + ' to knownNodes based on DNS bootstrap method') - shared.knownNodes[1][shared.Peer(item[4][0], 8080)] = int(time.time()) + ip = sock.resolve("bootstrap" + str(port) + ".bitmessage.org") + sock.shutdown(socket.SHUT_RDWR) + sock.close() except: - logger.error('bootstrap8080.bitmessage.org DNS bootstrapping failed.') - try: - for item in socket.getaddrinfo('bootstrap8444.bitmessage.org', 80): - logger.info ('Adding ' + item[4][0] + ' to knownNodes based on DNS bootstrap method') - shared.knownNodes[1][shared.Peer(item[4][0], 8444)] = int(time.time()) - except: - logger.error('bootstrap8444.bitmessage.org DNS bootstrapping failed.') - else: - logger.info('DNS bootstrap skipped because SOCKS is used.') + logger.error("SOCKS DNS resolving failed", exc_info=True) + if ip is not None: + logger.info ('Adding ' + ip + ' to knownNodes based on SOCKS DNS bootstrap method') + shared.knownNodes[1][shared.Peer(ip, port)] = time.time() + else: + logger.info('DNS bootstrap skipped because the proxy type does not support DNS resolution.') diff --git a/src/socks/__init__.py b/src/socks/__init__.py index ebb68f45..d5d59687 100644 --- a/src/socks/__init__.py +++ b/src/socks/__init__.py @@ -155,7 +155,7 @@ class socksocket(socket.socket): """ self.__proxy = (proxytype, addr, port, rdns, username, password) - def __negotiatesocks5(self, destaddr, destport): + def __negotiatesocks5(self): """__negotiatesocks5(self,destaddr,destport) Negotiates a connection through a SOCKS5 server. """ @@ -200,6 +200,8 @@ class socksocket(socket.socket): 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 @@ -247,6 +249,37 @@ class socksocket(socket.socket): 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. @@ -361,7 +394,8 @@ class socksocket(socket.socket): else: portnum = 1080 _orgsocket.connect(self, (self.__proxy[1], portnum)) - self.__negotiatesocks5(destpair[0], destpair[1]) + self.__negotiatesocks5() + self.__connectsocks5(destpair[0], destpair[1]) elif self.__proxy[0] == PROXY_TYPE_SOCKS4: if self.__proxy[2] != None: portnum = self.__proxy[2] @@ -380,3 +414,15 @@ class socksocket(socket.socket): _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 \ No newline at end of file From 41e65d459b814752de32f82f397fd9b186b09022 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Fri, 27 Nov 2015 00:37:44 +0100 Subject: [PATCH 193/210] Allow SQL arguments as a list or tuple --- src/helper_sql.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/helper_sql.py b/src/helper_sql.py index 0353f9ae..c50db192 100644 --- a/src/helper_sql.py +++ b/src/helper_sql.py @@ -11,6 +11,8 @@ def sqlQuery(sqlStatement, *args): if args == (): sqlSubmitQueue.put('') + elif type(args[0]) in [list, tuple]: + sqlSubmitQueue.put(args[0]) else: sqlSubmitQueue.put(args) From 5500041a0c52b60f53ed486d2644d42f1e680a9f Mon Sep 17 00:00:00 2001 From: mailchuck Date: Fri, 27 Nov 2015 00:44:14 +0100 Subject: [PATCH 194/210] Typo OpenCL config variable setter had a typo --- src/bitmessageqt/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index f3715b0c..38d19f62 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2632,8 +2632,8 @@ class MyForm(settingsmixin.SMainWindow): shared.config.set('bitmessagesettings', 'defaultpayloadlengthextrabytes', str(int(float( self.settingsDialogInstance.ui.lineEditSmallMessageDifficulty.text()) * shared.networkDefaultPayloadLengthExtraBytes))) - if openclpow.has_opencl() and self.settingsDialogInstance.ui.checkBoxOpenCL.isChecked != shared.safeConfigGetBoolean("bitmessagesettings", "opencl"): - shared.config.set('bitmessagesettings', 'opencl', str(self.settingsDialogInstance.ui.checkBoxOpenCL.isChecked)) + if openclpow.has_opencl() and self.settingsDialogInstance.ui.checkBoxOpenCL.isChecked() != shared.safeConfigGetBoolean("bitmessagesettings", "opencl"): + shared.config.set('bitmessagesettings', 'opencl', str(self.settingsDialogInstance.ui.checkBoxOpenCL.isChecked())) acceptableDifficultyChanged = False From 3fbd3f9cb4bb74aa3ab44bc918f099341812601a Mon Sep 17 00:00:00 2001 From: mailchuck Date: Fri, 27 Nov 2015 00:56:25 +0100 Subject: [PATCH 195/210] Global folder, global search, unread folder Fixes #38 Fixes #39 Minor unread refresh issues. --- src/bitmessageqt/__init__.py | 124 ++++++++++++++++++++------------- src/bitmessageqt/foldertree.py | 15 ++-- 2 files changed, 84 insertions(+), 55 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 38d19f62..10e601cd 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -378,7 +378,8 @@ class MyForm(settingsmixin.SMainWindow): def rerenderTabTreeSubscriptions(self): treeWidget = self.ui.treeWidgetSubscriptions - folders = ['inbox', 'sent', 'trash'] + folders = Ui_FolderWidget.folderWeight.keys() + folders.remove("new") # sort ascending when creating if treeWidget.topLevelItemCount() == 0: @@ -464,8 +465,8 @@ class MyForm(settingsmixin.SMainWindow): treeWidget = self.ui.treeWidgetYourIdentities elif tab == 'chan': treeWidget = self.ui.treeWidgetChans - folders = ['inbox', 'sent', 'trash'] - + folders = Ui_FolderWidget.folderWeight.keys() + # sort ascending when creating if treeWidget.topLevelItemCount() == 0: treeWidget.header().setSortIndicator(0, Qt.AscendingOrder) @@ -495,11 +496,18 @@ class MyForm(settingsmixin.SMainWindow): enabled[toAddress] = isEnabled # get number of (unread) messages + total = 0 queryreturn = sqlQuery('SELECT toaddress, folder, count(msgid) as cnt FROM inbox WHERE read = 0 GROUP BY toaddress, folder') for row in queryreturn: toaddress, folder, cnt = row + total += cnt if toaddress in db and folder in db[toaddress]: db[toaddress][folder] = cnt + if tab == "messages": + db[None] = {} + db[None]["inbox"] = total + db[None]["new"] = total + enabled[None] = True if treeWidget.isSortingEnabled(): treeWidget.setSortingEnabled(False) @@ -547,6 +555,10 @@ class MyForm(settingsmixin.SMainWindow): j = 0 unread = 0 for folder in folders: + if toAddress is not None and folder == "new": + continue + if toAddress is None and folder in ["trash", "sent"]: + continue subwidget = Ui_FolderWidget(widget, j, toAddress, folder, db[toAddress][folder]) unread += db[toAddress][folder] j += 1 @@ -915,22 +927,25 @@ class MyForm(settingsmixin.SMainWindow): if isinstance(item, Ui_AddressWidget): self.drawTrayIcon(self.currentTrayIconFileName, self.findInboxUnreadCount(self.unreadCount -1)) - if widget == None: + if widget == None or self.getCurrentAccount() == None: widgets = [self.ui.treeWidgetYourIdentities, self.ui.treeWidgetSubscriptions, self.ui.treeWidgetChans] else: widgets = [widget] + # FIXME this is a hack + if folder == "new": + folder = "inbox" for treeWidget in widgets: root = treeWidget.invisibleRootItem() for i in range(root.childCount()): addressItem = root.child(i) - if address is not None and addressItem.data(0, QtCore.Qt.UserRole) != address: + if addressItem.type != AccountMixin.ALL and address is not None and addressItem.data(0, QtCore.Qt.UserRole) != address: continue updateUnreadCount(addressItem) if addressItem.childCount == 0: continue for j in range(addressItem.childCount()): folderItem = addressItem.child(j) - if folder is not None and folderItem.folderName != folder: + if folder is not None and folderItem.folderName != folder and addressItem.type != AccountMixin.ALL: continue updateUnreadCount(folderItem) @@ -1053,46 +1068,56 @@ class MyForm(settingsmixin.SMainWindow): tableWidget.keyPressEvent = self.tableWidgetSentKeyPressEvent # Load messages from database file - def loadMessagelist(self, tableWidget, account, folder="inbox", where="", what=""): + def loadMessagelist(self, tableWidget, account, folder="inbox", where="", what="", unreadOnly = False): if folder == 'sent': self.loadSent(tableWidget, account, where, what) return - what = "%" + what + "%" - if where == _translate("MainWindow", "To"): - where = "toaddress" - elif where == _translate("MainWindow", "From"): - where = "fromaddress" - elif where == _translate("MainWindow", "Subject"): - where = "subject" - elif where == _translate("MainWindow", "Message"): - where = "message" + if what != "": + what = "%" + what + "%" + if where == _translate("MainWindow", "To"): + where = "toaddress" + elif where == _translate("MainWindow", "From"): + where = "fromaddress" + elif where == _translate("MainWindow", "Subject"): + where = "subject" + elif where == _translate("MainWindow", "Message"): + where = "message" + else: + where = "toaddress || fromaddress || subject || message" else: - where = "toaddress || fromaddress || subject || message" + what = None if tableWidget == self.ui.tableWidgetInboxSubscriptions: xAddress = "fromaddress" else: xAddress = "toaddress" + sqlStatementBase = '''SELECT folder, msgid, toaddress, fromaddress, subject, received, read + FROM inbox ''' + sqlStatementParts = [] + sqlArguments = [] + if account is not None: + sqlStatementParts.append(xAddress + " = ? ") + sqlArguments.append(account) if folder is not None: - sqlStatement = ''' - SELECT folder, msgid, toaddress, fromaddress, subject, received, read - FROM inbox WHERE ''' + xAddress + '''=? AND folder=? AND %s LIKE ? - ORDER BY received - ''' % (where) - queryreturn = sqlQuery(sqlStatement, account, folder, what) - else: - sqlStatement = ''' - SELECT folder, msgid, toaddress, fromaddress, subject, received, read - FROM inbox WHERE ''' + xAddress + '''=? AND folder != "trash" AND %s LIKE ? - ORDER BY received - ''' % (where) - queryreturn = sqlQuery(sqlStatement, account, what) + sqlStatementParts.append("folder = ? ") + sqlArguments.append(folder) + if what is not None: + sqlStatementParts.append("%s LIKE ?" % (where)) + sqlArguments.append(what) + if unreadOnly: + sqlStatementParts.append("read = 0") + if len(sqlStatementParts) > 0: + sqlStatementBase += "WHERE " + " AND ".join(sqlStatementParts) + queryreturn = sqlQuery(sqlStatementBase, sqlArguments) tableWidget.setRowCount(0) - - tableWidget.setColumnHidden(0, True) - tableWidget.setColumnHidden(1, False) + if account is not None: + tableWidget.setColumnHidden(0, True) + tableWidget.setColumnHidden(1, False) + else: + tableWidget.setColumnHidden(0, False) + tableWidget.setColumnHidden(1, False) tableWidget.setSortingEnabled(False) font = QFont() @@ -1912,7 +1937,7 @@ class MyForm(settingsmixin.SMainWindow): self.statusBar().showMessage(_translate( "MainWindow", "Message trashed")) treeWidget = self.widgetConvert(inbox) - self.propagateUnreadCount(self.getCurrentAccount(treeWidget), self.getCurrentFolder(treeWidget), treeWidget, 0) + self.propagateUnreadCount(str(inbox.item(i, 1 if inbox == self.ui.tableWidgetInboxSubscriptions else 0).data(Qt.UserRole).toPyObject()), self.getCurrentFolder(treeWidget), treeWidget, 0) inbox.removeRow(i) break @@ -2421,7 +2446,7 @@ class MyForm(settingsmixin.SMainWindow): self.propagateUnreadCount(acct.address) if shared.config.getboolean('bitmessagesettings', 'showtraynotifications'): self.notifierShow(unicode(_translate("MainWindow",'New Message').toUtf8(),'utf-8'), unicode(_translate("MainWindow",'From ').toUtf8(),'utf-8') + unicode(acct.fromLabel, 'utf-8'), self.SOUND_UNKNOWN, None) - if (self.getCurrentFolder(treeWidget) != "inbox" and self.getCurrentFolder(treeWidget) is not None) or self.getCurrentAccount(treeWidget) != acct.address: + if self.getCurrentAccount() is not None and ((self.getCurrentFolder(treeWidget) != "inbox" and self.getCurrentFolder(treeWidget) is not None) or self.getCurrentAccount(treeWidget) != acct.address): # Ubuntu should notify of new message irespective of whether it's in current message list or not self.ubuntuMessagingMenuUpdate(True, None, acct.toLabel) return @@ -3008,9 +3033,9 @@ class MyForm(settingsmixin.SMainWindow): "?," * len(inventoryHashesToMarkUnread))[:-1], *inventoryHashesToMarkUnread) if modified == 1: # performance optimisation - self.propagateUnreadCount(self.getCurrentAccount(), self.getCurrentFolder()) + self.propagateUnreadCount(str(tableWidget.item(currentRow, 1 if tableWidget == self.ui.tableWidgetInboxSubscriptions else 0).data(Qt.UserRole).toPyObject()), self.getCurrentFolder()) else: - self.propagateUnreadCount(self.getCurrentAccount(), self.getCurrentFolder(), self.getCurrentTreeWidget(), 0) + self.propagateUnreadCount(str(tableWidget.item(currentRow, 1 if tableWidget == self.ui.tableWidgetInboxSubscriptions else 0).data(Qt.UserRole).toPyObject()), self.getCurrentFolder(), self.getCurrentTreeWidget(), 0) # tableWidget.selectRow(currentRow + 1) # This doesn't de-select the last message if you try to mark it unread, but that doesn't interfere. Might not be necessary. # We could also select upwards, but then our problem would be with the topmost message. @@ -3171,7 +3196,10 @@ class MyForm(settingsmixin.SMainWindow): else: sqlExecute('''UPDATE inbox SET folder='trash' WHERE msgid=?''', inventoryHashToTrash) if tableWidget.item(currentRow, 0).font().bold(): - unread = True + self.propagateUnreadCount(str(tableWidget.item(currentRow, 1 if tableWidget == self.ui.tableWidgetInboxSubscriptions else 0).data(Qt.UserRole).toPyObject()), folder, self.getCurrentTreeWidget(), -1) + if folder != "trash" and not shifted: + self.propagateUnreadCount(str(tableWidget.item(currentRow, 1 if tableWidget == self.ui.tableWidgetInboxSubscriptions else 0).data(Qt.UserRole).toPyObject()), "trash", self.getCurrentTreeWidget(), 1) + self.getCurrentMessageTextedit().setText("") tableWidget.removeRow(currentRow) self.statusBar().showMessage(_translate( @@ -3180,8 +3208,6 @@ class MyForm(settingsmixin.SMainWindow): tableWidget.selectRow(currentRow) else: tableWidget.selectRow(currentRow - 1) - if unread: - self.propagateUnreadCount(self.getCurrentAccount(), None, self.getCurrentTreeWidget(), 0) def on_action_TrashUndelete(self): tableWidget = self.getCurrentMessagelist() @@ -3195,7 +3221,8 @@ class MyForm(settingsmixin.SMainWindow): currentRow, 3).data(Qt.UserRole).toPyObject()) sqlExecute('''UPDATE inbox SET folder='inbox' WHERE msgid=?''', inventoryHashToTrash) if tableWidget.item(currentRow, 0).font().bold(): - unread = True + self.propagateUnreadCount(str(tableWidget.item(currentRow, 1 if tableWidget == self.ui.tableWidgetInboxSubscriptions else 0).data(Qt.UserRole).toPyObject()), "inbox", self.getCurrentTreeWidget(), 1) + self.propagateUnreadCount(str(tableWidget.item(currentRow, 1 if tableWidget == self.ui.tableWidgetInboxSubscriptions else 0).data(Qt.UserRole).toPyObject()), "trash", self.getCurrentTreeWidget(), -1) self.getCurrentMessageTextedit().setText("") tableWidget.removeRow(currentRow) self.statusBar().showMessage(_translate( @@ -3204,8 +3231,6 @@ class MyForm(settingsmixin.SMainWindow): tableWidget.selectRow(currentRow) else: tableWidget.selectRow(currentRow - 1) - if unread: - self.propagateUnreadCount(self.getCurrentAccount(), None, self.getCurrentTreeWidget(), 0) def on_action_InboxSaveMessageAs(self): tableWidget = self.getCurrentMessagelist() @@ -3256,7 +3281,7 @@ class MyForm(settingsmixin.SMainWindow): else: sqlExecute('''UPDATE sent SET folder='trash' WHERE ackdata=?''', ackdataToTrash) if tableWidget.item(currentRow, 0).font().bold(): - unread = True + self.propagateUnreadCount(str(tableWidget.item(currentRow, 1 if tableWidget == self.ui.tableWidgetInboxSubscriptions else 0).data(Qt.UserRole).toPyObject()), folder, self.getCurrentTreeWidget(), -1) self.getCurrentMessageTextedit().setPlainText("") tableWidget.removeRow(currentRow) self.statusBar().showMessage(_translate( @@ -3265,8 +3290,6 @@ class MyForm(settingsmixin.SMainWindow): self.ui.tableWidgetInbox.selectRow(currentRow) else: self.ui.tableWidgetInbox.selectRow(currentRow - 1) - if unread: - self.propagateUnreadCount(self.getCurrentAccount(), None, self.getCurrentTreeWidget(), 0) def on_action_ForceSend(self): currentRow = self.ui.tableWidgetInbox.currentRow() @@ -3631,7 +3654,7 @@ class MyForm(settingsmixin.SMainWindow): return currentItem return False - def getCurrentAccount(self, treeWidget = None): + def getCurrentAccount(self, treeWidget = None, force = None): currentItem = self.getCurrentItem(treeWidget) if currentItem: account = currentItem.address @@ -3896,7 +3919,10 @@ class MyForm(settingsmixin.SMainWindow): if messagelist: account = self.getCurrentAccount() folder = self.getCurrentFolder() - self.loadMessagelist(messagelist, account, folder) + if folder == "new": + self.loadMessagelist(messagelist, account, None, unreadOnly = True) + else: + self.loadMessagelist(messagelist, account, folder) def treeWidgetItemChanged(self, item, column): # only for manual edits. automatic edits (setText) are ignored @@ -3978,7 +4004,7 @@ class MyForm(settingsmixin.SMainWindow): tableWidget.item(currentRow, 1).setTextColor(AccountColor(str(tableWidget.item(currentRow, 1).data(Qt.UserRole).toPyObject())).accountColor()) tableWidget.item(currentRow, 2).setFont(font) tableWidget.item(currentRow, 3).setFont(font) - self.propagateUnreadCount(self.getCurrentAccount(), folder, self.getCurrentTreeWidget(), -1) + self.propagateUnreadCount(str(tableWidget.item(currentRow, 1 if tableWidget == self.ui.tableWidgetInboxSubscriptions else 0).data(Qt.UserRole).toPyObject()), folder, self.getCurrentTreeWidget(), -1) else: data = self.getCurrentMessageId() diff --git a/src/bitmessageqt/foldertree.py b/src/bitmessageqt/foldertree.py index 9b01b9c3..03477218 100644 --- a/src/bitmessageqt/foldertree.py +++ b/src/bitmessageqt/foldertree.py @@ -39,7 +39,10 @@ class AccountMixin (object): return brush def setAddress(self, address): - self.address = str(address) + if address is None: + self.address = None + else: + self.address = str(address) self.updateText() def setUnreadCount(self, cnt): @@ -71,7 +74,7 @@ class AccountMixin (object): class Ui_FolderWidget(QtGui.QTreeWidgetItem, AccountMixin): - folderWeight = {"inbox": 1, "sent": 2, "trash": 3} + folderWeight = {"inbox": 1, "new": 2, "sent": 3, "trash": 4} def __init__(self, parent, pos = 0, address = "", folderName = "", unreadCount = 0): super(QtGui.QTreeWidgetItem, self).__init__() self.initialised = False @@ -110,11 +113,11 @@ class Ui_FolderWidget(QtGui.QTreeWidgetItem, AccountMixin): if self.folderName in self.folderWeight: x = self.folderWeight[self.folderName] else: - x = 4 + x = 99 if other.folderName in self.folderWeight: y = self.folderWeight[other.folderName] else: - y = 4 + y = 99 reverse = False if self.treeWidget().header().sortIndicatorOrder() == QtCore.Qt.DescendingOrder: reverse = True @@ -141,12 +144,12 @@ class Ui_AddressWidget(QtGui.QTreeWidgetItem, AccountMixin, SettingsMixin): def _getLabel(self): if self.address is None: - label = QtGui.QApplication.translate("MainWindow", "All accounts") + return unicode(str(QtGui.QApplication.translate("MainWindow", "All accounts")), 'utf-8') else: try: return unicode(shared.config.get(self.address, 'label'), 'utf-8)') except: - return self.address + return unicode(self.address, 'utf-8') def _getAddressBracket(self, unreadCount = False): ret = "" From 64ee159ae35ce3b86999e8764c40553c8564310d Mon Sep 17 00:00:00 2001 From: mailchuck Date: Fri, 27 Nov 2015 02:10:27 +0100 Subject: [PATCH 196/210] Add BROADCAST type also improve sorting --- src/bitmessageqt/account.py | 24 +++++++++++++----------- src/bitmessageqt/foldertree.py | 3 ++- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/bitmessageqt/account.py b/src/bitmessageqt/account.py index 83ce76c9..f881cc8c 100644 --- a/src/bitmessageqt/account.py +++ b/src/bitmessageqt/account.py @@ -42,11 +42,11 @@ def accountClass(address): if not shared.config.has_section(address): if address == str_broadcast_subscribers: subscription = BroadcastAccount(address) - if subscription.type != 'broadcast': + if subscription.type != AccountMixin.BROADCAST: return None else: subscription = SubscriptionAccount(address) - if subscription.type != 'subscription': + if subscription.type != AccountMixin.SUBSCRIPTION: return None return subscription try: @@ -67,15 +67,17 @@ class AccountColor(AccountMixin): self.isEnabled = True self.address = address if type is None: - if shared.safeConfigGetBoolean(self.address, 'mailinglist'): - self.type = "mailinglist" + if address is None: + self.type = AccountMixin.ALL + elif shared.safeConfigGetBoolean(self.address, 'mailinglist'): + self.type = AccountMixin.MAILINGLIST elif shared.safeConfigGetBoolean(self.address, 'chan'): - self.type = "chan" + self.type = AccountMixin.CHAN elif sqlQuery( '''select label from subscriptions where address=?''', self.address): - self.type = 'subscription' + self.type = AccountMixin.SUBSCRIPTION else: - self.type = "normal" + self.type = AccountMixin.NORMAL else: self.type = type @@ -86,16 +88,16 @@ class BMAccount(object): self.type = 'normal' if shared.config.has_section(address): if shared.safeConfigGetBoolean(self.address, 'chan'): - self.type = "chan" + self.type = AccountMixin.CHAN elif shared.safeConfigGetBoolean(self.address, 'mailinglist'): - self.type = "mailinglist" + self.type = AccountMixin.MAILINGLIST elif self.address == str_broadcast_subscribers: - self.type = 'broadcast' + self.type = AccountMixin.BROADCAST else: queryreturn = sqlQuery( '''select label from subscriptions where address=?''', self.address) if queryreturn: - self.type = 'subscription' + self.type = AccountMixin.SUBSCRIPTION def getLabel(self, address = None): if address is None: diff --git a/src/bitmessageqt/foldertree.py b/src/bitmessageqt/foldertree.py index 03477218..971cda61 100644 --- a/src/bitmessageqt/foldertree.py +++ b/src/bitmessageqt/foldertree.py @@ -11,6 +11,7 @@ class AccountMixin (object): CHAN = 2 MAILINGLIST = 3 SUBSCRIPTION = 4 + BROADCAST = 5 def accountColor (self): if not self.isEnabled: @@ -206,7 +207,7 @@ class Ui_AddressWidget(QtGui.QTreeWidgetItem, AccountMixin, SettingsMixin): def _getSortRank(self): ret = self.type if not self.isEnabled: - ret += 5 + ret += 100 return ret # label (or address) alphabetically, disabled at the end From 26fb92e98c3394448f378e3194ca6c497cbb26a0 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Fri, 27 Nov 2015 02:11:24 +0100 Subject: [PATCH 197/210] Unify messagelist rendering also some minor fixes --- src/bitmessageqt/__init__.py | 362 +++++++++++++++-------------------- 1 file changed, 159 insertions(+), 203 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 10e601cd..659508e1 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -927,10 +927,7 @@ class MyForm(settingsmixin.SMainWindow): if isinstance(item, Ui_AddressWidget): self.drawTrayIcon(self.currentTrayIconFileName, self.findInboxUnreadCount(self.unreadCount -1)) - if widget == None or self.getCurrentAccount() == None: - widgets = [self.ui.treeWidgetYourIdentities, self.ui.treeWidgetSubscriptions, self.ui.treeWidgetChans] - else: - widgets = [widget] + widgets = [self.ui.treeWidgetYourIdentities, self.ui.treeWidgetSubscriptions, self.ui.treeWidgetChans] # FIXME this is a hack if folder == "new": folder = "inbox" @@ -949,6 +946,146 @@ class MyForm(settingsmixin.SMainWindow): continue updateUnreadCount(folderItem) + def addMessageListItem(self, tableWidget, items): + tableWidget.insertRow(0) + for i in range(len(items)): + tableWidget.setItem(0, i, items[i]) + + def addMessageListItemSent(self, tableWidget, toAddress, fromAddress, subject, status, ackdata, lastactiontime): + subject = shared.fixPotentiallyInvalidUTF8Data(subject) + acct = accountClass(fromAddress) + acct.parseMessage(toAddress, fromAddress, subject, "") + + items = [] + toAddressItem = QtGui.QTableWidgetItem(unicode(acct.toLabel, 'utf-8')) + toAddressItem.setToolTip(unicode(acct.toLabel, 'utf-8') + " (" + str(acct.toAddress) + ")") + toAddressItem.setIcon(avatarize(toAddress)) + toAddressItem.setData(Qt.UserRole, str(toAddress)) + toAddressItem.setTextColor(AccountColor(toAddress).accountColor()) + toAddressItem.setFlags( + QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) + items.append(toAddressItem) + + fromAddressItem = QtGui.QTableWidgetItem(unicode(acct.fromLabel, 'utf-8')) + fromAddressItem.setToolTip(unicode(acct.fromLabel, 'utf-8') + " (" + str(acct.fromAddress) + ")") + fromAddressItem.setIcon(avatarize(fromAddress)) + fromAddressItem.setData(Qt.UserRole, str(fromAddress)) + fromAddressItem.setTextColor(AccountColor(fromAddress).accountColor()) + fromAddressItem.setFlags( + QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) + items.append(fromAddressItem) + + subjectItem = QtGui.QTableWidgetItem(unicode(acct.subject, 'utf-8')) + subjectItem.setToolTip(unicode(acct.subject, 'utf-8')) + subjectItem.setData(Qt.UserRole, str(subject)) + subjectItem.setFlags( + QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) + items.append(subjectItem) + + if status == 'awaitingpubkey': + statusText = _translate( + "MainWindow", "Waiting for their encryption key. Will request it again soon.") + elif status == 'doingpowforpubkey': + statusText = _translate( + "MainWindow", "Encryption key request queued.") + elif status == 'msgqueued': + statusText = _translate( + "MainWindow", "Queued.") + elif status == 'msgsent': + statusText = _translate("MainWindow", "Message sent. Waiting for acknowledgement. Sent at %1").arg( + l10n.formatTimestamp(lastactiontime)) + elif status == 'msgsentnoackexpected': + statusText = _translate("MainWindow", "Message sent. Sent at %1").arg( + l10n.formatTimestamp(lastactiontime)) + elif status == 'doingmsgpow': + statusText = _translate( + "MainWindow", "Need to do work to send message. Work is queued.") + elif status == 'ackreceived': + statusText = _translate("MainWindow", "Acknowledgement of the message received %1").arg( + l10n.formatTimestamp(lastactiontime)) + elif status == 'broadcastqueued': + statusText = _translate( + "MainWindow", "Broadcast queued.") + elif status == 'broadcastsent': + statusText = _translate("MainWindow", "Broadcast on %1").arg( + l10n.formatTimestamp(lastactiontime)) + elif status == 'toodifficult': + statusText = _translate("MainWindow", "Problem: The work demanded by the recipient is more difficult than you are willing to do. %1").arg( + l10n.formatTimestamp(lastactiontime)) + elif status == 'badkey': + statusText = _translate("MainWindow", "Problem: The recipient\'s encryption key is no good. Could not encrypt message. %1").arg( + l10n.formatTimestamp(lastactiontime)) + elif status == 'forcepow': + statusText = _translate( + "MainWindow", "Forced difficulty override. Send should start soon.") + else: + statusText = _translate("MainWindow", "Unknown status: %1 %2").arg(status).arg( + l10n.formatTimestamp(lastactiontime)) + newItem = myTableWidgetItem(statusText) + newItem.setToolTip(statusText) + newItem.setData(Qt.UserRole, QByteArray(ackdata)) + newItem.setData(33, int(lastactiontime)) + newItem.setFlags( + QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) + items.append(newItem) + self.addMessageListItem(tableWidget, items) + return acct + + def addMessageListItemInbox(self, tableWidget, msgfolder, msgid, toAddress, fromAddress, subject, received, read): + font = QFont() + font.setBold(True) + if tableWidget == self.ui.tableWidgetInboxSubscriptions: + acct = accountClass(fromAddress) + else: + acct = accountClass(toAddress) + subject = shared.fixPotentiallyInvalidUTF8Data(subject) + acct.parseMessage(toAddress, fromAddress, subject, "") + + items = [] + #to + to_item = QtGui.QTableWidgetItem(unicode(acct.toLabel, 'utf-8')) + to_item.setToolTip(unicode(acct.toLabel, 'utf-8') + " (" + str(acct.toAddress) + ")") + to_item.setFlags( + QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) + if not read: + to_item.setFont(font) + to_item.setData(Qt.UserRole, str(toAddress)) + to_item.setTextColor(AccountColor(toAddress).accountColor()) + to_item.setIcon(avatarize(toAddress)) + items.append(to_item) + # from + from_item = QtGui.QTableWidgetItem(unicode(acct.fromLabel, 'utf-8')) + from_item.setToolTip(unicode(acct.fromLabel, 'utf-8') + " (" + str(fromAddress) + ")") + from_item.setFlags( + QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) + if not read: + from_item.setFont(font) + from_item.setData(Qt.UserRole, str(fromAddress)) + from_item.setTextColor(AccountColor(fromAddress).accountColor()) + from_item.setIcon(avatarize(fromAddress)) + items.append(from_item) + # subject + subject_item = QtGui.QTableWidgetItem(unicode(acct.subject, 'utf-8')) + subject_item.setToolTip(unicode(acct.subject, 'utf-8')) + subject_item.setData(Qt.UserRole, str(subject)) + subject_item.setFlags( + QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) + if not read: + subject_item.setFont(font) + items.append(subject_item) + # time received + time_item = myTableWidgetItem(l10n.formatTimestamp(received)) + time_item.setToolTip(l10n.formatTimestamp(received)) + time_item.setData(Qt.UserRole, QByteArray(msgid)) + time_item.setData(33, int(received)) + time_item.setFlags( + QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) + if not read: + time_item.setFont(font) + items.append(time_item) + self.addMessageListItem(tableWidget, items) + return acct + # Load Sent items from database def loadSent(self, tableWidget, account, where="", what=""): what = "%" + what + "%" @@ -985,83 +1122,7 @@ class MyForm(settingsmixin.SMainWindow): queryreturn = sqlQuery(sqlStatement, account, what) for row in queryreturn: toAddress, fromAddress, subject, status, ackdata, lastactiontime = row - subject = shared.fixPotentiallyInvalidUTF8Data(subject) - if acct is None: - acct = accountClass(fromAddress) - acct.parseMessage(toAddress, fromAddress, subject, "") - - tableWidget.insertRow(0) - toAddressItem = QtGui.QTableWidgetItem(unicode(acct.toLabel, 'utf-8')) - toAddressItem.setToolTip(unicode(acct.toLabel, 'utf-8') + " (" + str(acct.toAddress) + ")") - toAddressItem.setIcon(avatarize(toAddress)) - toAddressItem.setData(Qt.UserRole, str(toAddress)) - toAddressItem.setTextColor(AccountColor(toAddress).accountColor()) - toAddressItem.setFlags( - QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) - tableWidget.setItem(0, 0, toAddressItem) - - fromAddressItem = QtGui.QTableWidgetItem(unicode(acct.fromLabel, 'utf-8')) - fromAddressItem.setToolTip(unicode(acct.fromLabel, 'utf-8') + " (" + str(acct.fromAddress) + ")") - fromAddressItem.setIcon(avatarize(fromAddress)) - fromAddressItem.setData(Qt.UserRole, str(fromAddress)) - fromAddressItem.setTextColor(AccountColor(fromAddress).accountColor()) - fromAddressItem.setFlags( - QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) - tableWidget.setItem(0, 1, fromAddressItem) - - subjectItem = QtGui.QTableWidgetItem(unicode(acct.subject, 'utf-8')) - subjectItem.setToolTip(unicode(acct.subject, 'utf-8')) - subjectItem.setData(Qt.UserRole, str(subject)) - subjectItem.setFlags( - QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) - tableWidget.setItem(0, 2, subjectItem) - - if status == 'awaitingpubkey': - statusText = _translate( - "MainWindow", "Waiting for their encryption key. Will request it again soon.") - elif status == 'doingpowforpubkey': - statusText = _translate( - "MainWindow", "Encryption key request queued.") - elif status == 'msgqueued': - statusText = _translate( - "MainWindow", "Queued.") - elif status == 'msgsent': - statusText = _translate("MainWindow", "Message sent. Waiting for acknowledgement. Sent at %1").arg( - l10n.formatTimestamp(lastactiontime)) - elif status == 'msgsentnoackexpected': - statusText = _translate("MainWindow", "Message sent. Sent at %1").arg( - l10n.formatTimestamp(lastactiontime)) - elif status == 'doingmsgpow': - statusText = _translate( - "MainWindow", "Need to do work to send message. Work is queued.") - elif status == 'ackreceived': - statusText = _translate("MainWindow", "Acknowledgement of the message received %1").arg( - l10n.formatTimestamp(lastactiontime)) - elif status == 'broadcastqueued': - statusText = _translate( - "MainWindow", "Broadcast queued.") - elif status == 'broadcastsent': - statusText = _translate("MainWindow", "Broadcast on %1").arg( - l10n.formatTimestamp(lastactiontime)) - elif status == 'toodifficult': - statusText = _translate("MainWindow", "Problem: The work demanded by the recipient is more difficult than you are willing to do. %1").arg( - l10n.formatTimestamp(lastactiontime)) - elif status == 'badkey': - statusText = _translate("MainWindow", "Problem: The recipient\'s encryption key is no good. Could not encrypt message. %1").arg( - l10n.formatTimestamp(lastactiontime)) - elif status == 'forcepow': - statusText = _translate( - "MainWindow", "Forced difficulty override. Send should start soon.") - else: - statusText = _translate("MainWindow", "Unknown status: %1 %2").arg(status).arg( - l10n.formatTimestamp(lastactiontime)) - newItem = myTableWidgetItem(statusText) - newItem.setToolTip(statusText) - newItem.setData(Qt.UserRole, QByteArray(ackdata)) - newItem.setData(33, int(lastactiontime)) - newItem.setFlags( - QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) - tableWidget.setItem(0, 3, newItem) + self.addMessageListItemSent(tableWidget, toAddress, fromAddress, subject, status, ackdata, lastactiontime) tableWidget.setSortingEnabled(False) tableWidget.horizontalHeader().setSortIndicator(3, Qt.DescendingOrder) @@ -1120,62 +1181,9 @@ class MyForm(settingsmixin.SMainWindow): tableWidget.setColumnHidden(1, False) tableWidget.setSortingEnabled(False) - font = QFont() - font.setBold(True) - acct = None for row in queryreturn: msgfolder, msgid, toAddress, fromAddress, subject, received, read = row - if acct is None: - if tableWidget == self.ui.tableWidgetInboxSubscriptions: - acct = accountClass(fromAddress) - else: - acct = accountClass(toAddress) - subject = shared.fixPotentiallyInvalidUTF8Data(subject) - acct.parseMessage(toAddress, fromAddress, subject, "") - - # message row - tableWidget.insertRow(0) - # to - to_item = QtGui.QTableWidgetItem(unicode(acct.toLabel, 'utf-8')) - to_item.setToolTip(unicode(acct.toLabel, 'utf-8') + " (" + str(acct.toAddress) + ")") - to_item.setFlags( - QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) - if not read: - to_item.setFont(font) - to_item.setData(Qt.UserRole, str(toAddress)) - to_item.setTextColor(AccountColor(toAddress).accountColor()) - to_item.setIcon(avatarize(toAddress)) - tableWidget.setItem(0, 0, to_item) - # from - from_item = QtGui.QTableWidgetItem(unicode(acct.fromLabel, 'utf-8')) - from_item.setToolTip(unicode(acct.fromLabel, 'utf-8') + " (" + str(fromAddress) + ")") - from_item.setFlags( - QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) - if not read: - from_item.setFont(font) - from_item.setData(Qt.UserRole, str(fromAddress)) - from_item.setTextColor(AccountColor(fromAddress).accountColor()) - from_item.setIcon(avatarize(fromAddress)) - tableWidget.setItem(0, 1, from_item) - # subject - subject_item = QtGui.QTableWidgetItem(unicode(acct.subject, 'utf-8')) - subject_item.setToolTip(unicode(acct.subject, 'utf-8')) - subject_item.setData(Qt.UserRole, str(subject)) - subject_item.setFlags( - QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) - if not read: - subject_item.setFont(font) - tableWidget.setItem(0, 2, subject_item) - # time received - time_item = myTableWidgetItem(l10n.formatTimestamp(received)) - time_item.setToolTip(l10n.formatTimestamp(received)) - time_item.setData(Qt.UserRole, QByteArray(msgid)) - time_item.setData(33, int(received)) - time_item.setFlags( - QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) - if not read: - time_item.setFont(font) - tableWidget.setItem(0, 3, time_item) + self.addMessageListItemInbox(tableWidget, msgfolder, msgid, toAddress, fromAddress, subject, received, read) tableWidget.horizontalHeader().setSortIndicator(3, Qt.DescendingOrder) tableWidget.setSortingEnabled(True) @@ -2404,45 +2412,28 @@ class MyForm(settingsmixin.SMainWindow): continue elif treeWidget in [self.ui.treeWidgetSubscriptions, self.ui.treeWidgetChans] and self.getCurrentAccount(treeWidget) != toAddress: continue - - sent.setSortingEnabled(False) - sent.insertRow(0) - newItem = QtGui.QTableWidgetItem(unicode(acct.toLabel, 'utf-8')) - newItem.setToolTip(unicode(acct.toLabel, 'utf-8') + " (" + str(acct.toAddress) + ")") - newItem.setData(Qt.UserRole, str(toAddress)) - newItem.setIcon(avatarize(toAddress)) - sent.setItem(0, 0, newItem) - newItem = QtGui.QTableWidgetItem(unicode(acct.fromLabel, 'utf-8')) - newItem.setToolTip(unicode(acct.fromLabel, 'utf-8') + " (" + str(acct.fromAddress) + ")") - newItem.setData(Qt.UserRole, str(fromAddress)) - newItem.setIcon(avatarize(fromAddress)) - sent.setItem(0, 1, newItem) - newItem = QtGui.QTableWidgetItem(unicode(acct.subject, 'utf-8)')) - newItem.setToolTip(unicode(acct.subject, 'utf-8)')) - newItem.setData(Qt.UserRole, str(subject)) - - #newItem.setData(Qt.UserRole, unicode(message, 'utf-8)')) # No longer hold the message in the table; we'll use a SQL query to display it as needed. - sent.setItem(0, 2, newItem) - # newItem = QtGui.QTableWidgetItem('Doing work necessary to send - # broadcast...'+ - # l10n.formatTimestamp()) - newItem = myTableWidgetItem(_translate("MainWindow", "Work is queued. %1").arg(l10n.formatTimestamp())) - newItem.setToolTip(_translate("MainWindow", "Work is queued. %1").arg(l10n.formatTimestamp())) - newItem.setData(Qt.UserRole, QByteArray(ackdata)) - newItem.setData(33, int(time.time())) - sent.setItem(0, 3, newItem) + + self.addMessageListItemSent(sent, toAddress, fromAddress, subject, "msgqueued", ackdata, time.time()) self.getAccountTextedit(acct).setPlainText(unicode(message, 'utf-8)')) - sent.setSortingEnabled(True) def displayNewInboxMessage(self, inventoryHash, toAddress, fromAddress, subject, message): - subject = shared.fixPotentiallyInvalidUTF8Data(subject) if toAddress == str_broadcast_subscribers: acct = accountClass(fromAddress) else: acct = accountClass(toAddress) - acct.parseMessage(toAddress, fromAddress, subject, message) inbox = self.getAccountMessagelist(acct) - treeWidget = self.getAccountTreeWidget(acct) + ret = None + for treeWidget in [self.ui.treeWidgetYourIdentities, self.ui.treeWidgetSubscriptions, self.ui.treeWidgetChans]: + tableWidget = self.widgetConvert(treeWidget) + if tableWidget == inbox and self.getCurrentAccount(treeWidget) == acct.address and self.getCurrentFolder(treeWidget) == "inbox": + ret = self.addMessageListItemInbox(inbox, "inbox", inventoryHash, toAddress, fromAddress, subject, time.time(), 0) + elif treeWidget == self.ui.treeWidgetYourIdentities and self.getCurrentAccount(treeWidget) is None: + ret = self.addMessageListItemInbox(tableWidget, "inbox", inventoryHash, toAddress, fromAddress, subject, time.time(), 0) + if ret is None: + subject = shared.fixPotentiallyInvalidUTF8Data(subject) + acct.parseMessage(toAddress, fromAddress, subject, "") + else: + acct = ret self.propagateUnreadCount(acct.address) if shared.config.getboolean('bitmessagesettings', 'showtraynotifications'): self.notifierShow(unicode(_translate("MainWindow",'New Message').toUtf8(),'utf-8'), unicode(_translate("MainWindow",'From ').toUtf8(),'utf-8') + unicode(acct.fromLabel, 'utf-8'), self.SOUND_UNKNOWN, None) @@ -2451,41 +2442,6 @@ class MyForm(settingsmixin.SMainWindow): self.ubuntuMessagingMenuUpdate(True, None, acct.toLabel) return - font = QFont() - font.setBold(True) - inbox.setSortingEnabled(False) - newItem = QtGui.QTableWidgetItem(unicode(acct.toLabel, 'utf-8')) - newItem.setToolTip(unicode(acct.toLabel, 'utf-8') + " (" + str(acct.toAddress) + ")") - newItem.setFont(font) - newItem.setData(Qt.UserRole, str(toAddress)) - newItem.setTextColor(AccountColor(toAddress).accountColor()) - inbox.insertRow(0) - newItem.setIcon(avatarize(toAddress)) - inbox.setItem(0, 0, newItem) - - newItem = QtGui.QTableWidgetItem(unicode(acct.fromLabel, 'utf-8')) - newItem.setToolTip(unicode(acct.fromLabel, 'utf-8') + " (" + str(acct.fromAddress) + ")") - newItem.setData(Qt.UserRole, str(fromAddress)) - newItem.setFont(font) - newItem.setTextColor(AccountColor(fromAddress).accountColor()) - newItem.setIcon(avatarize(fromAddress)) - inbox.setItem(0, 1, newItem) - newItem = QtGui.QTableWidgetItem(unicode(acct.subject, 'utf-8)')) - newItem.setToolTip(unicode(acct.subject, 'utf-8)')) - newItem.setData(Qt.UserRole, str(subject)) - - #newItem.setData(Qt.UserRole, unicode(message, 'utf-8)')) # No longer hold the message in the table; we'll use a SQL query to display it as needed. - newItem.setFont(font) - inbox.setItem(0, 2, newItem) - newItem = myTableWidgetItem(l10n.formatTimestamp()) - newItem.setToolTip(l10n.formatTimestamp()) - newItem.setData(Qt.UserRole, QByteArray(inventoryHash)) - newItem.setData(33, int(time.time())) - newItem.setFont(font) - inbox.setItem(0, 3, newItem) - inbox.setSortingEnabled(True) - self.ubuntuMessagingMenuUpdate(True, newItem, acct.toLabel) - def click_pushButtonAddAddressBook(self): self.AddAddressDialogInstance = AddAddressDialog(self) if self.AddAddressDialogInstance.exec_(): @@ -3531,7 +3487,7 @@ class MyForm(settingsmixin.SMainWindow): return self.ui.tableWidgetInbox elif widget == self.ui.treeWidgetSubscriptions: return self.ui.tableWidgetInboxSubscriptions - elif twidget == self.ui.treeWidgetChans: + elif widget == self.ui.treeWidgetChans: return self.ui.tableWidgetInboxChans else: return None @@ -3654,7 +3610,7 @@ class MyForm(settingsmixin.SMainWindow): return currentItem return False - def getCurrentAccount(self, treeWidget = None, force = None): + def getCurrentAccount(self, treeWidget = None): currentItem = self.getCurrentItem(treeWidget) if currentItem: account = currentItem.address From 0b53abf39cac5dd35530117d3a0faca35e27cdd8 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Fri, 27 Nov 2015 12:13:10 +0100 Subject: [PATCH 198/210] Allow bootstrap from a Tor hidden service I run a modified node as a Tor hidden service for bootstrapping node addresses, and PyBitmessage can now connect to it. --- src/helper_bootstrap.py | 2 ++ src/shared.py | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/helper_bootstrap.py b/src/helper_bootstrap.py index d0de8622..fbb1a81b 100644 --- a/src/helper_bootstrap.py +++ b/src/helper_bootstrap.py @@ -52,6 +52,8 @@ def dns(): except: logger.error('bootstrap8444.bitmessage.org DNS bootstrapping failed.') elif shared.config.get('bitmessagesettings', 'socksproxytype') == 'SOCKS5': + shared.knownNodes[1][shared.Peer('quzwelsuziwqgpt2.onion', 8444)] = int(time.time()) + logger.debug("Adding quzwelsuziwqgpt2.onion:8444 to knownNodes.") for port in [8080, 8444]: logger.debug("Resolving %i through SOCKS...", port) address_family = socket.AF_INET diff --git a/src/shared.py b/src/shared.py index 249bd562..d5f8bad7 100644 --- a/src/shared.py +++ b/src/shared.py @@ -143,7 +143,9 @@ def isInSqlInventory(hash): return queryreturn != [] def encodeHost(host): - if host.find(':') == -1: + if host.find('.onion') > -1: + return '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\x7F\x00\x00\x01' + elif host.find(':') == -1: return '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF' + \ socket.inet_aton(host) else: From d48af87b1425746da94297d4da56dcd5d9d3f108 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Fri, 27 Nov 2015 14:45:52 +0100 Subject: [PATCH 199/210] Initialise inventorySets earlier In corner cases, they may be referenced before they are populated. Probably fixes Bitmessage#530 --- src/shared.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared.py b/src/shared.py index d5f8bad7..a3f2f066 100644 --- a/src/shared.py +++ b/src/shared.py @@ -84,7 +84,7 @@ lastTimeWeResetBytesSent = 0 # used for the bandwidth rate limit sendDataLock = threading.Lock() # used for the bandwidth rate limit receiveDataLock = threading.Lock() # used for the bandwidth rate limit daemon = False -inventorySets = {} # key = streamNumer, value = a set which holds the inventory object hashes that we are aware of. This is used 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 every couple hours. +inventorySets = {1: set()} # key = streamNumer, value = a set which holds the inventory object hashes that we are aware of. This is used 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 every couple hours. needToWriteKnownNodesToDisk = False # If True, the singleCleaner will write it to disk eventually. maximumLengthOfTimeToBotherResendingMessages = 0 objectProcessorQueue = Queue.Queue( From 9f6ddce7c5814f316fcd4f09f4e5378c6d4edc30 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Fri, 27 Nov 2015 16:53:05 +0100 Subject: [PATCH 200/210] Fix double unread count in tray icon --- src/bitmessageqt/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 659508e1..90c495dd 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -916,15 +916,15 @@ class MyForm(settingsmixin.SMainWindow): queryreturn = sqlQuery("SELECT COUNT(*) FROM inbox WHERE read = 0") for row in queryreturn: item.setUnreadCount(int(row[0])) - if isinstance(item, Ui_AddressWidget): + if isinstance(item, Ui_AddressWidget) and item.type == AccountMixin.ALL: self.drawTrayIcon(self.currentTrayIconFileName, self.findInboxUnreadCount()) elif type == 1: item.setUnreadCount(item.unreadCount + 1) - if isinstance(item, Ui_AddressWidget): + if isinstance(item, Ui_AddressWidget) and item.type == AccountMixin.ALL: self.drawTrayIcon(self.currentTrayIconFileName, self.findInboxUnreadCount(self.unreadCount + 1)) elif type == -1: item.setUnreadCount(item.unreadCount - 1) - if isinstance(item, Ui_AddressWidget): + if isinstance(item, Ui_AddressWidget) and item.type == AccountMixin.ALL: self.drawTrayIcon(self.currentTrayIconFileName, self.findInboxUnreadCount(self.unreadCount -1)) widgets = [self.ui.treeWidgetYourIdentities, self.ui.treeWidgetSubscriptions, self.ui.treeWidgetChans] From 89bd40118326ca76590d4bc583c7a52df61047a8 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sun, 29 Nov 2015 18:22:40 +0100 Subject: [PATCH 201/210] Version bump --- src/shared.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared.py b/src/shared.py index a3f2f066..8f6b4294 100644 --- a/src/shared.py +++ b/src/shared.py @@ -1,6 +1,6 @@ from __future__ import division -softwareVersion = '0.5.4' +softwareVersion = '0.5.5' verbose = 1 maximumAgeOfAnObjectThatIAmWillingToAccept = 216000 # This is obsolete with the change to protocol v3 but the singleCleaner thread still hasn't been updated so we need this a little longer. lengthOfTimeToHoldOnToAllPubkeys = 2419200 # Equals 4 weeks. You could make this longer if you want but making it shorter would not be advisable because there is a very small possibility that it could keep you from obtaining a needed pubkey for a period of time. From 06fdd5652629914e6cbbd86a21da21bb534f8cc3 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sun, 29 Nov 2015 19:14:26 +0100 Subject: [PATCH 202/210] Re-disable windows SMP frozen support The recommended workaround didn't work. --- src/bitmessagemain.py | 2 -- src/proofofwork.py | 9 +++++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 64e2784d..2767e465 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -18,7 +18,6 @@ import singleton import os import socket import ctypes -from multiprocessing import Process, freeze_support from struct import pack import sys from subprocess import call @@ -266,7 +265,6 @@ class Main: return {'address':address,'port':port} if __name__ == "__main__": - freeze_support() mainprogram = Main() mainprogram.start() diff --git a/src/proofofwork.py b/src/proofofwork.py index 8ab04820..8441bdeb 100644 --- a/src/proofofwork.py +++ b/src/proofofwork.py @@ -151,6 +151,15 @@ def run(target, initialHash): return _doCPoW(target, initialHash) except: pass # fallback + if frozen == "macosx_app" or not frozen: + # on my (Peter Surda) Windows 10, Windows Defender + # does not like this and fights with PyBitmessage + # over CPU, resulting in very slow PoW + # added on 2015-11-29: multiprocesing.freeze_support() doesn't help + try: + return _doFastPoW(target, initialHash) + except: + pass #fallback try: return _doFastPoW(target, initialHash) except: From 4c9e2bf8bb8f94f426c887411044ce85c0199c78 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sun, 29 Nov 2015 19:16:44 +0100 Subject: [PATCH 203/210] Frozen SMP re-disable try 2 --- src/proofofwork.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/proofofwork.py b/src/proofofwork.py index 8441bdeb..69744053 100644 --- a/src/proofofwork.py +++ b/src/proofofwork.py @@ -160,8 +160,4 @@ def run(target, initialHash): return _doFastPoW(target, initialHash) except: pass #fallback - try: - return _doFastPoW(target, initialHash) - except: - pass #fallback return _doSafePoW(target, initialHash) From 9a2d36fc5b9bf3b4b632b6a75d3b5bc031c5cefb Mon Sep 17 00:00:00 2001 From: mailchuck Date: Tue, 1 Dec 2015 01:09:28 +0100 Subject: [PATCH 204/210] Fix Addressbook context menu delete --- src/bitmessageqt/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 90c495dd..d4af59ef 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -3348,8 +3348,8 @@ class MyForm(settingsmixin.SMainWindow): normal = True for row in self.ui.tableWidgetAddressBook.selectedIndexes(): currentRow = row.row() - type = str(self.ui.tableWidgetAddressBook.item( - currentRow, 0).data(Qt.UserRole).toPyObject()) + type = self.ui.tableWidgetAddressBook.item( + currentRow, 0).type if type != AccountMixin.NORMAL: normal = False if normal: From 36e84a982869c08d125084571b9c835887ecd0d6 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Tue, 1 Dec 2015 01:30:38 +0100 Subject: [PATCH 205/210] Test commit policy commit --- src/proofofwork.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/proofofwork.py b/src/proofofwork.py index 69744053..841c7791 100644 --- a/src/proofofwork.py +++ b/src/proofofwork.py @@ -160,4 +160,4 @@ def run(target, initialHash): return _doFastPoW(target, initialHash) except: pass #fallback - return _doSafePoW(target, initialHash) + return _doSafePoW(target, initialHash) \ No newline at end of file From bcdf60871691204b405b3eb8acda4136d1355904 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Wed, 2 Dec 2015 11:12:37 +0100 Subject: [PATCH 206/210] Fix wrong account type initialiser Addresses #14 --- src/bitmessageqt/account.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitmessageqt/account.py b/src/bitmessageqt/account.py index f881cc8c..dc21d669 100644 --- a/src/bitmessageqt/account.py +++ b/src/bitmessageqt/account.py @@ -85,7 +85,7 @@ class AccountColor(AccountMixin): class BMAccount(object): def __init__(self, address = None): self.address = address - self.type = 'normal' + self.type = AccountMixin.NORMAL if shared.config.has_section(address): if shared.safeConfigGetBoolean(self.address, 'chan'): self.type = AccountMixin.CHAN From f90b1890e38338e56c47d3498ae0810325de593d Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Wed, 2 Dec 2015 22:36:19 +0100 Subject: [PATCH 207/210] New message appears if viewing account root Fixes #132 --- src/bitmessageqt/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index d4af59ef..bc6a0eec 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2425,7 +2425,7 @@ class MyForm(settingsmixin.SMainWindow): ret = None for treeWidget in [self.ui.treeWidgetYourIdentities, self.ui.treeWidgetSubscriptions, self.ui.treeWidgetChans]: tableWidget = self.widgetConvert(treeWidget) - if tableWidget == inbox and self.getCurrentAccount(treeWidget) == acct.address and self.getCurrentFolder(treeWidget) == "inbox": + if tableWidget == inbox and self.getCurrentAccount(treeWidget) == acct.address and self.getCurrentFolder(treeWidget) in ["inbox", None]: ret = self.addMessageListItemInbox(inbox, "inbox", inventoryHash, toAddress, fromAddress, subject, time.time(), 0) elif treeWidget == self.ui.treeWidgetYourIdentities and self.getCurrentAccount(treeWidget) is None: ret = self.addMessageListItemInbox(tableWidget, "inbox", inventoryHash, toAddress, fromAddress, subject, time.time(), 0) From a27a608b6d4b7e49725228d7d2ba35ba9b3d2546 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Wed, 2 Dec 2015 23:12:38 +0100 Subject: [PATCH 208/210] Auto-register for email gateway if necessary If attempting to send to an email address from an unregistered account, auto-send registration to mailchuck. Use label if possible, otherwise random 12 characater address Fixes #131 --- src/bitmessageqt/__init__.py | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index bc6a0eec..5dafba15 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -53,7 +53,9 @@ import pickle import platform import textwrap import debug +import random import subprocess +import string import datetime from helper_sql import * import l10n @@ -2186,12 +2188,24 @@ class MyForm(settingsmixin.SMainWindow): toAddressesList)) # remove duplicate addresses. If the user has one address with a BM- and the same address without the BM-, this will not catch it. They'll send the message to the person twice. for toAddress in toAddressesList: if toAddress != '': - if toAddress.find("@") >= 0 and isinstance(acct, GatewayAccount): - acct.createMessage(toAddress, fromAddress, subject, message) - subject = acct.subject - toAddress = acct.toAddress - logger.debug("Subject: %s" % (subject)) - logger.debug("address: %s" % (toAddress)) + if toAddress.find("@") >= 0: + if isinstance(acct, GatewayAccount): + acct.createMessage(toAddress, fromAddress, subject, message) + subject = acct.subject + toAddress = acct.toAddress + else: + email = acct.getLabel() + if email[-14:] != "@mailchuck.com": #attempt register + # 12 character random email address + email = ''.join(random.SystemRandom().choice(string.ascii_lowercase) for _ in range(12)) + "@mailchuck.com" + acct = MailchuckAccount(fromAddress) + acct.register(email) + shared.config.set(fromAddress, 'label', email) + shared.config.set(fromAddress, 'gateway', 'mailchuck') + shared.writeKeysFile() + self.statusBar().showMessage(_translate( + "MainWindow", "Error: Your account wasn't registered at an email gateway. Sending registration now as %1, please wait for the registration to be processed before retrying sending.").arg(email)) + return status, addressVersionNumber, streamNumber, ripe = decodeAddress( toAddress) if status != 'success': From fe8696d2670a648e4e55edc5f649068d160e7a56 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Thu, 3 Dec 2015 23:14:17 +0100 Subject: [PATCH 209/210] Label for blacklisting sender fix It wasn't properly utf-8-ied. --- src/bitmessageqt/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 5dafba15..b6da74f9 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -3137,7 +3137,7 @@ class MyForm(settingsmixin.SMainWindow): queryreturn = sqlQuery('''select * from blacklist where address=?''', addressAtCurrentInboxRow) if queryreturn == []: - label = "\"" + str(tableWidget.item(currentInboxRow, 2).data(Qt.UserRole).toPyObject()) + "\" in " + shared.config.get(self.getCurrentAccount(), "label") + label = "\"" + unicode(tableWidget.item(currentInboxRow, 2).data(Qt.UserRole).toString(), 'utf-8') + "\" in " + shared.config.get(self.getCurrentAccount(), "label") sqlExecute('''INSERT INTO blacklist VALUES (?,?, ?)''', label, addressAtCurrentInboxRow, True) From 507b6b0f37ac03ae8d536c4325211532f1adbbfa Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sat, 5 Dec 2015 11:18:51 +0100 Subject: [PATCH 210/210] Joining chans interface freeze Fixes #137 --- src/class_addressGenerator.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/class_addressGenerator.py b/src/class_addressGenerator.py index c48756e8..b84be8e3 100644 --- a/src/class_addressGenerator.py +++ b/src/class_addressGenerator.py @@ -142,8 +142,7 @@ class addressGenerator(threading.Thread, StoppableThread): # The API and the join and create Chan functionality # both need information back from the address generator. - if shared.safeConfigGetBoolean('bitmessagesettings', 'apienabled'): - shared.apiAddressGeneratorReturnQueue.put(address) + shared.apiAddressGeneratorReturnQueue.put(address) shared.UISignalQueue.put(( 'updateStatusBar', tr.translateText("MainWindow", "Done generating address. Doing work necessary to broadcast it..."))) @@ -209,8 +208,7 @@ class addressGenerator(threading.Thread, StoppableThread): # If we are joining an existing chan, let us check to make sure it matches the provided Bitmessage address if command == 'joinChan': if address != chanAddress: - if shared.safeConfigGetBoolean('bitmessagesettings', 'apienabled'): - shared.apiAddressGeneratorReturnQueue.put('chan name does not match address') + shared.apiAddressGeneratorReturnQueue.put('chan name does not match address') saveAddressToDisk = False if command == 'getDeterministicAddress': saveAddressToDisk = False @@ -281,12 +279,11 @@ class addressGenerator(threading.Thread, StoppableThread): # Done generating addresses. - if shared.safeConfigGetBoolean('bitmessagesettings', 'apienabled'): - if command == 'createDeterministicAddresses' or command == 'joinChan' or command == 'createChan': - shared.apiAddressGeneratorReturnQueue.put( - listOfNewAddressesToSendOutThroughTheAPI) - elif command == 'getDeterministicAddress': - shared.apiAddressGeneratorReturnQueue.put(address) + if command == 'createDeterministicAddresses' or command == 'joinChan' or command == 'createChan': + shared.apiAddressGeneratorReturnQueue.put( + listOfNewAddressesToSendOutThroughTheAPI) + elif command == 'getDeterministicAddress': + shared.apiAddressGeneratorReturnQueue.put(address) else: raise Exception( "Error in the addressGenerator thread. Thread was given a command it could not understand: " + command)