2014-04-18 07:48:42 +02:00
# Copyright (c) 2014 Luke Montalvo <lukemontalvo@gmail.com>
2014-04-29 07:10:33 +02:00
# This file adds a alternative commandline interface, feel free to critique and fork
2014-04-19 20:45:37 +02:00
#
2014-04-29 07:10:33 +02:00
# This has only been tested on Arch Linux
2014-04-19 20:45:37 +02:00
# Dependencies:
# * from python2-pip
# * python2-pythondialog
# * dialog
2014-04-18 07:48:42 +02:00
import os
import sys
import StringIO
2014-04-19 20:45:37 +02:00
import time
from time import strftime , localtime
from threading import Timer
import curses
import dialog
from dialog import Dialog
2014-04-29 07:10:33 +02:00
from helper_sql import *
2014-04-19 20:45:37 +02:00
import shared
import ConfigParser
2014-04-18 07:48:42 +02:00
from addresses import *
2014-04-29 07:10:33 +02:00
from pyelliptic . openssl import OpenSSL
2014-04-18 07:48:42 +02:00
quit = False
menutab = 1
menu = [ " Inbox " , " Send " , " Sent " , " Your Identities " , " Subscriptions " , " Address Book " , " Blacklist " , " Network Status " ]
2014-04-19 20:57:08 +02:00
naptime = 100
2014-04-18 07:48:42 +02:00
log = " "
2014-04-19 20:45:37 +02:00
logpad = None
inventorydata = 0
startuptime = time . time ( )
2014-04-29 07:10:33 +02:00
inbox = [ ]
inboxcur = 0
sent = [ ]
sentcur = 0
2014-04-18 07:48:42 +02:00
addresses = [ ]
addrcur = 0
2014-04-19 20:45:37 +02:00
addrcopy = 0
2014-04-29 07:10:33 +02:00
subscriptions = [ ]
subcur = 0
addrbook = [ ]
abookcur = 0
blacklist = [ ]
blackcur = 0
BROADCAST_STR = " [Broadcast subscribers] "
2014-04-18 07:48:42 +02:00
class printLog :
def write ( self , output ) :
global log
log + = output
2014-04-19 20:45:37 +02:00
def flush ( self ) :
pass
class errLog :
def write ( self , output ) :
global log
log + = " ! " + output
def flush ( self ) :
pass
2014-04-18 07:48:42 +02:00
printlog = printLog ( )
2014-04-19 20:45:37 +02:00
errlog = errLog ( )
2014-04-18 07:48:42 +02:00
def cpair ( a ) :
r = curses . color_pair ( a )
if r not in range ( 1 , curses . COLOR_PAIRS - 1 ) :
r = curses . color_pair ( 0 )
return r
2014-04-29 07:10:33 +02:00
def ascii ( s ) :
r = " "
for c in s :
if ord ( c ) in range ( 128 ) :
r + = c
return r
2014-04-18 07:48:42 +02:00
def drawmenu ( stdscr ) :
menustr = " "
for i in range ( 0 , len ( menu ) ) :
if menutab == i + 1 :
menustr = menustr [ : - 1 ]
menustr + = " [ "
menustr + = str ( i + 1 ) + menu [ i ]
if menutab == i + 1 :
menustr + = " ] "
elif i != len ( menu ) - 1 :
menustr + = " "
stdscr . addstr ( 2 , 5 , menustr , curses . A_UNDERLINE )
2014-04-19 20:45:37 +02:00
def resetlookups ( ) :
2014-04-29 07:10:33 +02:00
global inventorydata
2014-04-19 20:45:37 +02:00
inventorydata = shared . numberOfInventoryLookupsPerformed
shared . numberOfInventoryLookupsPerformed = 0
2014-04-29 07:10:33 +02:00
Timer ( 1 , resetlookups , ( ) ) . start ( )
2014-04-18 07:48:42 +02:00
def drawtab ( stdscr ) :
2014-04-19 20:45:37 +02:00
if menutab in range ( 1 , len ( menu ) + 1 ) :
2014-04-18 07:48:42 +02:00
if menutab == 1 : # Inbox
2014-04-29 07:10:33 +02:00
stdscr . addstr ( 3 , 5 , " To " , curses . A_BOLD )
stdscr . addstr ( 3 , 30 , " From " , curses . A_BOLD )
stdscr . addstr ( 3 , 60 , " Subject " , curses . A_BOLD )
stdscr . addstr ( 3 , 90 , " Time Received " , curses . A_BOLD )
stdscr . hline ( 4 , 5 , ' - ' , 91 )
for i , item in enumerate ( inbox [ max ( min ( len ( inbox ) - curses . LINES + 6 , inboxcur - 5 ) , 0 ) : ] ) :
if 6 + i < curses . LINES :
a = 0
if i == inboxcur - max ( min ( len ( inbox ) - curses . LINES + 6 , inboxcur - 5 ) , 0 ) : # Highlight current address
a = a | curses . A_REVERSE
if item [ 7 ] == False : # If not read, highlight
a = a | curses . A_BOLD
stdscr . addstr ( 5 + i , 5 , item [ 1 ] [ : 29 ] , a )
stdscr . addstr ( 5 + i , 30 , item [ 3 ] [ : 29 ] , a )
stdscr . addstr ( 5 + i , 60 , item [ 5 ] [ : 29 ] , a )
stdscr . addstr ( 5 + i , 90 , item [ 6 ] [ : 29 ] , a )
2014-04-19 20:45:37 +02:00
elif menutab == 3 : # Sent
pass
2014-04-29 07:10:33 +02:00
elif menutab == 2 or menutab == 4 : # Send or Identities
2014-04-19 20:45:37 +02:00
stdscr . addstr ( 3 , 5 , " Label " , curses . A_BOLD )
2014-04-29 07:10:33 +02:00
stdscr . addstr ( 3 , 30 , " Address " , curses . A_BOLD )
stdscr . addstr ( 3 , 60 , " Stream " , curses . A_BOLD )
stdscr . hline ( 4 , 5 , ' - ' , 61 )
for i , item in enumerate ( addresses [ max ( min ( len ( addresses ) - curses . LINES + 6 , addrcur - 5 ) , 0 ) : ] ) :
2014-04-18 07:48:42 +02:00
a = 0
2014-04-29 07:10:33 +02:00
if i == addrcur - max ( min ( len ( addresses ) - curses . LINES + 6 , addrcur - 5 ) , 0 ) : # Highlight current address
2014-04-19 20:45:37 +02:00
a = a | curses . A_REVERSE
if item [ 1 ] == True and item [ 3 ] not in [ 8 , 9 ] : # Embolden enabled, non-special addresses
a = a | curses . A_BOLD
2014-04-29 07:10:33 +02:00
stdscr . addstr ( 5 + i , 5 , item [ 0 ] [ : 30 ] , a )
stdscr . addstr ( 5 + i , 30 , item [ 2 ] [ : 30 ] , cpair ( item [ 3 ] ) | a )
stdscr . addstr ( 5 + i , 60 , str ( 1 ) [ : 30 ] , a )
2014-04-19 20:45:37 +02:00
elif menutab == 5 : # Subscriptions
pass
elif menutab == 6 : # Address book
2014-04-29 07:10:33 +02:00
stdscr . addstr ( 3 , 5 , " Label " , curses . A_BOLD )
stdscr . addstr ( 3 , 30 , " Address " , curses . A_BOLD )
stdscr . hline ( 4 , 5 , ' - ' , 31 )
for i , item in enumerate ( addrbook [ max ( min ( len ( addrbook ) - curses . LINES + 6 , abookcur - 5 ) , 0 ) : ] ) :
a = 0
if i == abookcur - max ( min ( len ( addrbook ) - curses . LINES + 6 , abookcur - 5 ) , 0 ) : # Highlight current address
a = a | curses . A_REVERSE
stdscr . addstr ( 5 + i , 5 , item [ 0 ] [ : 30 ] , a )
stdscr . addstr ( 5 + i , 30 , item [ 1 ] [ : 30 ] , a )
2014-04-19 20:45:37 +02:00
elif menutab == 7 : # Blacklist
pass
elif menutab == 8 : # Network status
# Connection data
stdscr . addstr ( 4 , 5 , " Total Connections: " + str ( len ( shared . connectedHostsList ) ) . ljust ( 2 ) )
stdscr . addstr ( 6 , 6 , " Stream # " , curses . A_BOLD )
2014-04-19 20:57:08 +02:00
stdscr . addstr ( 6 , 18 , " Connections " , curses . A_BOLD )
stdscr . hline ( 7 , 6 , ' - ' , 23 )
2014-04-19 20:45:37 +02:00
streamcount = [ ]
for host , stream in shared . connectedHostsList . items ( ) :
if stream > = len ( streamcount ) :
streamcount . append ( 1 )
else :
streamcount [ stream ] + = 1
for i , item in enumerate ( streamcount ) :
2014-04-19 20:57:08 +02:00
if i < 4 :
2014-04-19 20:45:37 +02:00
if i == 0 :
2014-04-19 20:57:08 +02:00
stdscr . addstr ( 8 + i , 6 , " ? " )
2014-04-19 20:45:37 +02:00
else :
2014-04-19 20:57:08 +02:00
stdscr . addstr ( 8 + i , 6 , str ( i ) )
stdscr . addstr ( 8 + i , 18 , str ( item ) . ljust ( 2 ) )
2014-04-19 20:45:37 +02:00
# Uptime and processing data
stdscr . addstr ( 6 , 35 , " Since startup on " + unicode ( strftime ( shared . config . get ( ' bitmessagesettings ' , ' timeformat ' ) , localtime ( int ( startuptime ) ) ) ) )
stdscr . addstr ( 7 , 40 , " Processed " + str ( shared . numberOfMessagesProcessed ) . ljust ( 4 ) + " person-to-person messages. " )
stdscr . addstr ( 8 , 40 , " Processed " + str ( shared . numberOfBroadcastsProcessed ) . ljust ( 4 ) + " broadcast messages. " )
stdscr . addstr ( 9 , 40 , " Processed " + str ( shared . numberOfPubkeysProcessed ) . ljust ( 4 ) + " public keys. " )
# Inventory data
2014-04-29 07:10:33 +02:00
stdscr . addstr ( 11 , 35 , " Inventory lookups per second: " + str ( inventorydata ) . ljust ( 3 ) )
2014-04-19 20:45:37 +02:00
# Log
stdscr . addstr ( 13 , 6 , " Log " , curses . A_BOLD )
n = log . count ( ' \n ' )
if n > 0 :
l = log . split ( ' \n ' )
if n > 512 :
del l [ : ( n - 256 ) ]
logpad . erase ( )
n = len ( l )
for i , item in enumerate ( l ) :
a = 0
if len ( item ) > 0 and item [ 0 ] == ' ! ' :
a = curses . color_pair ( 1 )
item = item [ 1 : ]
logpad . addstr ( i , 0 , item , a )
logpad . refresh ( n - curses . LINES + 2 , 0 , 14 , 6 , curses . LINES - 2 , curses . COLS - 7 )
2014-04-18 07:48:42 +02:00
stdscr . refresh ( )
def redraw ( stdscr ) :
stdscr . erase ( )
stdscr . border ( )
drawmenu ( stdscr )
stdscr . refresh ( )
2014-04-19 20:45:37 +02:00
def dialogreset ( stdscr ) :
stdscr . clear ( )
stdscr . keypad ( 1 )
curses . curs_set ( 0 )
2014-04-18 07:48:42 +02:00
def handlech ( c , stdscr ) :
if c != curses . ERR :
if c in range ( 256 ) :
if chr ( c ) in ' 12345678 ' :
2014-04-29 07:10:33 +02:00
global menutab
2014-04-18 07:48:42 +02:00
menutab = int ( chr ( c ) )
elif chr ( c ) == ' q ' :
global quit
quit = True
2014-04-19 20:45:37 +02:00
elif chr ( c ) == ' \n ' :
2014-04-29 07:10:33 +02:00
if menutab == 1 :
curses . curs_set ( 1 )
d = Dialog ( dialog = " dialog " )
d . set_background_title ( " Inbox Message Dialog Box " )
r , t = d . menu ( " Do what with \" " + inbox [ inboxcur ] [ 5 ] + " \" from \" " + inbox [ inboxcur ] [ 3 ] + " \" ? " ,
choices = [ ( " 1 " , " View message " ) ,
( " 2 " , " Mark message as unread " ) ,
( " 3 " , " Reply " ) ,
( " 4 " , " Add sender to Address Book " ) ,
( " 5 " , " Save message as text file " ) ,
( " 6 " , " Move to trash " ) ] )
if r == d . DIALOG_OK :
if t == " 1 " : # View
d . set_background_title ( " \" " + inbox [ inboxcur ] [ 5 ] + " \" from \" " + inbox [ inboxcur ] [ 3 ] + " \" to \" " + inbox [ inboxcur ] [ 1 ] + " \" " )
msg = " "
ret = sqlQuery ( " SELECT message FROM inbox WHERE msgid=? " , inbox [ inboxcur ] [ 0 ] )
if ret != [ ] :
for row in ret :
msg , = row
msg = shared . fixPotentiallyInvalidUTF8Data ( msg )
d . scrollbox ( unicode ( ascii ( msg ) ) , 30 , 100 , exit_label = " Continue " )
sqlExecute ( " UPDATE inbox SET read=1 WHERE msgid=? " , inbox [ inboxcur ] [ 0 ] )
inbox [ inboxcur ] [ 7 ] = 1
else :
d . scrollbox ( unicode ( " Could not fetch message. " ) , exit_label = " Continue " )
elif t == " 2 " : # Mark unread
sqlExecute ( " UPDATE inbox SET read=0 WHERE msgid=? " , inbox [ inboxcur ] [ 0 ] )
inbox [ inboxcur ] [ 7 ] = 0
elif t == " 3 " : # Reply
curses . curs_set ( 1 )
m = inbox [ inboxcur ]
fromaddr = m [ 4 ]
ischan = False
for i , item in enumerate ( addresses ) :
if fromaddr == item [ 2 ] and item [ 3 ] != 0 :
ischan = True
break
if not addresses [ i ] [ 1 ] :
d . scrollbox ( unicode ( " Sending address disabled, please either enable it or choose a different address. " ) , exit_label = " Continue " )
return
toaddr = m [ 2 ]
if ischan :
toaddr = fromaddr
subject = m [ 5 ]
if not m [ 5 ] [ : 4 ] == " Re: " :
subject = " Re: " + m [ 5 ]
body = " "
ret = sqlQuery ( " SELECT message FROM inbox WHERE msgid=? " , m [ 0 ] )
if ret != [ ] :
body = " \n \n ------------------------------------------------------ \n "
for row in ret :
body , = row
sendMessage ( fromaddr , toaddr , ischan , subject , body )
dialogreset ( stdscr )
elif t == " 4 " : # Add to Address Book
addr = inbox [ inboxcur ] [ 4 ]
if addr in [ item [ 1 ] for i , item in enumerate ( addrbook ) ] :
r , t = d . inputbox ( " Label for address \" " + addr + " \" " )
if r == d . DIALOG_OK :
sqlExecute ( " INSERT INTO addressbook VALUES (?,?) " , t , addr )
addrbook . reverse ( ) . append ( [ t , addr ] ) . reverse ( ) # Prepend new entry
else :
d . scrollbox ( unicode ( " The selected address is already in the Address Book. " ) , exit_label = " Continue " )
elif t == " 5 " : # Save message
d . set_background_title ( " Save \" " + inbox [ inboxcur ] [ 5 ] + " \" as text file " )
r , t = d . inputbox ( " Filename " , init = inbox [ inboxcur ] [ 5 ] + " .txt " )
if r == d . DIALOG_OK :
msg = " "
ret = sqlQuery ( " SELECT message FROM inbox WHERE msgid=? " , inbox [ inboxcur ] [ 0 ] )
if ret != [ ] :
for row in ret :
msg , = row
fh = open ( t , " a " ) # Open in append mode just in case
fh . write ( msg )
fh . close ( )
else :
d . scrollbox ( unicode ( " Could not fetch message. " ) , exit_label = " Continue " )
elif t == " 6 " : # Move to trash
sqlExecute ( " UPDATE inbox SET folder= ' trash ' WHERE msgid=? " , inbox [ inboxcur ] [ 0 ] )
del inbox [ inboxcur ]
d . scrollbox ( unicode ( " Message moved to trash. There is no interface to view your trash, but the message is still on disk if you are desperate to recover it. " ) ,
exit_label = " Continue " )
dialogreset ( stdscr )
elif menutab == 2 :
curses . curs_set ( 1 )
sendMessage ( addresses [ addrcur ] [ 2 ] )
dialogreset ( stdscr )
elif menutab == 4 :
2014-04-19 20:45:37 +02:00
curses . curs_set ( 1 )
d = Dialog ( dialog = " dialog " )
d . set_background_title ( " Your Identities Dialog Box " )
r , t = d . menu ( " Do what with \" " + addresses [ addrcur ] [ 0 ] + " \" : \" " + addresses [ addrcur ] [ 2 ] + " \" ? " ,
choices = [ ( " 1 " , " Create new address " ) ,
( " 2 " , " Copy address to internal buffer " ) ,
( " 3 " , " Rename " ) ,
( " 4 " , " Enable " ) ,
( " 5 " , " Disable " ) ,
( " 6 " , " Delete " ) ,
( " 7 " , " Special address behavior " ) ] )
if r == d . DIALOG_OK :
if t == " 1 " : # Create new address
d . set_background_title ( " Create new address " )
d . scrollbox ( unicode ( " Here you may generate as many addresses as you like. \n "
" Indeed, creating and abandoning addresses is encouraged. \n "
" Deterministic addresses have several pros and cons: \n "
" \n Pros: \n "
" * You can recreate your addresses on any computer from memory \n "
" * You need not worry about backing up your keys.dat file as long as you \n can remember your passphrase \n "
" Cons: \n "
" * You must remember (or write down) your passphrase in order to recreate \n your keys if they are lost \n "
" * You must also remember the address version and stream numbers \n "
" * If you choose a weak passphrase someone may be able to brute-force it \n and then send and receive messages as you " ) ,
exit_label = " Continue " )
r , t = d . menu ( " Choose an address generation technique " ,
choices = [ ( " 1 " , " Use a random number generator " ) ,
( " 2 " , " Use a passphrase " ) ] )
if r == d . DIALOG_OK :
if t == " 1 " :
d . set_background_title ( " Randomly generate address " )
r , t = d . inputbox ( " Label (not shown to anyone except you) " )
label = " "
if r == d . DIALOG_OK and len ( t ) > 0 :
label = t
r , t = d . menu ( " Choose a stream " ,
choices = [ ( " 1 " , " Use the most available stream " ) , ( " " , " (Best if this is the first of many addresses you will create) " ) ,
( " 2 " , " Use the same stream as an existing address " ) , ( " " , " (Saves you some bandwidth and processing power) " ) ] )
if r == d . DIALOG_OK :
if t == " 1 " :
stream = 1
elif t == " 2 " :
addrs = [ ]
for i , item in enumerate ( addresses ) :
addrs . append ( [ str ( i ) , item [ 2 ] ] )
r , t = d . menu ( " Choose an existing address ' s stream " , choices = addrs )
if r == d . DIALOG_OK :
stream = decodeAddress ( addrs [ int ( t ) ] [ 1 ] ) [ 2 ]
shorten = False
r , t = d . checklist ( " Miscellaneous options " ,
choices = [ ( " 1 " , " Spend time shortening the address " , shorten ) ] )
if r == d . DIALOG_OK and " 1 " in t :
shorten = True
shared . addressGeneratorQueue . put ( ( " createRandomAddress " , 4 , stream , label , 1 , " " , shorten ) )
elif t == " 2 " :
d . set_background_title ( " Make deterministic addresses " )
r , t = d . passwordform ( " Enter passphrase " ,
[ ( " Passphrase " , 1 , 1 , " " , 2 , 1 , 64 , 128 ) ,
( " Confirm passphrase " , 3 , 1 , " " , 4 , 1 , 64 , 128 ) ] ,
form_height = 4 , insecure = True )
if r == d . DIALOG_OK :
if t [ 0 ] == t [ 1 ] :
passphrase = t [ 0 ]
r , t = d . rangebox ( " Number of addresses to generate " ,
width = 48 , min = 1 , max = 99 , init = 8 )
if r == d . DIALOG_OK :
number = t
stream = 1
shorten = False
r , t = d . checklist ( " Miscellaneous options " ,
choices = [ ( " 1 " , " Spend time shortening the address " , shorten ) ] )
if r == d . DIALOG_OK and " 1 " in t :
shorten = True
d . scrollbox ( unicode ( " In addition to your passphrase, be sure to remember the following numbers: \n "
" \n * Address version number: " + str ( 4 ) + " \n "
" * Stream number: " + str ( stream ) ) ,
exit_label = " Continue " )
shared . addressGeneratorQueue . put ( ( ' createDeterministicAddresses ' , 4 , stream , " unused deterministic address " , number , str ( passphrase ) , shorten ) )
else :
d . scrollbox ( unicode ( " Passphrases do not match " ) , exit_label = " Continue " )
elif t == " 2 " : # Copy address to internal buffer
2014-04-29 07:10:33 +02:00
global addrcopy
2014-04-19 20:45:37 +02:00
addrcopy = addrcur
elif t == " 3 " : # Rename address label
a = addresses [ addrcur ] [ 2 ]
label = addresses [ addrcur ] [ 0 ]
r , t = d . inputbox ( " New address label " , init = label )
if r == d . DIALOG_OK :
label = t
shared . config . set ( a , " label " , label )
# Write config
with open ( shared . appdata + ' keys.dat ' , ' wb ' ) as configfile :
shared . config . write ( configfile )
addresses [ addrcur ] [ 0 ] = label
elif t == " 4 " : # Enable address
a = addresses [ addrcur ] [ 2 ]
shared . config . set ( a , " enabled " , " true " ) # Set config
# Write config
with open ( shared . appdata + ' keys.dat ' , ' wb ' ) as configfile :
shared . config . write ( configfile )
# Change color
if shared . safeConfigGetBoolean ( a , ' chan ' ) :
addresses [ addrcur ] [ 3 ] = 9 # orange
elif shared . safeConfigGetBoolean ( a , ' mailinglist ' ) :
addresses [ addrcur ] [ 3 ] = 5 # magenta
else :
addresses [ addrcur ] [ 3 ] = 0 # black
addresses [ addrcur ] [ 1 ] = True
shared . reloadMyAddressHashes ( ) # Reload address hashes
elif t == " 5 " : # Disable address
a = addresses [ addrcur ] [ 2 ]
shared . config . set ( a , " enabled " , " false " ) # Set config
addresses [ addrcur ] [ 3 ] = 8 # Set color to gray
# Write config
with open ( shared . appdata + ' keys.dat ' , ' wb ' ) as configfile :
shared . config . write ( configfile )
addresses [ addrcur ] [ 1 ] = False
shared . reloadMyAddressHashes ( ) # Reload address hashes
elif t == " 6 " : # Delete address
pass
elif t == " 7 " : # Special address behavior
a = addresses [ addrcur ] [ 2 ]
d . set_background_title ( " Special address behavior " )
if shared . safeConfigGetBoolean ( a , " chan " ) :
d . scrollbox ( unicode ( " This is a chan address. You cannot use it as a pseudo-mailing list. " ) , exit_label = " Continue " )
else :
m = shared . safeConfigGetBoolean ( a , " mailinglist " )
r , t = d . radiolist ( " Select address behavior " ,
2014-04-29 07:10:33 +02:00
choices = [ ( " 1 " , " Behave as a normal address " , not m ) ,
( " 2 " , " Behave as a pseudo-mailing-list address " , m ) ] )
2014-04-19 20:45:37 +02:00
if r == d . DIALOG_OK :
if t == " 1 " and m == True :
shared . config . set ( a , " mailinglist " , " false " )
if addresses [ addrcur ] [ 1 ] :
addresses [ addrcur ] [ 3 ] = 0 # Set color to black
else :
addresses [ addrcur ] [ 3 ] = 8 # Set color to gray
elif t == " 2 " and m == False :
try :
mn = shared . config . get ( a , " mailinglistname " )
except ConfigParser . NoOptionError :
mn = " "
r , t = d . inputbox ( " Mailing list name " , init = mn )
if r == d . DIALOG_OK :
mn = t
shared . config . set ( a , " mailinglist " , " true " )
shared . config . set ( a , " mailinglistname " , mn )
addresses [ addrcur ] [ 3 ] = 6 # Set color to magenta
# Write config
with open ( shared . appdata + ' keys.dat ' , ' wb ' ) as configfile :
shared . config . write ( configfile )
dialogreset ( stdscr )
2014-04-18 07:48:42 +02:00
else :
2014-04-29 07:10:33 +02:00
global addrcur , inboxcur , abookcur
2014-04-18 07:48:42 +02:00
if c == curses . KEY_UP :
2014-04-29 07:10:33 +02:00
if menutab == 1 and inboxcur > 0 :
inboxcur - = 1
if ( menutab == 2 or menutab == 4 ) and addrcur > 0 :
2014-04-18 07:48:42 +02:00
addrcur - = 1
2014-04-29 07:10:33 +02:00
if menutab == 6 and abookcur > 0 :
abookcur - = 1
2014-04-18 07:48:42 +02:00
elif c == curses . KEY_DOWN :
2014-04-29 07:10:33 +02:00
if menutab == 1 and inboxcur < len ( inbox ) - 1 :
inboxcur + = 1
if ( menutab == 2 or menutab == 4 ) and addrcur < len ( addresses ) - 1 :
2014-04-18 07:48:42 +02:00
addrcur + = 1
2014-04-29 07:10:33 +02:00
if menutab == 6 and abookcur < len ( addrbook ) - 1 :
abookcur + = 1
elif c == curses . KEY_HOME :
if menutab == 1 :
inboxcur = 0
if menutab == 2 or menutab == 4 :
addrcur = 0
if menutab == 6 :
abookcur = 0
elif c == curses . KEY_END :
if menutab == 1 :
inboxcur = len ( inbox ) - 1
if menutab == 2 or menutab == 4 :
addrcur = len ( addresses ) - 1
if menutab == 6 :
abookcur = len ( addrbook ) - 1
2014-04-18 07:48:42 +02:00
redraw ( stdscr )
2014-04-29 07:10:33 +02:00
def sendMessage ( sender = " " , recv = " " , broadcast = None , subject = " " , body = " " ) :
if sender == " " :
return
d = Dialog ( dialog = " dialog " )
d . set_background_title ( " Send a message " )
if recv == " " :
r , t = d . inputbox ( " Recipient address (Cancel to load from the Address Book or leave blank to broadcast) " , 10 , 60 )
if r != d . DIALOG_OK :
global menutab
menutab = 6
return
recv = t
if broadcast == None :
r , t = d . radiolist ( " How to send the message? " ,
choices = [ ( " 1 " , " Send to one or more specific people " , True ) ,
( " 2 " , " Broadcast to everyone who is subscribed to your address " , False ) ] )
if r != d . DIALOG_OK :
return
broadcast = False
if t == " 2 " : # Broadcast
broadcast = True
if subject == " " :
r , t = d . inputbox ( " Message subject " , width = 60 )
if r != d . DIALOG_OK :
return
subject = shared . fixPotentiallyInvalidUTF8Data ( t )
if body == " " :
r , t = d . inputbox ( " Message body " , 10 , 80 )
if r != d . DIALOG_OK :
return
body = shared . fixPotentiallyInvalidUTF8Data ( t )
if not broadcast :
recvlist = [ ]
for i , item in enumerate ( recv . replace ( " , " , " ; " ) . split ( " ; " ) ) :
recvlist . append ( item . strip ( ) )
list ( set ( recvlist ) ) # Remove exact duplicates
for addr in recvlist :
if addr != " " :
status , version , stream , ripe = decodeAddress ( addr )
if status != " success " :
d . set_background_title ( " Recipient address error " )
err = " Could not decode " + addr + " : " + status + " \n \n "
if status == " missingbm " :
err + = " Bitmessage addresses should start with \" BM- \" . "
elif status == " checksumfailed " :
err + = " The address was not typed or copied correctly. "
elif status == " invalidcharacters " :
err + = " The address contains invalid characters. "
elif status == " versiontoohigh " :
err + = " The address version is too high. Either you need to upgrade your Bitmessage software or your acquaintance is doing something clever. "
elif status == " ripetooshort " :
err + = " Some data encoded in the address is too short. There might be something wrong with the software of your acquaintance. "
elif status == " ripetoolong " :
err + = " Some data encoded in the address is too long. There might be something wrong with the software of your acquaintance. "
else :
err + = " It is unknown what is wrong with the address. "
d . scrollbox ( unicode ( err ) , exit_label = " Continue " )
else :
addr = addBMIfNotPresent ( addr )
if version > 4 or version < = 1 :
d . set_background_title ( " Recipient address error " )
d . scrollbox ( unicode ( " Could not understand version number " + version + " of address " + addr + " . " ) ,
exit_label = " Continue " )
continue
if stream > 1 or stream == 0 :
d . set_background_title ( " Recipient address error " )
d . scrollbox ( unicode ( " Bitmessage currently only supports stream numbers of 1, unlike as requested for address " + addr + " . " ) ,
exit_label = " Continue " )
continue
if len ( shared . connectedHostsList ) == 0 :
d . set_background_title ( " Not connected warning " )
d . scrollbox ( unicode ( " Because you are not currently connected to the network, " ) ,
exit_label = " Continue " )
ackdata = OpenSSL . rand ( 32 )
sqlExecute (
''' INSERT INTO sent VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?) ''' ,
' ' ,
addr ,
ripe ,
sender ,
subject ,
body ,
ackdata ,
int ( time . time ( ) ) ,
' msgqueued ' ,
1 ,
1 ,
' sent ' ,
2 )
shared . workerQueue . put ( ( " sendmessage " , addr ) )
else : # Broadcast
if addr == " " :
d . set_background_title ( " Empty sender error " )
d . scrollbox ( unicode ( " You must specify an address to send the message from. " ) ,
exit_label = " Continue " )
else :
ackdata = OpenSSL . rand ( 32 )
addr = BROADCAST_STR
ripe = " "
sqlExecute (
''' INSERT INTO sent VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?) ''' ,
' ' ,
addr ,
ripe ,
sender ,
subject ,
body ,
ackdata ,
int ( time . time ( ) ) ,
' broadcastqueued ' ,
1 ,
1 ,
' sent ' ,
2 )
shared . workerQueue . put ( ( ' sendbroadcast ' , ' ' ) )
def loadInbox ( ) :
sys . stdout = sys . __stdout__
print ( " Loading inbox messages... " )
sys . stdout = printlog
where = " toaddress || fromaddress || subject || message "
what = " %% "
ret = sqlQuery ( """ SELECT msgid, toaddress, fromaddress, subject, received, read
FROM inbox WHERE folder = ' inbox ' AND % s LIKE ?
ORDER BY received
""" % (where,), what)
global inbox
for row in ret :
msgid , toaddr , fromaddr , subject , received , read = row
subject = ascii ( shared . fixPotentiallyInvalidUTF8Data ( subject ) )
# Set label for to address
try :
if toaddr == BROADCAST_STR :
tolabel = BROADCAST_STR
else :
tolabel = shared . config . get ( toaddr , " label " )
except :
tolabel = " "
if tolabel == " " :
tolabel = toaddr
tolabel = shared . fixPotentiallyInvalidUTF8Data ( tolabel )
# Set label for from address
fromlabel = " "
if shared . config . has_section ( fromaddr ) :
fromlabel = shared . config . get ( fromaddr , " label " )
if fromlabel == " " : # Check Address Book
qr = sqlQuery ( " SELECT label FROM addressbook WHERE address=? " , fromaddr )
if qr != [ ] :
for r in qr :
fromlabel , = r
if fromlabel == " " : # Check Subscriptions
qr = sqlQuery ( " SELECT label FROM subscriptions WHERE address=? " , fromaddr )
if qr != [ ] :
for r in qr :
fromlabel , = r
if fromlabel == " " :
fromlabel = fromaddr
fromlabel = shared . fixPotentiallyInvalidUTF8Data ( fromlabel )
# Load into array
inbox . append ( [ msgid , tolabel , toaddr , fromlabel , fromaddr , subject ,
strftime ( shared . config . get ( ' bitmessagesettings ' , ' timeformat ' ) , localtime ( int ( received ) ) ) ,
read ] )
inbox . reverse ( )
def loadAddrBook ( ) :
sys . stdout = sys . __stdout__
print ( " Loading address book... " )
sys . stdout = printlog
ret = sqlQuery ( " SELECT label, address FROM addressbook " )
global addrbook
for row in ret :
label , addr = row
label = shared . fixPotentiallyInvalidUTF8Data ( label )
addrbook . append ( [ label , addr ] )
addrbook . reverse ( )
2014-04-18 07:48:42 +02:00
def runwrapper ( ) :
sys . stdout = printlog
2014-04-29 07:10:33 +02:00
#sys.stderr = errlog
# Load messages from database
loadInbox ( )
#loadSent()
loadAddrBook ( )
2014-04-18 07:48:42 +02:00
stdscr = curses . initscr ( )
2014-04-19 20:45:37 +02:00
global logpad
logpad = curses . newpad ( 1024 , curses . COLS )
2014-04-29 07:10:33 +02:00
stdscr . nodelay ( 0 )
2014-04-18 07:48:42 +02:00
curses . curs_set ( 0 )
2014-04-29 07:10:33 +02:00
stdscr . timeout ( 1000 )
2014-04-18 07:48:42 +02:00
curses . wrapper ( run )
shutdown ( )
def run ( stdscr ) :
2014-04-19 20:45:37 +02:00
# Schedule inventory lookup data
resetlookups ( )
# Init color pairs
2014-04-18 07:48:42 +02:00
if curses . has_colors ( ) :
curses . init_pair ( 1 , curses . COLOR_RED , curses . COLOR_BLACK ) # red
2014-04-19 20:45:37 +02:00
curses . init_pair ( 2 , curses . COLOR_GREEN , curses . COLOR_BLACK ) # green
curses . init_pair ( 3 , curses . COLOR_YELLOW , curses . COLOR_BLACK ) # yellow
curses . init_pair ( 4 , curses . COLOR_BLUE , curses . COLOR_BLACK ) # blue
curses . init_pair ( 5 , curses . COLOR_MAGENTA , curses . COLOR_BLACK ) # magenta
curses . init_pair ( 6 , curses . COLOR_CYAN , curses . COLOR_BLACK ) # cyan
curses . init_pair ( 7 , curses . COLOR_WHITE , curses . COLOR_BLACK ) # white
2014-04-18 07:48:42 +02:00
if curses . can_change_color ( ) :
curses . init_color ( 8 , 500 , 500 , 500 ) # gray
curses . init_pair ( 8 , 8 , 0 )
curses . init_color ( 9 , 844 , 465 , 0 ) # orange
curses . init_pair ( 9 , 9 , 0 )
2014-04-19 20:45:37 +02:00
else :
curses . init_pair ( 8 , curses . COLOR_WHITE , curses . COLOR_BLACK ) # grayish
curses . init_pair ( 9 , curses . COLOR_YELLOW , curses . COLOR_BLACK ) # orangish
# Init list of address in 'Your Identities' tab
configSections = shared . config . sections ( )
2014-04-18 07:48:42 +02:00
for addressInKeysFile in configSections :
if addressInKeysFile != " bitmessagesettings " :
isEnabled = shared . config . getboolean ( addressInKeysFile , " enabled " )
2014-04-19 20:45:37 +02:00
addresses . append ( [ shared . config . get ( addressInKeysFile , " label " ) , isEnabled , addressInKeysFile ] )
# Set address color
2014-04-18 07:48:42 +02:00
if not isEnabled :
addresses [ len ( addresses ) - 1 ] . append ( 8 ) # gray
elif shared . safeConfigGetBoolean ( addressInKeysFile , ' chan ' ) :
addresses [ len ( addresses ) - 1 ] . append ( 9 ) # orange
elif shared . safeConfigGetBoolean ( addressInKeysFile , ' mailinglist ' ) :
addresses [ len ( addresses ) - 1 ] . append ( 5 ) # magenta
2014-04-19 20:45:37 +02:00
else :
addresses [ len ( addresses ) - 1 ] . append ( 0 ) # black
addresses . reverse ( )
2014-04-18 07:48:42 +02:00
2014-04-29 07:10:33 +02:00
stdscr . clear ( )
2014-04-18 07:48:42 +02:00
redraw ( stdscr )
while quit == False :
drawtab ( stdscr )
handlech ( stdscr . getch ( ) , stdscr )
def shutdown ( ) :
sys . stdout = sys . __stdout__
print ( " Shutting down... " )
sys . stdout = printlog
shared . doCleanShutdown ( )
sys . stdout = sys . __stdout__
2014-04-19 20:45:37 +02:00
sys . stderr = sys . __stderr__
2014-04-18 07:48:42 +02:00
os . _exit ( 0 )