Version 135

This commit is contained in:
Hydrus 2014-10-29 16:39:01 -05:00
parent 5662e56649
commit 33bfb28972
12 changed files with 298 additions and 87 deletions

View File

@ -8,6 +8,25 @@
<div class="content">
<h3>changelog</h3>
<ul>
<li><h3>version 135</h3></li>
<ul>
<li>added a menu option to any tag's right-click menu to open a new search page for that tag</li>
<li>added a subscription cache to the client database to speed up subs checking</li>
<li>added a filter to the message popup system so those annoying and 99% pointless PyDeadObjectErrors will not display. they will still be written to the log</li>
<li>cleaned up a couple of temporary file deletion errors</li>
<li>improved some more temp file deletion error handling</li>
<li>fixed a bug that I think was stopping file repositories from being deleted</li>
<li>improved the manage services db edit log</li>
<li>fixed a bad comparison that was causing superfluous edit actions after a manage services dialog ok</li>
<li>fixed a new bug related to displaying non text in the popup system</li>
<li>added a 'just woke from sleep' check to all daemons, so CPU heavy stuff like repository sync will not initialise if you just woke your computer. the grace period lasts about ten minutes</li>
<li>retuned the way the subscrption daemon initialises (it'll now wait two minutes after startup before firing)</li>
<li>fixed a typo that was causing fatten service info to fire more often than it should</li>
<li>added a yes/no warning to options ok when the thumbnail dimensions have been changed</li>
<li>added a popup message when thumbnail dimensions have been changed to report on deletion progress</li>
<li>added account testing to my server db testing suite</li>
<li>improved the security of the registration_key->access_key transaction; it'll now generate a new access_key with every call</li>
</ul>
<li><h3>version 134</h3></li>
<ul>
<li>updated to wx 3.0.1.1</li>

View File

@ -7,7 +7,7 @@
<body>
<div class="content">
<p><b>access key</b> A 32-byte identifier-password that gives you access to an account that has certain permissions with a repository. Usually represented as a 64-character hex string like so: 7ce4dbf18f7af8b420ee942bae42030aab344e91dc0e839260fcd71a4c9879e3</p>
<p><b>account key</b> A 32-byte identifier for a hydrus service account. Usually represented as a 64-character hex string like so: 0c3b554cb6fe7d55c945df88b2f6cf6ca0ae40824bca7534aa2fd483da7fb219</p>
<p><b>account key</b> A 32-byte identifier for a hydrus service account. Usually represented as a 64-character hex string like so: 207d592682a7962564d52d2480f05e72a272443017553cedbd8af0fecc7b6e0a</p>
<p><b>address</b> The pairing of both a server's host (be that an IP or a domain) with its port number, like so: 74.125.225.18:80, or google.com:80</p>
<p><b>archive</b> The store of files you have chosen to keep.</p>
<p><b>file repository</b> A service in the hydrus network that hosts files.</p>

View File

@ -161,7 +161,8 @@ The database will be locked while the backup occurs, which may lock up your gui
os.chmod( path, stat.S_IWRITE )
function_called( path ) # try again
try: function_called( path ) # try again
except: pass
if os.path.exists( HC.TEMP_DIR ): shutil.rmtree( HC.TEMP_DIR, onerror = make_temp_files_deletable )
@ -248,6 +249,8 @@ The database will be locked while the backup occurs, which may lock up your gui
self._db.StartDaemons()
def JustWokeFromSleep( self ): return self._just_woke_from_sleep
def MaintainDB( self ):
sys.stdout.flush()
@ -270,14 +273,16 @@ The database will be locked while the backup occurs, which may lock up your gui
for service in services: self.Read( 'service_info', service.GetServiceKey() )
self._timestamps[ 'service_info_cache_fatten' ] = HC.GetNow()
self._timestamps[ 'last_service_info_cache_fatten' ] = HC.GetNow()
HC.pubsub.pub( 'clear_closed_pages' )
def OnInit( self ):
self.SetAssertMode(wx.PYAPP_ASSERT_SUPPRESS)
self.SetAssertMode( wx.PYAPP_ASSERT_SUPPRESS )
HC.app = self
HC.http = HydrusNetworking.HTTPConnectionManager()
@ -285,6 +290,8 @@ The database will be locked while the backup occurs, which may lock up your gui
self._timestamps[ 'boot' ] = HC.GetNow()
self._just_woke_from_sleep = False
self._local_service = None
self._booru_service = None
@ -546,9 +553,10 @@ Once it is done, the client will restart.'''
self._timestamps[ 'last_check_idle_time' ] = HC.GetNow()
# this tests if we probably just woke up from a sleep
if HC.GetNow() - last_time_this_ran > MAINTENANCE_PERIOD + ( 5 * 60 ): return
if HC.GetNow() - last_time_this_ran > MAINTENANCE_PERIOD + ( 5 * 60 ): self._just_woke_from_sleep = True
else: self._just_woke_from_sleep = False
if self.CurrentlyIdle(): self.MaintainDB()
if not self._just_woke_from_sleep and self.CurrentlyIdle(): self.MaintainDB()
def WaitUntilGoodTimeToUseGUIThread( self ):

View File

@ -256,7 +256,8 @@ class MessageDB( object ):
except: pass
os.remove( temp_path )
try: os.remove( temp_path )
except: pass # sometimes this fails, I think due to old handles not being cleaned up fast enough. np--it'll be cleaned up later
hash_ids = self._GetHashIds( c, attachment_hashes )
@ -1588,6 +1589,8 @@ class ServiceDB( FileDB, MessageDB, TagDB, RatingDB ):
if dump_name is None: c.execute( 'DELETE FROM yaml_dumps WHERE dump_type = ?;', ( dump_type, ) )
else:
if dump_type == YAML_DUMP_ID_SUBSCRIPTION and dump_name in self._subscriptions_cache: del self._subscriptions_cache[ dump_name ]
if dump_type == YAML_DUMP_ID_LOCAL_BOORU: dump_name = dump_name.encode( 'hex' )
c.execute( 'DELETE FROM yaml_dumps WHERE dump_type = ? AND dump_name = ?;', ( dump_type, dump_name ) )
@ -2965,6 +2968,8 @@ class ServiceDB( FileDB, MessageDB, TagDB, RatingDB ):
else:
if dump_type == YAML_DUMP_ID_SUBSCRIPTION and dump_name in self._subscriptions_cache: return self._subscriptions_cache[ dump_name ]
if dump_type == YAML_DUMP_ID_LOCAL_BOORU: dump_name = dump_name.encode( 'hex' )
result = c.execute( 'SELECT dump FROM yaml_dumps WHERE dump_type = ? AND dump_name = ?;', ( dump_type, dump_name ) ).fetchone()
@ -2981,6 +2986,8 @@ class ServiceDB( FileDB, MessageDB, TagDB, RatingDB ):
else: ( result, ) = result
if dump_type == YAML_DUMP_ID_SUBSCRIPTION: self._subscriptions_cache[ dump_name ] = result
return result
@ -3771,6 +3778,8 @@ class ServiceDB( FileDB, MessageDB, TagDB, RatingDB ):
def _SetYAMLDump( self, c, dump_type, dump_name, data ):
if dump_type == YAML_DUMP_ID_SUBSCRIPTION: self._subscriptions_cache[ dump_name ] = data
if dump_type == YAML_DUMP_ID_LOCAL_BOORU: dump_name = dump_name.encode( 'hex' )
c.execute( 'DELETE FROM yaml_dumps WHERE dump_type = ? AND dump_name = ?;', ( dump_type, dump_name ) )
@ -4197,17 +4206,19 @@ class ServiceDB( FileDB, MessageDB, TagDB, RatingDB ):
recalc_combined_mappings = False
message = None
for ( action, details ) in edit_log:
for entry in edit_log:
action = entry.GetAction()
if action == HC.ADD:
( service_key, service_type, name, info ) = details
( service_key, service_type, name, info ) = entry.GetData()
self._AddService( c, service_key, service_type, name, info )
elif action == HC.DELETE:
service_key = details
service_key = entry.GetIdentifier()
service_id = self._GetServiceId( c, service_key )
@ -4249,7 +4260,7 @@ class ServiceDB( FileDB, MessageDB, TagDB, RatingDB ):
elif action == HC.EDIT:
( service_key, service_type, new_name, info_update ) = details
( service_key, service_type, new_name, info_update ) = entry.GetData()
service_id = self._GetServiceId( c, service_key )
@ -4309,6 +4320,8 @@ class DB( ServiceDB ):
self._jobs = Queue.PriorityQueue()
self._pubsubs = []
self._subscriptions_cache = {}
self._currently_doing_job = False
self._InitDB()
@ -4659,12 +4672,23 @@ class DB( ServiceDB ):
if resize_thumbs:
thumbnail_paths = [ path for path in CC.IterateAllThumbnailPaths() if path.endswith( '_resized' ) ]
message = HC.Message( HC.MESSAGE_TYPE_TEXT, { 'text' : 'deleting old thumbnails: initialising' } )
for path in thumbnail_paths: os.remove( path )
HC.pubsub.pub( 'message', message )
thumbnail_paths = ( path for path in CC.IterateAllThumbnailPaths() if path.endswith( '_resized' ) )
for ( i, path ) in enumerate( thumbnail_paths ):
os.remove( path )
if i % 100 == 0: message.SetInfo( 'text', 'deleting old thumbnails: done ' + HC.ConvertIntToPrettyString( i ) )
self.pub_after_commit( 'thumbnail_resize' )
message.SetInfo( 'text', 'deleting old thumbnails: done' )
self.pub_after_commit( 'notify_new_options' )
@ -4734,31 +4758,6 @@ class DB( ServiceDB ):
def _UpdateDB( self, c, version ):
if version == 84:
boorus = []
name = 'e621'
search_url = 'https://e621.net/post/index?page=%index%&tags=%tags%'
search_separator = '%20'
advance_by_page_num = True
thumb_classname = 'thumb'
image_id = None
image_data = 'Download'
tag_classnames_to_namespaces = { 'tag-type-general categorized-tag' : '', 'tag-type-character categorized-tag' : 'character', 'tag-type-copyright categorized-tag' : 'series', 'tag-type-artist categorized-tag' : 'creator', 'tag-type-species categorized-tag' : 'species' }
boorus.append( CC.Booru( name, search_url, search_separator, advance_by_page_num, thumb_classname, image_id, image_data, tag_classnames_to_namespaces ) )
for booru in boorus:
name = booru.GetName()
c.execute( 'DELETE FROM boorus WHERE name = ?;', ( name, ) )
c.execute( 'INSERT INTO boorus VALUES ( ?, ? );', ( name, booru ) )
if version == 87:
c.execute( 'CREATE TABLE namespace_blacklists ( service_id INTEGER PRIMARY KEY REFERENCES services ON DELETE CASCADE, blacklist INTEGER_BOOLEAN, namespaces TEXT_YAML );' )
@ -5426,7 +5425,6 @@ class DB( ServiceDB ):
elif action == 'status_num_inbox': result = self._DoStatusNumInbox( c, *args, **kwargs )
elif action == 'subscription_names': result = self._GetYAMLDumpNames( c, YAML_DUMP_ID_SUBSCRIPTION )
elif action == 'subscription': result = self._GetYAMLDump( c, YAML_DUMP_ID_SUBSCRIPTION, *args, **kwargs )
elif action == 'subscriptions': result = self._GetYAMLDump( c, YAML_DUMP_ID_SUBSCRIPTION, *args, **kwargs )
elif action == 'tag_censorship': result = self._GetTagCensorship( c, *args, **kwargs )
elif action == 'tag_parents': result = self._GetTagParents( c, *args, **kwargs )
elif action == 'tag_siblings': result = self._GetTagSiblings( c, *args, **kwargs )
@ -5629,7 +5627,7 @@ class DB( ServiceDB ):
HydrusThreading.DAEMONWorker( 'ResizeThumbnails', DAEMONResizeThumbnails, period = 3600 * 24, init_wait = 600 )
HydrusThreading.DAEMONWorker( 'SynchroniseAccounts', DAEMONSynchroniseAccounts, ( 'permissions_are_stale', ) )
HydrusThreading.DAEMONWorker( 'SynchroniseRepositories', DAEMONSynchroniseRepositories, ( 'notify_restart_repo_sync_daemon', 'notify_new_permissions' ) )
HydrusThreading.DAEMONWorker( 'SynchroniseSubscriptions', DAEMONSynchroniseSubscriptions, ( 'notify_restart_subs_sync_daemon', 'notify_new_subscriptions' ), period = 360 )
HydrusThreading.DAEMONWorker( 'SynchroniseSubscriptions', DAEMONSynchroniseSubscriptions, ( 'notify_restart_subs_sync_daemon', 'notify_new_subscriptions' ), period = 360, init_wait = 120 )
HydrusThreading.DAEMONWorker( 'UPnP', DAEMONUPnP, ( 'notify_new_upnp_mappings', ), pre_callable_wait = 10 )
HydrusThreading.DAEMONQueue( 'FlushRepositoryUpdates', DAEMONFlushServiceUpdates, 'service_updates_delayed', period = 5 )
@ -5894,7 +5892,8 @@ def DAEMONDownloadFiles():
HC.app.WriteSynchronous( 'import_file', temp_path )
os.remove( temp_path )
try: os.remove( temp_path )
except: pass # sometimes this fails, I think due to old handles not being cleaned up fast enough. np--it'll be cleaned up later
break
@ -6770,7 +6769,8 @@ def DAEMONSynchroniseSubscriptions():
( status, hash ) = HC.app.WriteSynchronous( 'import_file', temp_path, advanced_import_options = advanced_import_options, service_keys_to_tags = service_keys_to_tags, url = url )
os.remove( temp_path )
try: os.remove( temp_path )
except: pass # sometimes this fails, I think due to old handles not being cleaned up fast enough. np--it'll be cleaned up later
if status in ( 'successful', 'redundant' ): successful_hashes.add( hash )

View File

@ -348,7 +348,7 @@ class FrameGUI( ClientGUICommon.FrameThatResizes ):
info[ 'port' ] = 45871
info[ 'access_key' ] = '4a285629721ca442541ef2c15ea17d1f7f7578b0c3f4f5f2a05f8f0ab297786f'.decode( 'hex' )
edit_log.append( ( HC.ADD, ( service_key, service_type, name, info ) ) )
edit_log.append( HC.EditLogActionAdd( ( service_key, service_type, name, info ) ) )
service_key = os.urandom( 32 )
service_type = HC.FILE_REPOSITORY
@ -360,7 +360,7 @@ class FrameGUI( ClientGUICommon.FrameThatResizes ):
info[ 'port' ] = 45872
info[ 'access_key' ] = '8f8a3685abc19e78a92ba61d84a0482b1cfac176fd853f46d93fe437a95e40a5'.decode( 'hex' )
edit_log.append( ( HC.ADD, ( service_key, service_type, name, info ) ) )
edit_log.append( HC.EditLogActionAdd( ( service_key, service_type, name, info ) ) )
HC.app.Write( 'update_services', edit_log )
@ -443,7 +443,7 @@ class FrameGUI( ClientGUICommon.FrameThatResizes ):
info[ 'port' ] = port
info[ 'access_key' ] = ''
edit_log.append( ( HC.ADD, ( admin_service_key, service_type, name, info ) ) )
edit_log.append( HC.EditLogActionAdd( ( admin_service_key, service_type, name, info ) ) )
HC.app.Write( 'update_services', edit_log )
@ -481,7 +481,7 @@ class FrameGUI( ClientGUICommon.FrameThatResizes ):
info_update = { 'access_key' : access_key }
edit_log = [ ( HC.EDIT, ( admin_service_key, service_type, name, info_update ) ) ]
edit_log = [ HC.EditLogActionEdit( admin_service_key, ( admin_service_key, service_type, name, info_update ) ) ]
HC.app.Write( 'update_services', edit_log )
@ -2870,7 +2870,7 @@ class FrameReviewServices( ClientGUICommon.Frame ):
info_update = { 'access_key' : access_key }
edit_log = [ ( HC.EDIT, ( service_key, service_type, name, info_update ) ) ]
edit_log = [ HC.EditLogActionEdit( service_key, ( service_key, service_type, name, info_update ) ) ]
HC.app.Write( 'update_services', edit_log )

View File

@ -2010,13 +2010,13 @@ class ListBox( wx.ScrolledWindow ):
term = self._strings_to_terms[ self._ordered_strings[ self._current_selected_index ] ]
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'copy_term' ), 'copy ' + term )
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'copy_term' ), 'copy "' + term + '"' )
if ':' in term:
sub_term = term.split( ':', 1 )[1]
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'copy_sub_term' ), 'copy ' + sub_term )
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'copy_sub_term' ), 'copy "' + sub_term + '"' )
self.PopupMenu( menu )
@ -2831,7 +2831,7 @@ class PopupMessageText( PopupMessage ):
def Update( self ):
text = self._ProcessText( self._message.GetInfo( 'text' ) )
text = self._ProcessText( HC.u( self._message.GetInfo( 'text' ) ) )
if self._text.GetLabel() != text: self._text.SetLabel( text )
@ -2951,6 +2951,20 @@ class PopupMessageManager( wx.Frame ):
except: print( repr( message_string ) )
def _ShouldDisplayMessage( self, message ):
message_type = message.GetType()
if message_type == HC.MESSAGE_TYPE_ERROR:
( etype, value, trace ) = message.GetInfo( 'error' )
if etype == wx.PyDeadObjectError: return False
return True
def _SizeAndPositionAndShow( self ):
try:
@ -3001,9 +3015,12 @@ class PopupMessageManager( wx.Frame ):
self._PrintMessage( message )
self._pending_messages.append( message )
self._CheckPending()
if self._ShouldDisplayMessage( message ):
self._pending_messages.append( message )
self._CheckPending()
except:
@ -4096,6 +4113,12 @@ class TagsBox( ListBox ):
elif command == 'copy_all_tags': HC.pubsub.pub( 'clipboard', 'text', os.linesep.join( self._GetAllTagsForClipboard() ) )
elif command == 'copy_all_tags_with_counts': HC.pubsub.pub( 'clipboard', 'text', os.linesep.join( self._GetAllTagsForClipboard( with_counts = True ) ) )
elif command == 'new_search_page_with_term':
term = self._strings_to_terms[ self._ordered_strings[ self._current_selected_index ] ]
HC.pubsub.pub( 'new_page_query', HC.LOCAL_FILE_SERVICE_KEY, initial_predicates = [ HC.Predicate( HC.PREDICATE_TYPE_TAG, ( '+', term ) ) ] )
elif command in ( 'parent', 'sibling' ):
tag = self._strings_to_terms[ self._ordered_strings[ self._current_selected_index ] ]
@ -4130,6 +4153,18 @@ class TagsBox( ListBox ):
menu = wx.Menu()
if self._current_selected_index is not None:
term = self._strings_to_terms[ self._ordered_strings[ self._current_selected_index ] ]
if type( term ) in ( str, unicode ):
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'new_search_page_with_term' ), 'open a new search page for "' + term + '"' )
menu.AppendSeparator()
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'copy_all_tags' ), 'copy all tags' )
if self.has_counts: menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'copy_all_tags_with_counts' ), 'copy all tags with counts' )
@ -4139,13 +4174,13 @@ class TagsBox( ListBox ):
if type( term ) in ( str, unicode ):
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'copy_term' ), 'copy ' + term )
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'copy_term' ), 'copy "' + term + '"' )
if ':' in term:
sub_term = term.split( ':', 1 )[1]
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'copy_sub_term' ), 'copy ' + sub_term )
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'copy_sub_term' ), 'copy "' + sub_term + '"' )
menu.AppendSeparator()

View File

@ -3759,7 +3759,19 @@ class DialogManageOptions( ClientGUIDialogs.Dialog ):
HC.options[ 'preview_cache_size' ] = self._preview_cache_size.GetValue() * 1048576
HC.options[ 'fullscreen_cache_size' ] = self._fullscreen_cache_size.GetValue() * 1048576
HC.options[ 'thumbnail_dimensions' ] = [ self._thumbnail_width.GetValue(), self._thumbnail_height.GetValue() ]
new_thumbnail_dimensions = [ self._thumbnail_width.GetValue(), self._thumbnail_height.GetValue() ]
if new_thumbnail_dimensions != HC.options[ 'thumbnail_dimensions' ]:
text = 'You have changed the thumbnail dimensions, which will mean deleting all the old resized thumbnails right now, during which time the database will be locked. If you have tens or hundreds of thousands of files, this could take a long time.'
text += os.linesep * 2
text += 'Are you sure you want to change your thumbnail dimensions?'
with ClientGUIDialogs.DialogYesNo( self, text ) as dlg:
if dlg.ShowModal() == wx.ID_YES: HC.options[ 'thumbnail_dimensions' ] = new_thumbnail_dimensions
HC.options[ 'num_autocomplete_chars' ] = self._num_autocomplete_chars.GetValue()
@ -4941,7 +4953,7 @@ class DialogManageServices( ClientGUIDialogs.Dialog ):
info[ 'upper' ] = 5
self._edit_log.append( ( HC.ADD, ( service_key, service_type, name, info ) ) )
self._edit_log.append( HC.EditLogActionAdd( ( service_key, service_type, name, info ) ) )
page = self._Panel( services_listbook, service_key, service_type, name, info )
@ -5022,7 +5034,12 @@ class DialogManageServices( ClientGUIDialogs.Dialog ):
for page in all_pages:
if page.HasChanges(): self._edit_log.append( ( HC.EDIT, page.GetInfo() ) )
if page.HasChanges():
( service_key, service_type, name, info ) = page.GetInfo()
self._edit_log.append( HC.EditLogActionEdit( service_key, ( service_key, service_type, name, info ) ) )
@ -5068,15 +5085,14 @@ class DialogManageServices( ClientGUIDialogs.Dialog ):
with ClientGUIDialogs.DialogYesNo( self, text ) as dlg:
if dlg.ShowModal() == wx.ID_YES:
self._edit_log.append( ( HC.DELETE, service_key ) )
services_listbook.DeleteCurrentPage()
if dlg.ShowModal() != wx.ID_YES: return
self._edit_log.append( HC.EditLogActionDelete( service_key ) )
services_listbook.DeleteCurrentPage()
@ -5152,7 +5168,7 @@ class DialogManageServices( ClientGUIDialogs.Dialog ):
else:
self._edit_log.append( ( HC.ADD, ( service_key, service_type, name, info ) ) )
self._edit_log.append( HC.EditLogActionAdd( ( service_key, service_type, name, info ) ) )
page = self._Panel( services_listbook, service_key, service_type, name, info )
@ -5395,8 +5411,6 @@ class DialogManageServices( ClientGUIDialogs.Dialog ):
if name == '': raise Exception( 'Please enter a name' )
info = {}
if service_type in HC.REMOTE_SERVICES:
connection_string = self._service_credentials.GetValue()

View File

@ -64,7 +64,7 @@ options = {}
# Misc
NETWORK_VERSION = 15
SOFTWARE_VERSION = 134
SOFTWARE_VERSION = 135
UNSCALED_THUMBNAIL_DIMENSIONS = ( 200, 200 )
@ -1753,6 +1753,56 @@ class ContentUpdate( object ):
def ToTuple( self ): return ( self._data_type, self._action, self._row )
class EditLogAction( object ):
yaml_tag = u'!EditLogAction'
def __init__( self, action ): self._action = action
def GetAction( self ): return self._action
class EditLogActionAdd( EditLogAction ):
yaml_tag = u'!EditLogActionAdd'
def __init__( self, data ):
EditLogAction.__init__( self, ADD )
self._data = data
def GetData( self ): return self._data
class EditLogActionDelete( EditLogAction ):
yaml_tag = u'!EditLogActionDelete'
def __init__( self, identifier ):
EditLogAction.__init__( self, DELETE )
self._identifier = identifier
def GetIdentifier( self ): return self._identifier
class EditLogActionEdit( EditLogAction ):
yaml_tag = u'!EditLogActionEdit'
def __init__( self, identifier, data ):
EditLogAction.__init__( self, EDIT )
self._identifier = identifier
self._data = data
def GetData( self ): return self._data
def GetIdentifier( self ): return self._identifier
class JobDatabase( object ):
yaml_tag = u'!JobDatabase'

View File

@ -57,6 +57,13 @@ class DAEMONQueue( DAEMON ):
self._event.clear()
while HC.app.JustWokeFromSleep():
if HC.shutdown: return
time.sleep( 10 )
items = []
while not self._queue.empty(): items.append( self._queue.get() )
@ -93,6 +100,13 @@ class DAEMONWorker( DAEMON ):
time.sleep( self._pre_callable_wait )
while HC.app.JustWokeFromSleep():
if HC.shutdown: return
time.sleep( 10 )
try: self._callable()
except Exception as e:

View File

@ -108,6 +108,8 @@ class Controller( wx.App ):
def GetManager( self, manager_type ): return self._managers[ manager_type ]
def JustWokeFromSleep( self ): return False
def OnInit( self ):
HC.app = self

View File

@ -1121,10 +1121,17 @@ class ServiceDB( FileDB, MessageDB, TagDB ):
def _GetAccessKey( self, c, registration_key ):
try: ( access_key, ) = c.execute( 'SELECT access_key FROM registration_keys WHERE registration_key = ?;', ( sqlite3.Binary( hashlib.sha256( registration_key ).digest() ), ) ).fetchone()
# we generate a new access_key every time this is requested so that if the registration_key leaks, no one grab the access_key before the legit user does
# the reg_key is deleted when the last-requested access_key is used to create a session, which calls getaccountkeyfromaccesskey
try: ( one, ) = c.execute( 'SELECT 1 FROM registration_keys WHERE registration_key = ?;', ( sqlite3.Binary( hashlib.sha256( registration_key ).digest() ), ) ).fetchone()
except: raise HydrusExceptions.ForbiddenException( 'The service could not find that registration key in its database.' )
return access_key
new_access_key = os.urandom( HC.HYDRUS_KEY_LENGTH )
c.execute( 'UPDATE registration_keys SET access_key = ? WHERE registration_key = ?;', ( sqlite3.Binary( new_access_key ), sqlite3.Binary( hashlib.sha256( registration_key ).digest() ) ) )
return new_access_key
def _GetAccount( self, c, account_key ):

View File

@ -58,7 +58,8 @@ class TestClientDB( unittest.TestCase ):
os.chmod( path, stat.S_IWRITE )
function_called( path ) # try again
try: function_called( path ) # try again
except: pass
if os.path.exists( HC.DB_DIR ): shutil.rmtree( HC.DB_DIR, onerror = make_temp_files_deletable )
@ -1016,10 +1017,10 @@ class TestClientDB( unittest.TestCase ):
edit_log = []
edit_log.append( ( HC.ADD, new_tag_repo ) )
edit_log.append( ( HC.ADD, other_new_tag_repo ) )
edit_log.append( ( HC.ADD, new_local_like ) )
edit_log.append( ( HC.ADD, new_local_numerical ) )
edit_log.append( HC.EditLogActionAdd( new_tag_repo ) )
edit_log.append( HC.EditLogActionAdd( other_new_tag_repo ) )
edit_log.append( HC.EditLogActionAdd( new_local_like ) )
edit_log.append( HC.EditLogActionAdd( new_local_numerical ) )
self._write( 'update_services', edit_log )
@ -1043,8 +1044,8 @@ class TestClientDB( unittest.TestCase ):
edit_log = []
edit_log.append( ( HC.DELETE, new_local_like[0] ) )
edit_log.append( ( HC.EDIT, other_new_tag_repo_updated ) )
edit_log.append( HC.EditLogActionDelete( new_local_like[0] ) )
edit_log.append( HC.EditLogActionEdit( other_new_tag_repo_updated[0], other_new_tag_repo_updated ) )
self._write( 'update_services', edit_log )
@ -1056,7 +1057,7 @@ class TestClientDB( unittest.TestCase ):
edit_log = []
edit_log.append( ( HC.DELETE, other_new_tag_repo_updated[0] ) )
edit_log.append( HC.EditLogActionDelete( other_new_tag_repo_updated[0] ) )
self._write( 'update_services', edit_log )
@ -1143,7 +1144,8 @@ class TestServerDB( unittest.TestCase ):
os.chmod( path, stat.S_IWRITE )
function_called( path ) # try again
try: function_called( path ) # try again
except: pass
if os.path.exists( HC.DB_DIR ): shutil.rmtree( HC.DB_DIR, onerror = make_temp_files_deletable )
@ -1155,15 +1157,75 @@ class TestServerDB( unittest.TestCase ):
def _test_account_creation( self ):
# create new account types
# edit the account types
# create rkeys
# create akeys
# test successive rkey fetch gives new akeys
# get session with akey
# make sure rkey is now dead
result = self._read( 'account_types', self._tag_service_key )
pass
( service_admin_at, ) = result
self.assertEqual( service_admin_at.GetTitle(), 'service admin' )
self.assertEqual( service_admin_at.GetPermissions(), [ HC.GET_DATA, HC.POST_DATA, HC.POST_PETITIONS, HC.RESOLVE_PETITIONS, HC.MANAGE_USERS, HC.GENERAL_ADMIN ] )
self.assertEqual( service_admin_at.GetMaxBytes(), None )
self.assertEqual( service_admin_at.GetMaxRequests(), None )
#
user_at = HC.AccountType( 'user', [ HC.GET_DATA, HC.POST_DATA ], ( 50000, 500 ) )
edit_log = [ ( HC.ADD, user_at ) ]
self._write( 'account_types', self._tag_service_key, edit_log )
result = self._read( 'account_types', self._tag_service_key )
( at_1, at_2 ) = result
d = { at_1.GetTitle() : at_1, at_2.GetTitle() : at_2 }
at = d[ 'user' ]
self.assertEqual( at.GetPermissions(), [ HC.GET_DATA, HC.POST_DATA ] )
self.assertEqual( at.GetMaxBytes(), 50000 )
self.assertEqual( at.GetMaxRequests(), 500 )
#
user_at_diff = HC.AccountType( 'user different', [ HC.GET_DATA ], ( 40000, None ) )
edit_log = [ ( HC.EDIT, ( 'user', user_at_diff ) ) ]
self._write( 'account_types', self._tag_service_key, edit_log )
result = self._read( 'account_types', self._tag_service_key )
( at_1, at_2 ) = result
d = { at_1.GetTitle() : at_1, at_2.GetTitle() : at_2 }
at = d[ 'user different' ]
self.assertEqual( at.GetPermissions(), [ HC.GET_DATA ] )
self.assertEqual( at.GetMaxBytes(), 40000 )
self.assertEqual( at.GetMaxRequests(), None )
#
r_keys = self._read( 'registration_keys', self._tag_service_key, 5, 'user different', 86400 * 365 )
self.assertEqual( len( r_keys ), 5 )
for r_key in r_keys: self.assertEqual( len( r_key ), 32 )
r_key = r_keys[0]
access_key = self._read( 'access_key', r_key )
access_key_2 = self._read( 'access_key', r_key )
self.assertNotEqual( access_key, access_key_2 )
self.assertRaises( HydrusExceptions.ForbiddenException, self._read, 'account_key_from_access_key', self._tag_service_key, access_key )
account_key = self._read( 'account_key_from_access_key', self._tag_service_key, access_key_2 )
self.assertRaises( HydrusExceptions.ForbiddenException, self._read, 'access_key', r_key )
def _test_content_creation( self ):