Version 92

This commit is contained in:
Hydrus 2013-11-13 15:30:38 -06:00
parent 6f980b67ae
commit 29e67fdf19
17 changed files with 516 additions and 137 deletions

View File

@ -49,4 +49,4 @@ HC.shutdown = True
reactor.callFromThread( reactor.stop )
HC.pubsub.pubimmediate( 'shutdown' )
HC.pubsub.WXpubimmediate( 'shutdown' )

View File

@ -8,6 +8,22 @@
<div class="content">
<h3>changelog</h3>
<ul>
<li><h3>version 91</h3></li>
<ul>
<li>encrypted instant messaging framework started</li>
<li>encrypted instant messaging test added</li>
<li>encrypted instant messaging trust framework started</li>
<li>started an AMP framework for encrypted instant messaging</li>
<li>tag A/C exact match parameter added</li>
<li>tag A/C exact match parameter test added</li>
<li>tag A/C logic reorganised and improved</li>
<li>tag A/C now gives exact matches for queries shorter than the A/C threshold</li>
<li>tag A/C threshold default set to 2</li>
<li>fixed an important architectural bug in pubsub</li>
<li>generally cleaned up pubsub code</li>
<li>'popup messages sometimes not updating' problem is fixed</li>
<li>a bit of initialisation cleanup to make startup behaviour more reliable</li>
</ul>
<li><h3>version 91</h3></li>
<ul>
<li>improved how accounts are identified in the server</li>

View File

@ -190,7 +190,7 @@ CLIENT_DEFAULT_OPTIONS[ 'preview_cache_size' ] = 25 * 1048576
CLIENT_DEFAULT_OPTIONS[ 'fullscreen_cache_size' ] = 200 * 1048576
CLIENT_DEFAULT_OPTIONS[ 'thumbnail_dimensions' ] = [ 150, 125 ]
CLIENT_DEFAULT_OPTIONS[ 'password' ] = None
CLIENT_DEFAULT_OPTIONS[ 'num_autocomplete_chars' ] = 1
CLIENT_DEFAULT_OPTIONS[ 'num_autocomplete_chars' ] = 2
CLIENT_DEFAULT_OPTIONS[ 'gui_capitalisation' ] = False
system_predicates = {}

View File

@ -109,24 +109,10 @@ class Controller( wx.App ):
def EventPubSub( self, event ):
pubsubs_queue = HC.pubsub.GetQueue()
( callable, args, kwargs ) = pubsubs_queue.get()
HC.busy_doing_pubsub = True
try: callable( *args, **kwargs )
except wx._core.PyDeadObjectError: pass
except TypeError as e:
if '_wxPyDeadObject' not in str( e ): raise
finally:
pubsubs_queue.task_done()
HC.busy_doing_pubsub = False
try: HC.pubsub.WXProcessQueueItem()
finally: HC.busy_doing_pubsub = False
def GetFullscreenImageCache( self ): return self._fullscreen_image_cache
@ -169,7 +155,7 @@ class Controller( wx.App ):
def OnInit( self ):
HC.app = self
self._local_service = None
@ -289,7 +275,7 @@ class Controller( wx.App ):
self.SetSplashText( 'starting daemons' )
if HC.is_first_start: self._gui.DoFirstStart()
if HC.is_db_updated: wx.CallAfter( HC.pubsub.pub, 'message', HC.Message( HC.MESSAGE_TYPE_TEXT, 'The client has updated to version ' + HC.u( HC.SOFTWARE_VERSION ) + '!' ) )
if HC.is_db_updated: wx.CallLater( 0, HC.pubsub.pub, 'message', HC.Message( HC.MESSAGE_TYPE_TEXT, 'The client has updated to version ' + HC.u( HC.SOFTWARE_VERSION ) + '!' ) )
self.RestartServer()
self._db.StartDaemons()
@ -471,12 +457,10 @@ class Controller( wx.App ):
def WaitUntilGoodTimeToUseGUIThread( self ):
pubsubs_queue = HC.pubsub.GetQueue()
while True:
if HC.shutdown: raise Exception( 'Client shutting down!' )
elif pubsubs_queue.qsize() == 0 and not HC.busy_doing_pubsub: return
elif HC.pubsub.NotBusy() and not HC.busy_doing_pubsub: return
else: time.sleep( 0.0001 )

View File

@ -1601,7 +1601,7 @@ class ServiceDB( FileDB, MessageDB, TagDB, RatingDB ):
else: return result
def _GetAutocompleteTags( self, c, tag_service_identifier = HC.COMBINED_TAG_SERVICE_IDENTIFIER, file_service_identifier = HC.COMBINED_FILE_SERVICE_IDENTIFIER, half_complete_tag = '', include_current = True, include_pending = True, collapse = True ):
def _GetAutocompleteTags( self, c, tag_service_identifier = HC.COMBINED_TAG_SERVICE_IDENTIFIER, file_service_identifier = HC.COMBINED_FILE_SERVICE_IDENTIFIER, tag = '', half_complete_tag = '', include_current = True, include_pending = True, collapse = True ):
tag_service_id = self._GetServiceId( c, tag_service_identifier )
file_service_id = self._GetServiceId( c, file_service_identifier )
@ -1676,6 +1676,12 @@ class ServiceDB( FileDB, MessageDB, TagDB, RatingDB ):
predicates_phrase = 'tag_id IN ' + HC.SplayListForDB( possible_tag_ids )
elif len( tag ) > 0:
( namespace_id, tag_id ) = self._GetNamespaceIdTagId( c, tag )
predicates_phrase = 'namespace_id = ' + HC.u( namespace_id ) + ' AND tag_id = ' + HC.u( tag_id )
else:
predicates_phrase = '1 = 1'
@ -1689,7 +1695,9 @@ class ServiceDB( FileDB, MessageDB, TagDB, RatingDB ):
siblings_manager = HC.app.GetTagSiblingsManager()
all_associated_sibling_tags = siblings_manager.GetAutocompleteSiblings( half_complete_tag )
if len( half_complete_tag ) > 0: all_associated_sibling_tags = siblings_manager.GetAutocompleteSiblings( half_complete_tag )
elif len( tag ) > 0: all_associated_sibling_tags = siblings_manager.GetAllSiblings( tag )
else: all_associated_sibling_tags = siblings_manager.GetAutocompleteSiblings( '' )
sibling_results = [ self._GetNamespaceIdTagId( c, sibling_tag ) for sibling_tag in all_associated_sibling_tags ]
@ -1700,7 +1708,7 @@ class ServiceDB( FileDB, MessageDB, TagDB, RatingDB ):
cache_results = []
if len( half_complete_tag ) > 0:
if len( half_complete_tag ) > 0 or len( tag ) > 0:
for ( namespace_id, tag_ids ) in HC.BuildKeyToListDict( results ).items(): cache_results.extend( c.execute( 'SELECT namespace_id, tag_id, current_count, pending_count FROM autocomplete_tags_cache WHERE tag_service_id = ? AND file_service_id = ? AND namespace_id = ? AND tag_id IN ' + HC.SplayListForDB( tag_ids ) + ';', ( tag_service_id, file_service_id, namespace_id ) ).fetchall() )
@ -4927,6 +4935,15 @@ class DB( ServiceDB ):
c.execute( 'UPDATE options SET options = ?;', ( HC.options, ) )
if version < 92:
( HC.options, ) = c.execute( 'SELECT options FROM options;' ).fetchone()
HC.options[ 'num_autocomplete_chars' ] = 2
c.execute( 'UPDATE options SET options = ?;', ( HC.options, ) )
unknown_account = HC.GetUnknownAccount()
unknown_account.MakeStale()

View File

@ -113,7 +113,10 @@ class FrameGUI( ClientGUICommon.FrameThatResizes ):
self.Show( True )
wx.CallAfter( self._NewPageQuery, HC.LOCAL_FILE_SERVICE_IDENTIFIER )
# as we are in oninit, callafter and calllater( 0 ) are different
# later waits until the mainloop is running, I think.
# after seems to execute synchronously
wx.CallLater( 0, self._NewPageQuery, HC.LOCAL_FILE_SERVICE_IDENTIFIER )
def _THREADUploadPending( self, service_identifier ):

View File

@ -609,46 +609,63 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
half_complete_tag = search_text
if len( half_complete_tag ) >= num_first_letters:
if half_complete_tag == '': matches = [] # a query like 'namespace:'
else:
if must_do_a_search or self._first_letters == '' or not half_complete_tag.startswith( self._first_letters ):
media = self._media_callable()
# if synchro not on, then can't rely on current media as being accurate for current preds, so search db normally
if media is None or not self._synchronised.IsOn():
self._first_letters = half_complete_tag
media = self._media_callable()
# if synchro not on, then can't rely on current media as being accurate for current preds, so search db normally
if media is None or not self._synchronised.IsOn(): self._cached_results = HC.app.Read( 'autocomplete_tags', file_service_identifier = self._file_service_identifier, tag_service_identifier = self._tag_service_identifier, half_complete_tag = search_text, include_current = self._include_current, include_pending = self._include_pending )
if len( search_text ) < num_first_letters:
results = HC.app.Read( 'autocomplete_tags', file_service_identifier = self._file_service_identifier, tag_service_identifier = self._tag_service_identifier, tag = search_text, include_current = self._include_current, include_pending = self._include_pending )
matches = results.GetMatches( half_complete_tag )
else:
tags_managers = []
for m in media:
if must_do_a_search or self._first_letters == '' or not half_complete_tag.startswith( self._first_letters ):
if m.IsCollection(): tags_managers.extend( m.GetSingletonsTagsManagers() )
else: tags_managers.append( m.GetTagsManager() )
self._first_letters = half_complete_tag
self._cached_results = HC.app.Read( 'autocomplete_tags', file_service_identifier = self._file_service_identifier, tag_service_identifier = self._tag_service_identifier, half_complete_tag = search_text, include_current = self._include_current, include_pending = self._include_pending )
lists_of_tags = []
if self._include_current: lists_of_tags += [ list( tags_manager.GetCurrent( self._tag_service_identifier ) ) for tags_manager in tags_managers ]
if self._include_pending: lists_of_tags += [ list( tags_manager.GetPending( self._tag_service_identifier ) ) for tags_manager in tags_managers ]
all_tags_flat_iterable = itertools.chain.from_iterable( lists_of_tags )
all_tags_flat = [ tag for tag in all_tags_flat_iterable if HC.SearchEntryMatchesTag( half_complete_tag, tag ) ]
if self._current_namespace != '': all_tags_flat = [ tag for tag in all_tags_flat if tag.startswith( self._current_namespace + ':' ) ]
tags_to_count = collections.Counter( all_tags_flat )
self._cached_results = CC.AutocompleteMatchesPredicates( self._tag_service_identifier, [ HC.Predicate( HC.PREDICATE_TYPE_TAG, ( operator, tag ), count ) for ( tag, count ) in tags_to_count.items() ] )
matches = self._cached_results.GetMatches( half_complete_tag )
else:
# it is possible that media will change between calls to this, so don't cache it
# it's also quick as hell, so who cares
tags_managers = []
for m in media:
if m.IsCollection(): tags_managers.extend( m.GetSingletonsTagsManagers() )
else: tags_managers.append( m.GetTagsManager() )
lists_of_tags = []
if self._include_current: lists_of_tags += [ list( tags_manager.GetCurrent( self._tag_service_identifier ) ) for tags_manager in tags_managers ]
if self._include_pending: lists_of_tags += [ list( tags_manager.GetPending( self._tag_service_identifier ) ) for tags_manager in tags_managers ]
all_tags_flat_iterable = itertools.chain.from_iterable( lists_of_tags )
all_tags_flat = [ tag for tag in all_tags_flat_iterable if HC.SearchEntryMatchesTag( half_complete_tag, tag ) ]
if self._current_namespace != '': all_tags_flat = [ tag for tag in all_tags_flat if tag.startswith( self._current_namespace + ':' ) ]
tags_to_count = collections.Counter( all_tags_flat )
results = CC.AutocompleteMatchesPredicates( self._tag_service_identifier, [ HC.Predicate( HC.PREDICATE_TYPE_TAG, ( operator, tag ), count ) for ( tag, count ) in tags_to_count.items() ] )
matches = results.GetMatches( half_complete_tag )
matches = self._cached_results.GetMatches( half_complete_tag )
else: matches = []
if self._current_namespace != '': matches.insert( 0, HC.Predicate( HC.PREDICATE_TYPE_NAMESPACE, ( operator, namespace ), None ) )
@ -782,7 +799,13 @@ class AutoCompleteDropdownTagsWrite( AutoCompleteDropdownTags ):
half_complete_tag = search_text
if len( half_complete_tag ) >= num_first_letters:
if len( search_text ) < num_first_letters:
results = HC.app.Read( 'autocomplete_tags', file_service_identifier = self._file_service_identifier, tag_service_identifier = self._tag_service_identifier, tag = search_text, collapse = False )
matches = results.GetMatches( half_complete_tag )
else:
if must_do_a_search or self._first_letters == '' or not half_complete_tag.startswith( self._first_letters ):
@ -793,7 +816,6 @@ class AutoCompleteDropdownTagsWrite( AutoCompleteDropdownTags ):
matches = self._cached_results.GetMatches( half_complete_tag )
else: matches = []
# do the 'put whatever they typed in at the top, whether it has count or not'
# now with sibling support!
@ -2356,7 +2378,7 @@ class PopupMessageGauge( PopupMessage ):
def SetInfo( self, job_key, range, value, message ):
print( value )
if job_key == self._job_key:
if value is None:

View File

@ -39,7 +39,7 @@ TEMP_DIR = BASE_DIR + os.path.sep + 'temp'
# Misc
NETWORK_VERSION = 11
SOFTWARE_VERSION = 91
SOFTWARE_VERSION = 92
UNSCALED_THUMBNAIL_DIMENSIONS = ( 200, 200 )

View File

@ -6,6 +6,7 @@ import Crypto.PublicKey.RSA
import hashlib
import HydrusConstants as HC
import os
import potr
import time
import traceback
import wx
@ -207,4 +208,49 @@ def UnpadAES( message ):
index_of_correct_end = len( message ) - i
return message[:index_of_correct_end + 1]
# I based this on the excellent article by Darrik L Mazey, here:
# https://blog.darmasoft.net/2013/06/30/using-pure-python-otr.html
DEFAULT_POLICY_FLAGS = {}
DEFAULT_POLICY_FLAGS[ 'ALLOW_V1' ] = False
DEFAULT_POLICY_FLAGS[ 'ALLOW_V2' ] = True
DEFAULT_POLICY_FLAGS[ 'REQUIRE_ENCRYPTION' ] = True
GenerateOTRKey = potr.compatcrypto.generateDefaultKey
def LoadOTRKey( stream ): return potr.crypt.PK.parsePrivateKey( stream )[0]
def DumpOTRKey( key ): return key.serializePrivateKey()
class HydrusOTRContext( potr.context.Context ):
def getPolicy( self, key ):
if key in DEFAULT_POLICY_FLAGS: return DEFAULT_POLICY_FLAGS[ key ]
else: return False
def inject( self, msg, appdata = None ):
inject_catcher = appdata
inject_catcher.write( msg )
class HydrusOTRAccount( potr.context.Account ):
def __init__( self, name, privkey, trusts ):
potr.context.Account.__init__( self, name, 'hydrus network otr', 1024, privkey )
self.trusts = trusts
def saveTrusts( self ):
HC.app.Write( 'otr_trusts', self.name, self.trusts )
# I need an accounts manager so there is only ever one copy of an account
# it should fetch name, privkey and trusts from db on bootup
# savettrusts should just spam to the db because it ain't needed that much.

View File

@ -12,7 +12,8 @@ class HydrusPubSub():
def __init__( self ):
self._pubsubs = Queue.Queue()
self._pubsubs = []
self._callables = []
self._lock = threading.Lock()
@ -20,72 +21,97 @@ class HydrusPubSub():
self._topics_to_method_names = {}
def GetQueue( self ): return self._pubsubs
def _GetCallables( self, topic ):
callables = []
if topic in self._topics_to_objects:
try:
objects = self._topics_to_objects[ topic ]
for object in objects:
method_names = self._topics_to_method_names[ topic ]
for method_name in method_names:
if hasattr( object, method_name ):
try:
callable = getattr( object, method_name )
callables.append( callable )
except wx.PyDeadObjectError: pass
except TypeError as e:
if '_wxPyDeadObject' not in str( e ): raise
except: pass
return callables
def NotBusy( self ):
with self._lock:
return len( self._pubsubs ) == 0
def WXProcessQueueItem( self ):
# we don't want to map a topic to its callables until the previous topic's callables have been fully executed
# e.g. when we start a message with a pubsub, it'll take a while (in indepedant thread-time) for wx to create
# the dialog and hence map the new callable to the topic. this was leading to messages not being updated
# because the (short) processing thread finished and entirely pubsubbed before wx had a chance to boot the
# message.
do_callable = False
with self._lock:
if len( self._callables ) > 0:
( callable, args, kwargs ) = self._callables.pop( 0 )
do_callable = True
else:
( topic, args, kwargs ) = self._pubsubs.pop( 0 )
callables = self._GetCallables( topic )
self._callables = [ ( callable, args, kwargs ) for callable in callables ]
for i in range( len( self._callables ) ): wx.PostEvent( HC.app, PubSubEvent() )
# do this _outside_ the lock, lol
if do_callable: callable( *args, **kwargs )
def pub( self, topic, *args, **kwargs ):
with self._lock:
if topic in self._topics_to_objects:
# this stops the pubsubs started at the beginning of the program screwing with the queue
if HC.app.IsMainLoopRunning():
try:
objects = self._topics_to_objects[ topic ]
for object in objects:
method_names = self._topics_to_method_names[ topic ]
for method_name in method_names:
if hasattr( object, method_name ):
try:
self._pubsubs.put( ( getattr( object, method_name ), args, kwargs ) )
wx.PostEvent( HC.app, PubSubEvent() )
except wx.PyDeadObjectError: pass
except Exception as e: raise e
except: pass
self._pubsubs.append( ( topic, args, kwargs ) )
def pubimmediate( self, topic, *args, **kwargs ):
with self._lock:
if topic in self._topics_to_objects:
try:
objects = self._topics_to_objects[ topic ]
for object in objects:
method_names = self._topics_to_method_names[ topic ]
for method_name in method_names:
if hasattr( object, method_name ):
try: getattr( object, method_name )( *args, **kwargs )
except wx.PyDeadObjectError: pass
except Exception as e: raise e
except RuntimeError: pass # sometimes the set changes size during iteration, which is a bug I haven't tracked down
except wx.PyDeadObjectError: pass
except TypeError: pass
except Exception as e: raise e
wx.PostEvent( HC.app, PubSubEvent() )
@ -101,4 +127,14 @@ class HydrusPubSub():
self._topics_to_method_names[ topic ].add( method_name )
def WXpubimmediate( self, topic, *args, **kwargs ):
with self._lock:
callables = self._GetCallables( topic )
for callable in callables: callable( *args, **kwargs )

115
include/HydrusServerAMP.py Normal file
View File

@ -0,0 +1,115 @@
import BaseHTTPServer
import ClientConstants as CC
import Cookie
import hashlib
import httplib
import HydrusAudioHandling
import HydrusConstants as HC
import HydrusDocumentHandling
import HydrusExceptions
import HydrusFileHandling
import HydrusFlashHandling
import HydrusImageHandling
import HydrusServerResources
import HydrusVideoHandling
import os
import random
import ServerConstants as SC
import SocketServer
import traceback
import urllib
import wx
import yaml
from twisted.internet import reactor, defer
from twisted.internet.threads import deferToThread
from twisted.protocols import amp
class HydrusAMPCommand( amp.Command ):
errors = {}
errors[ HydrusExceptions.ForbiddenException ] = 'FORBIDDEN'
errors[ HydrusExceptions.NetworkVersionException ] = 'NETWORK_VERSION'
errors[ HydrusExceptions.NotFoundException ] = 'NOT_FOUND'
errors[ HydrusExceptions.PermissionException ] = 'PERMISSION'
errors[ HydrusExceptions.SessionException ] = 'SESSION'
errors[ Exception ] = 'EXCEPTION'
# IMFile, for aes-encrypted file transfers, as negotiated over otr messages
# file_transfer_id (so we can match up the correct aes key)
# file (this is complicated -- AMP should be little things, right? I need to check max packet size.)
# so, this should be blocks. a block_id and a block
class IMLogin( HydrusAMPCommand ):
arguments = [ ( 'session_key', amp.String() ) ]
class IMMessage( HydrusAMPCommand ):
arguments = [ ( 'identifier_from', amp.String() ), ( 'identifier_to', amp.String() ), ( 'message', amp.String() ) ]
class IMSessionKey( HydrusAMPCommand ):
arguments = [ ( 'access_key', amp.String() ) ]
response = [ ( 'session_key', amp.String() ) ]
class MPublicKey( HydrusAMPCommand ):
arguments = [ ( 'identifier', amp.String() ) ]
response = [ ( 'public_key', amp.String() ) ]
class MessagingServiceProtocol( amp.AMP ):
def im_login( self, session_key ):
# check session_key.
# if it is good, stick this connection on the login manager
# else error
return {}
IMLogin.responder( im_login )
def im_message( self, identifier_from, identifier_to, message ):
# get connection for identifier_to from larger, failing appropriately
# if we fail, we should probably log the _to out, right?
# connection.callRemote( IMMessage, identifier_from = identifier_from, identifier_to = identifier_to, message = message )
# this returns a deferred, so set up a 'return {}' deferred.
return {}
IMMessage.responder( im_message )
def im_session_key( self, access_key ):
session_key = os.urandom( 32 )
return { 'session_key' : session_key }
IMSessionKey.responder( im_session_key )
def m_public_key( self, identifier ):
# this will not be useful until we have normal messaging sorted
public_key = 'public key'
return { 'public_key' : public_key }
MPublicKey.responder( m_public_key )
def connectionLost( self, reason ):
# delete this connection from the login stuffs.
pass
class MessagingClientProtocol( amp.AMP ):
def im_message( self, identifier_from, identifier_to, message ):
# send these args on to the messaging manager, which will:
# start a context, if needed
# spawn a gui prompt/window to start a convo, if needed
# queue the message through to the appropriate context
# maybe the context should spam up to the ui, prob in a pubsub; whatever.
pass
IMMessage.responder( im_message )

View File

@ -38,18 +38,7 @@ class Controller( wx.App ):
def EventExit( self, event ): self._tbicon.Destroy()
def EventPubSub( self, event ):
pubsubs_queue = HC.pubsub.GetQueue()
( callable, args, kwargs ) = pubsubs_queue.get()
try: callable( *args, **kwargs )
except TypeError: pass
except Exception as e: HC.ShowException( e )
pubsubs_queue.task_done()
def EventPubSub( self, event ): HC.pubsub.WXProcessQueueItem()
def OnInit( self ):

View File

@ -155,6 +155,26 @@ class TestClientDB( unittest.TestCase ):
self.assertEqual( pred, read_pred )
#
result = self._read( 'autocomplete_tags', tag = 'car' )
pred = HC.Predicate( HC.PREDICATE_TYPE_TAG, ( '+', 'car' ), 1 )
( read_pred, ) = result.GetMatches( 'car' )
self.assertEqual( read_pred.GetCount(), 1 )
self.assertEqual( pred, read_pred )
#
result = self._read( 'autocomplete_tags', tag = 'c' )
read_preds = result.GetMatches( 'c' )
self.assertEqual( read_preds, [] )
def test_booru( self ):

View File

@ -0,0 +1,130 @@
import ClientConstants as CC
import ClientGUIDialogs
import collections
import HydrusConstants as HC
import HydrusEncryption
import os
import potr
import TestConstants
import unittest
import wx
class Catcher():
def __init__( self ):
self._last_write = ''
def GetLastWrite( self ):
l_w = self._last_write
self._last_write = ''
return l_w
def write( self, data ): self._last_write = data
class TestIM( unittest.TestCase ):
def test_otr( self ):
alice = os.urandom( 32 ).encode( 'hex' )
bob = os.urandom( 32 ).encode( 'hex' )
alice_privkey_hex = '0000000000808000000000000000944834d12b2ad788d34743102266aa9d87fc180577f977c2b201799a4149ca598819ff59591254cb312d1ad23d791a9355cd423c438cb0bc7000bb33377cf73be6fc900705c250d2bdba3287c8e545faf0653e44e66aefffda6e445947ff98cac7c02cb4911f9f527a6f25cf6b8aae4af2909b3c077b80bb00000014afb936c2487a867db906015d755f158e5bf38c1d00000080345d40c8fc329e254ef4be5efa7e1dc20484b982394d09fece366ef598db1a29f4b63160728de57058f405903ded01d6359242656f1e8c02a0b5c67f5d09496486f2f9f005abcec1470888bd7f31dbee8b0ce94b31ed36437dc2446b38829ba08927329bd1ecec0de1d2cd409f840ed2478cdf154a12f79815b29e75ea4a2e0f000000807731a186f55afcdebc34aba5a10130e5eafac0d0067c50f49be494a463271b34a657114c9b69c4fbe30302259feafe75f091b5c5670c7193e256bd7a5be2f3daee2d1a8bc4e04eec891cd6c4591edf40e5cbf8f3e1ca985a9b01d13768ea7160761af475b0097878376dbac6b1ce5b101fb1dd7da354e739791895caba52f14c000000146497dca1a62f1039a0ce8bfc99984de1cc5a9848'
bob_privkey_hex = '00000000008080000000000000741dae82c8c9a55a7f2a5eb9e4db0b3e5990de5df5d7e2a0dab221a8e1e8b92d99f70387458088215836ed1c42c157640578da801120aa50c180c7d9b4e72205b863ecbd6f43e2efbca04d4c6b1b184fd57bda231445ad4a5e9b7ada27ddd9b24c2cfdba77858e76072b5e87a0a4eb91608ffea42ded252bd700000014ec380fdb62ad0248746142c58654403f665c9701000000806e1aaee6b00ee1a77927b5c7a28089eb9bc147e7688091aeeff7de7c3fa98498748d0744f328c230991e9d8031b704d9fc2a87206d62e2f3b1c30b3a370a237368b04dbe826978a232666be84db52c398700d8e2dbc4f5cabc8bd1270f429ea54247a087fdedfac723bf8b1aa4cfad664646a51d97f96a7dffaef0c24d90a5f5000000803dff456298b4fdc4a08599790341f274c8ea7685101cd2d42fb90a34034f71ca0b9b1f2074ec41e1282bd6a3b74d855c82fcea411485da83f784ca15deb3b5372b544ae84fa6f9a8cd470bc8ebd8e60135098e4a4b608d2aea395b2053311f0802a6db0836e25170ce8e5670579f63445688113b93f8597e88d28f03c020c77800000014a762254ce091c8abf6acd0945e32436abbc1b3f2'
alice_privkey = HydrusEncryption.LoadOTRKey( alice_privkey_hex.decode( 'hex' ) )
bob_privkey = HydrusEncryption.LoadOTRKey( bob_privkey_hex.decode( 'hex' ) )
#alice_privkey = HydrusEncryption.GenerateOTRKey()
#bob_privkey = HydrusEncryption.GenerateOTRKey()
alice_account = HydrusEncryption.HydrusOTRAccount( alice, alice_privkey, {} )
bob_account = HydrusEncryption.HydrusOTRAccount( bob, bob_privkey, {} )
alice_context = HydrusEncryption.HydrusOTRContext( alice_account, bob )
bob_context = HydrusEncryption.HydrusOTRContext( bob_account, alice )
catcher = Catcher()
#
self.assertEqual( alice_context.state, potr.context.STATE_PLAINTEXT )
self.assertEqual( bob_context.state, potr.context.STATE_PLAINTEXT )
m = alice_context.sendMessage( potr.context.FRAGMENT_SEND_ALL, '' )
res = bob_context.receiveMessage( m, catcher )
m = catcher.GetLastWrite()
res = alice_context.receiveMessage( m, catcher )
m = catcher.GetLastWrite()
res = bob_context.receiveMessage( m, catcher )
m = catcher.GetLastWrite()
res = alice_context.receiveMessage( m, catcher )
m = catcher.GetLastWrite()
res = bob_context.receiveMessage( m, catcher )
self.assertEqual( alice_context.state, potr.context.STATE_ENCRYPTED )
self.assertEqual( bob_context.state, potr.context.STATE_ENCRYPTED )
self.assertEqual( bob_privkey.getPublicPayload(), alice_context.getCurrentKey().getPublicPayload() )
self.assertEqual( alice_privkey.getPublicPayload(), bob_context.getCurrentKey().getPublicPayload() )
#
self.assertEqual( alice_context.getCurrentTrust(), None )
alice_context.setCurrentTrust( 'verified' )
self.assertEqual( alice_context.getCurrentTrust(), 'verified' )
[ ( args, kwargs ) ] = HC.app.GetWrite( 'otr_trusts' )
self.assertEqual( args, ( alice, { bob : { alice_context.getCurrentKey().cfingerprint() : 'verified' } } ) )
self.assertEqual( bob_context.getCurrentTrust(), None )
bob_context.setCurrentTrust( 'verified' )
self.assertEqual( bob_context.getCurrentTrust(), 'verified' )
[ ( args, kwargs ) ] = HC.app.GetWrite( 'otr_trusts' )
self.assertEqual( args, ( bob, { alice : { bob_context.getCurrentKey().cfingerprint() : 'verified' } } ) )
#
m = alice_context.sendMessage( potr.context.FRAGMENT_SEND_ALL, 'hello bob', appdata = catcher )
m = catcher.GetLastWrite()
res = bob_context.receiveMessage( m, catcher )
( message, gumpf ) = res
self.assertEqual( message, 'hello bob' )
#
m = bob_context.sendMessage( potr.context.FRAGMENT_SEND_ALL, 'hello alice', appdata = catcher )
m = catcher.GetLastWrite()
res = alice_context.receiveMessage( m, catcher )
( message, gumpf ) = res
self.assertEqual( message, 'hello alice' )

View File

@ -14,7 +14,6 @@ import threading
import unittest
from twisted.internet import reactor
class TestServer( unittest.TestCase ):
@classmethod

View File

@ -49,4 +49,4 @@ HC.shutdown = True
reactor.callFromThread( reactor.stop )
HC.pubsub.pubimmediate( 'shutdown' )
HC.pubsub.WXpubimmediate( 'shutdown' )

View File

@ -9,6 +9,7 @@ from include import TestDialogs
from include import TestDB
from include import TestFunctions
from include import TestHydrusDownloading
from include import TestHydrusEncryption
from include import TestHydrusSessions
from include import TestHydrusTags
from include import TestServer
@ -58,6 +59,7 @@ class App( wx.App ):
if run_all or only_run == 'daemons': suites.append( unittest.TestLoader().loadTestsFromModule( TestClientDaemons ) )
if run_all or only_run == 'dialogs': suites.append( unittest.TestLoader().loadTestsFromModule( TestDialogs ) )
if run_all or only_run == 'db': suites.append( unittest.TestLoader().loadTestsFromModule( TestDB ) )
if run_all or only_run == 'encryption': suites.append( unittest.TestLoader().loadTestsFromModule( TestHydrusEncryption ) )
if run_all or only_run == 'functions': suites.append( unittest.TestLoader().loadTestsFromModule( TestFunctions ) )
if run_all or only_run == 'downloading': suites.append( unittest.TestLoader().loadTestsFromModule( TestHydrusDownloading ) )
if run_all or only_run == 'sessions': suites.append( unittest.TestLoader().loadTestsFromModule( TestHydrusSessions ) )
@ -136,4 +138,4 @@ if __name__ == '__main__':
HC.shutdown = True
HC.pubsub.pubimmediate( 'shutdown' )
HC.pubsub.WXpubimmediate( 'shutdown' )