from . import ClientConstants as CC from . import ClientData from . import ClientDefaults from . import ClientGUICommon from . import ClientGUIDialogs from . import ClientGUIDialogsQuick from . import ClientGUIMenus from . import ClientGUIControls from . import ClientGUIFunctions from . import ClientGUIListBoxes from . import ClientGUIListCtrl from . import ClientGUIParsing from . import ClientGUIScrolledPanels from . import ClientGUIScrolledPanelsEdit from . import ClientGUISerialisable from . import ClientGUITopLevelWindows from . import ClientImporting from . import ClientNetworking from . import ClientNetworkingBandwidth from . import ClientNetworkingContexts from . import ClientNetworkingDomain from . import ClientNetworkingLogin from . import ClientNetworkingJobs from . import ClientNetworkingSessions from . import ClientParsing from . import ClientPaths from . import ClientSerialisable from . import ClientThreading from . import HydrusConstants as HC from . import HydrusData from . import HydrusExceptions from . import HydrusGlobals as HG from . import HydrusSerialisable from . import HydrusTags from . import HydrusText import itertools import os import re import threading import traceback import time import wx class EditLoginCredentialsPanel( ClientGUIScrolledPanels.EditPanel ): def __init__( self, parent, credential_definitions, credentials ): ClientGUIScrolledPanels.EditPanel.__init__( self, parent ) # self._control_data = [] rows = [] credential_definitions = list( credential_definitions ) credential_definitions.sort( key = lambda cd: cd.ShouldHide() ) for credential_definition in credential_definitions: if credential_definition.ShouldHide(): style = wx.TE_PASSWORD else: style = 0 control = wx.TextCtrl( self, style = style ) name = credential_definition.GetName() if name in credentials: control.SetValue( credentials[ name ] ) control.Bind( wx.EVT_TEXT, self.EventKey ) control_st = ClientGUICommon.BetterStaticText( self ) self._control_data.append( ( credential_definition, control, control_st ) ) hbox = wx.BoxSizer( wx.HORIZONTAL ) hbox.Add( control, CC.FLAGS_EXPAND_BOTH_WAYS ) hbox.Add( control_st, CC.FLAGS_EXPAND_BOTH_WAYS ) rows.append( ( credential_definition.GetName() + ': ', hbox ) ) gridbox = ClientGUICommon.WrapInGrid( self, rows ) self.SetSizer( gridbox ) self._UpdateSts() def _UpdateSts( self ): for ( credential_definition, control, control_st ) in self._control_data: value = control.GetValue() colour = ( 127, 0, 0 ) if value == '': string_match = credential_definition.GetStringMatch() if string_match is None: st_label = '' else: st_label = string_match.ToString() else: try: credential_definition.Test( value ) st_label = 'looks good \u2713' colour = ( 0, 127, 0 ) except Exception as e: st_label = str( e ) control_st.SetLabelText( st_label ) control_st.SetForegroundColour( colour ) def CanOK( self ): veto_errors = [] for ( credential_definition, control, control_st ) in self._control_data: name = credential_definition.GetName() value = control.GetValue() if value == '': veto_errors.append( 'Value for ' + name + ' is blank!' ) else: try: credential_definition.Test( value ) except Exception as e: veto_errors.append( 'For ' + name + ': ' + str( e ) ) if len( veto_errors ) > 0: message = 'These values are invalid--are you sure this is ok?' message += os.linesep * 2 message += os.linesep.join( veto_errors ) with ClientGUIDialogs.DialogYesNo( self, message ) as dlg: if dlg.ShowModal() != wx.ID_YES: return False return True def EventKey( self, event ): self._UpdateSts() event.Skip() def GetValue( self ): credentials = {} for ( credential_definition, control, control_st ) in self._control_data: name = credential_definition.GetName() value = control.GetValue() credentials[ name ] = value return credentials class EditLoginCredentialDefinitionPanel( ClientGUIScrolledPanels.EditPanel ): def __init__( self, parent, credential_definition ): ClientGUIScrolledPanels.EditPanel.__init__( self, parent ) # self._name = wx.TextCtrl( self ) self._credential_type = ClientGUICommon.BetterChoice( self ) for credential_type in [ ClientNetworkingLogin.CREDENTIAL_TYPE_TEXT, ClientNetworkingLogin.CREDENTIAL_TYPE_PASS ]: self._credential_type.Append( ClientNetworkingLogin.credential_type_str_lookup[ credential_type ], credential_type ) string_match = credential_definition.GetStringMatch() self._string_match = ClientGUIControls.StringMatchButton( self, string_match ) # self._name.SetValue( credential_definition.GetName() ) self._credential_type.SetValue( credential_definition.GetType() ) # rows = [] rows.append( ( 'name: ', self._name ) ) rows.append( ( 'input type: ', self._credential_type ) ) rows.append( ( 'permitted input: ', self._string_match ) ) gridbox = ClientGUICommon.WrapInGrid( self, rows ) self.SetSizer( gridbox ) def GetValue( self ): name = self._name.GetValue() credential_type = self._credential_type.GetValue() string_match = self._string_match.GetValue() credential_definition = ClientNetworkingLogin.LoginCredentialDefinition( name = name, credential_type = credential_type, string_match = string_match ) return credential_definition class EditLoginsPanel( ClientGUIScrolledPanels.EditPanel ): def __init__( self, parent, engine, login_scripts, domains_to_login_info ): ClientGUIScrolledPanels.EditPanel.__init__( self, parent ) self._engine = engine self._login_scripts = login_scripts self._domains_to_login_after_ok = [] self._domains_and_login_info_panel = ClientGUIListCtrl.BetterListCtrlPanel( self ) columns = [ ( 'domain', 20 ), ( 'login script', -1 ), ( 'access given', 36 ), ( 'active?', 8 ), ( 'logged in now?', 28 ), ( 'validity', 28 ), ( 'recent error/delay?', 21 ) ] self._domains_and_login_info = ClientGUIListCtrl.BetterListCtrl( self._domains_and_login_info_panel, 'domains_to_login_info', 8, 36, columns, self._ConvertDomainAndLoginInfoListCtrlTuples, use_simple_delete = True, activation_callback = self._EditCredentials ) self._domains_and_login_info_panel.SetListCtrl( self._domains_and_login_info ) self._domains_and_login_info_panel.AddButton( 'add', self._Add ) self._domains_and_login_info_panel.AddDeleteButton() self._domains_and_login_info_panel.AddSeparator() self._domains_and_login_info_panel.AddButton( 'edit credentials', self._EditCredentials, enabled_check_func = self._CanEditCreds ) self._domains_and_login_info_panel.AddButton( 'change login script', self._EditLoginScript, enabled_only_on_selection = True ) self._domains_and_login_info_panel.AddButton( 'flip active', self._FlipActive, enabled_only_on_selection = True ) self._domains_and_login_info_panel.AddSeparator() self._domains_and_login_info_panel.AddButton( 'scrub invalidity', self._ScrubInvalidity, enabled_check_func = self._CanScrubInvalidity ) self._domains_and_login_info_panel.AddButton( 'scrub delays', self._ScrubDelays, enabled_check_func = self._CanScrubDelays ) self._domains_and_login_info_panel.NewButtonRow() self._domains_and_login_info_panel.AddButton( 'do login now', self._DoLogin, enabled_check_func = self._CanDoLogin ) self._domains_and_login_info_panel.AddButton( 'reset login (delete cookies)', self._ClearSessions, enabled_only_on_selection = True ) # listctrl_data = [] for ( login_domain, ( login_script_key_and_name, credentials, login_access_type, login_access_text, active, validity, validity_error_text, no_work_until, no_work_until_reason ) ) in list(domains_to_login_info.items()): credentials_tuple = tuple( credentials.items() ) domain_and_login_info = ( login_domain, login_script_key_and_name, credentials_tuple, login_access_type, login_access_text, active, validity, validity_error_text, no_work_until, no_work_until_reason ) listctrl_data.append( domain_and_login_info ) self._domains_and_login_info.AddDatas( listctrl_data ) self._domains_and_login_info.Sort( 0 ) # vbox = wx.BoxSizer( wx.VERTICAL ) warning = 'WARNING: Your credentials are stored in plaintext! For this and other reasons, I recommend you use throwaway accounts with hydrus!' warning_st = ClientGUICommon.BetterStaticText( self, warning ) warning_st.SetForegroundColour( ( 128, 0, 0 ) ) vbox.Add( warning_st, CC.FLAGS_CENTER ) vbox.Add( self._domains_and_login_info_panel, CC.FLAGS_EXPAND_BOTH_WAYS ) self.SetSizer( vbox ) def _Add( self ): if len( self._login_scripts ) == 0: wx.MessageBox( 'You have no login scripts, so you cannot add a new login!' ) return choice_tuples = [ ( login_script.GetName(), login_script ) for login_script in self._login_scripts ] try: login_script = ClientGUIDialogsQuick.SelectFromList( self, 'select the login script to use', choice_tuples ) except HydrusExceptions.CancelledException: return example_domains = set( login_script.GetExampleDomains() ) domains_in_use = { login_domain for ( login_domain, login_script_key_and_name, credentials_tuple, login_access_type, login_access_text, active, validity, validity_error_text, no_work_until, no_work_until_reason ) in self._domains_and_login_info.GetData() } available_examples = list( example_domains.difference( domains_in_use ) ) available_examples.sort() if len( available_examples ) > 0: choice_tuples = [ ( login_domain, login_domain ) for login_domain in available_examples ] choice_tuples.append( ( 'use other domain', None ) ) try: login_domain = ClientGUIDialogsQuick.SelectFromList( self, 'select the domain to use', choice_tuples, sort_tuples = False ) except HydrusExceptions.CancelledException: return if login_domain is not None: ( login_access_type, login_access_text ) = login_script.GetExampleDomainInfo( login_domain ) else: login_domain = None if login_domain is None: with ClientGUIDialogs.DialogTextEntry( self, 'enter the domain', default = 'example.com', allow_blank = False ) as dlg: if dlg.ShowModal() == wx.ID_OK: login_domain = dlg.GetValue() if login_domain in domains_in_use: wx.MessageBox( 'That domain is already in use!' ) return a_types = [ ClientNetworkingLogin.LOGIN_ACCESS_TYPE_EVERYTHING, ClientNetworkingLogin.LOGIN_ACCESS_TYPE_NSFW, ClientNetworkingLogin.LOGIN_ACCESS_TYPE_SPECIAL, ClientNetworkingLogin.LOGIN_ACCESS_TYPE_USER_PREFS_ONLY ] choice_tuples = [ ( ClientNetworkingLogin.login_access_type_str_lookup[ a_type ], a_type ) for a_type in a_types ] try: login_access_type = ClientGUIDialogsQuick.SelectFromList( self, 'select what type of access the login gives to this domain', choice_tuples, sort_tuples = False ) except HydrusExceptions.CancelledException: return login_access_text = ClientNetworkingLogin.login_access_type_default_description_lookup[ login_access_type ] with ClientGUIDialogs.DialogTextEntry( self, 'edit the access description, if needed', default = login_access_text, allow_blank = False ) as dlg: if dlg.ShowModal() == wx.ID_OK: login_access_text = dlg.GetValue() else: return else: return credential_definitions = login_script.GetCredentialDefinitions() credentials = {} if len( credential_definitions ) > 0: with ClientGUITopLevelWindows.DialogEdit( self, 'edit login' ) as dlg: panel = EditLoginCredentialsPanel( dlg, credential_definitions, credentials ) dlg.SetPanel( panel ) if dlg.ShowModal() == wx.ID_OK: credentials = panel.GetValue() else: return try: login_script.CheckCanLogin( credentials ) validity = ClientNetworkingLogin.VALIDITY_UNTESTED validity_error_text = '' # hacky: if there are creds, is at least one not empty string? creds_are_good = len( credentials ) == 0 or True in ( value != '' for value in list(credentials.values()) ) except HydrusExceptions.ValidationException as e: validity = ClientNetworkingLogin.VALIDITY_INVALID validity_error_text = str( e ) creds_are_good = False if creds_are_good: message = 'Activate this login script for this domain?' with ClientGUIDialogs.DialogYesNo( self, message, title = message ) as dlg: if dlg.ShowModal() == wx.ID_YES: active = True else: active = False else: active = False login_script_key_and_name = login_script.GetLoginScriptKeyAndName() credentials_tuple = tuple( credentials.items() ) no_work_until = 0 no_work_until_reason = '' domain_and_login_info = ( login_domain, login_script_key_and_name, credentials_tuple, login_access_type, login_access_text, active, validity, validity_error_text, no_work_until, no_work_until_reason ) self._domains_and_login_info.AddDatas( ( domain_and_login_info, ) ) self._domains_and_login_info.Sort() def _CanDoLogin( self ): domain_and_login_infos = self._domains_and_login_info.GetData( only_selected = True ) for domain_and_login_info in domain_and_login_infos: ( login_domain, login_script_key_and_name, credentials_tuple, login_access_type, login_access_text, active, validity, validity_error_text, no_work_until, no_work_until_reason ) = domain_and_login_info if not active: continue if validity == ClientNetworkingLogin.VALIDITY_INVALID: continue try: login_script = self._GetLoginScript( login_script_key_and_name ) network_context = ClientNetworkingContexts.NetworkContext( context_type = CC.NETWORK_CONTEXT_DOMAIN, context_data = login_domain ) logged_in = login_script.IsLoggedIn( self._engine, network_context ) if logged_in: continue except HydrusExceptions.DataMissing: continue return True return False def _CanEditCreds( self ): domain_and_login_infos = self._domains_and_login_info.GetData( only_selected = True ) for domain_and_login_info in domain_and_login_infos: ( login_domain, login_script_key_and_name, credentials_tuple, login_access_type, login_access_text, active, validity, validity_error_text, no_work_until, no_work_until_reason ) = domain_and_login_info try: login_script = self._GetLoginScript( login_script_key_and_name ) if len( login_script.GetCredentialDefinitions() ) > 0: return True except HydrusExceptions.DataMissing: continue return False def _CanScrubDelays( self ): domain_and_login_infos = self._domains_and_login_info.GetData( only_selected = True ) for domain_and_login_info in domain_and_login_infos: ( login_domain, login_script_key_and_name, credentials_tuple, login_access_type, login_access_text, active, validity, validity_error_text, no_work_until, no_work_until_reason ) = domain_and_login_info if not HydrusData.TimeHasPassed( no_work_until ) or no_work_until_reason != '': return True return False def _CanScrubInvalidity( self ): domain_and_login_infos = self._domains_and_login_info.GetData( only_selected = True ) for domain_and_login_info in domain_and_login_infos: ( login_domain, login_script_key_and_name, credentials_tuple, login_access_type, login_access_text, active, validity, validity_error_text, no_work_until, no_work_until_reason ) = domain_and_login_info if validity == ClientNetworkingLogin.VALIDITY_INVALID: return True return False def _ClearSessions( self ): domain_and_login_infos = self._domains_and_login_info.GetData( only_selected = True ) if len( domain_and_login_infos ) > 0: message = 'Are you sure you want to clear these domains\' sessions? This will delete all their existing cookies and cannot be undone.' with ClientGUIDialogs.DialogYesNo( self, message ) as dlg: if dlg.ShowModal() != wx.ID_YES: return for domain_and_login_info in domain_and_login_infos: ( login_domain, login_script_key_and_name, credentials_tuple, login_access_type, login_access_text, active, validity, validity_error_text, no_work_until, no_work_until_reason ) = domain_and_login_info network_context = ClientNetworkingContexts.NetworkContext( context_type = CC.NETWORK_CONTEXT_DOMAIN, context_data = login_domain ) self._engine.session_manager.ClearSession( network_context ) self._domains_and_login_info.UpdateDatas() self._domains_and_login_info_panel.UpdateButtons() def _ConvertDomainAndLoginInfoListCtrlTuples( self, domain_and_login_info ): ( login_domain, login_script_key_and_name, credentials_tuple, login_access_type, login_access_text, active, validity, validity_error_text, no_work_until, no_work_until_reason ) = domain_and_login_info login_expiry = None try: login_script = self._GetLoginScript( login_script_key_and_name ) sort_login_script = login_script.GetName() network_context = ClientNetworkingContexts.NetworkContext( context_type = CC.NETWORK_CONTEXT_DOMAIN, context_data = login_domain ) logged_in = login_script.IsLoggedIn( self._engine, network_context ) if logged_in: login_expiry = login_script.GetLoginExpiry( self._engine, network_context ) except HydrusExceptions.DataMissing: sort_login_script = 'login script not found' logged_in = False access = ClientNetworkingLogin.login_access_type_str_lookup[ login_access_type ] + ' - ' + login_access_text if active: sort_active = 'yes' else: sort_active = 'no' sort_validity = ClientNetworkingLogin.validity_str_lookup[ validity ] if len( validity_error_text ) > 0: sort_validity += ' - ' + validity_error_text sort_logged_in = ( logged_in, login_expiry ) if HydrusData.TimeHasPassed( no_work_until ): pretty_no_work_until = '' else: pretty_no_work_until = HydrusData.ConvertTimestampToPrettyExpires( no_work_until ) + ' - ' + no_work_until_reason pretty_login_domain = login_domain pretty_login_script = sort_login_script pretty_access = access pretty_active = sort_active if active: pretty_validity = sort_validity else: pretty_validity = '' if logged_in: if login_expiry is None: pretty_login_expiry = 'session' else: pretty_login_expiry = HydrusData.ConvertTimestampToPrettyExpires( login_expiry ) pretty_logged_in = 'yes - ' + pretty_login_expiry else: pretty_logged_in = 'no' display_tuple = ( pretty_login_domain, pretty_login_script, pretty_access, pretty_active, pretty_logged_in, pretty_validity, pretty_no_work_until ) sort_tuple = ( login_domain, sort_login_script, access, sort_active, sort_logged_in, sort_validity, no_work_until ) return ( display_tuple, sort_tuple ) def _DoLogin( self ): domains_to_login = [] domain_and_login_infos = self._domains_and_login_info.GetData( only_selected = True ) for domain_and_login_info in domain_and_login_infos: ( login_domain, login_script_key_and_name, credentials_tuple, login_access_type, login_access_text, active, validity, validity_error_text, no_work_until, no_work_until_reason ) = domain_and_login_info if not active: continue if validity == ClientNetworkingLogin.VALIDITY_INVALID: continue try: login_script = self._GetLoginScript( login_script_key_and_name ) network_context = ClientNetworkingContexts.NetworkContext( context_type = CC.NETWORK_CONTEXT_DOMAIN, context_data = login_domain ) logged_in = login_script.IsLoggedIn( self._engine, network_context ) if logged_in: continue except HydrusExceptions.DataMissing: continue domains_to_login.append( login_domain ) if len( domains_to_login ) == 0: wx.MessageBox( 'Unfortunately, none of the selected domains appear able to log in. Do you need to activate or scrub something somewhere?' ) else: domains_to_login.sort() message = 'It looks like the following domains can log in:' message += os.linesep * 2 message += os.linesep.join( domains_to_login ) message += os.linesep * 2 message += 'The dialog will ok and the login attempts will start. Is this ok?' with ClientGUIDialogs.DialogYesNo( self, message ) as dlg: if dlg.ShowModal() != wx.ID_YES: return self._domains_to_login_after_ok = domains_to_login self.GetParent().DoOK() def _EditCredentials( self ): domain_and_login_infos = self._domains_and_login_info.GetData( only_selected = True ) for domain_and_login_info in domain_and_login_infos: ( login_domain, login_script_key_and_name, credentials_tuple, login_access_type, login_access_text, active, validity, validity_error_text, no_work_until, no_work_until_reason ) = domain_and_login_info try: login_script = self._GetLoginScript( login_script_key_and_name ) except HydrusExceptions.DataMissing: wx.MessageBox( 'Could not find a login script for "' + login_domain + '"! Please re-add the login script in the other dialog or update the entry here to a new one!' ) return credential_definitions = login_script.GetCredentialDefinitions() if len( credential_definitions ) > 0: with ClientGUITopLevelWindows.DialogEdit( self, 'edit login' ) as dlg: credentials = dict( credentials_tuple ) panel = EditLoginCredentialsPanel( dlg, credential_definitions, credentials ) dlg.SetPanel( panel ) if dlg.ShowModal() == wx.ID_OK: credentials = panel.GetValue() else: return else: continue try: login_script.CheckCanLogin( credentials ) validity = ClientNetworkingLogin.VALIDITY_UNTESTED validity_error_text = '' # hacky: if there are creds, is at least one not empty string? creds_are_good = len( credentials ) == 0 or True in ( value != '' for value in list(credentials.values()) ) except HydrusExceptions.ValidationException as e: validity = ClientNetworkingLogin.VALIDITY_INVALID validity_error_text = str( e ) creds_are_good = False credentials_tuple = tuple( credentials.items() ) if creds_are_good: if not active: message = 'Activate this login script for this domain?' with ClientGUIDialogs.DialogYesNo( self, message, title = message ) as dlg: if dlg.ShowModal() == wx.ID_YES: active = True else: active = False no_work_until = 0 no_work_until_reason = '' edited_domain_and_login_info = ( login_domain, login_script_key_and_name, credentials_tuple, login_access_type, login_access_text, active, validity, validity_error_text, no_work_until, no_work_until_reason ) self._domains_and_login_info.DeleteDatas( ( domain_and_login_info, ) ) self._domains_and_login_info.AddDatas( ( edited_domain_and_login_info, ) ) self._domains_and_login_info.Sort() def _EditLoginScript( self ): domain_and_login_infos = self._domains_and_login_info.GetData( only_selected = True ) for domain_and_login_info in domain_and_login_infos: ( login_domain, login_script_key_and_name, credentials_tuple, login_access_type, login_access_text, active, validity, validity_error_text, no_work_until, no_work_until_reason ) = domain_and_login_info try: current_login_script = self._GetLoginScript( login_script_key_and_name ) except HydrusExceptions.DataMissing: current_login_script = None potential_login_scripts = list( self._login_scripts ) potential_login_scripts.sort( key = lambda ls: ls.GetName() ) matching_potential_login_scripts = [ login_script for login_script in potential_login_scripts if login_domain in login_script.GetExampleDomains() ] unmatching_potential_login_scripts = [ login_script for login_script in potential_login_scripts if login_domain not in login_script.GetExampleDomains() ] choice_tuples = [ ( login_script.GetName(), login_script ) for login_script in matching_potential_login_scripts ] if len( matching_potential_login_scripts ) > 0 and len( unmatching_potential_login_scripts ) > 0: choice_tuples.append( ( '------', None ) ) choice_tuples.extend( [ ( login_script.GetName(), login_script ) for login_script in unmatching_potential_login_scripts ] ) try: login_script = ClientGUIDialogsQuick.SelectFromList( self, 'select the login script to use', choice_tuples, value_to_select = current_login_script, sort_tuples = False ) except HydrusExceptions.CancelledException: break if login_script is None: break if login_script == current_login_script: break login_script_key_and_name = login_script.GetLoginScriptKeyAndName() try: ( login_access_type, login_access_text ) = login_script.GetExampleDomainInfo( login_domain ) except HydrusExceptions.DataMissing: a_types = [ ClientNetworkingLogin.LOGIN_ACCESS_TYPE_EVERYTHING, ClientNetworkingLogin.LOGIN_ACCESS_TYPE_NSFW, ClientNetworkingLogin.LOGIN_ACCESS_TYPE_SPECIAL, ClientNetworkingLogin.LOGIN_ACCESS_TYPE_USER_PREFS_ONLY ] choice_tuples = [ ( ClientNetworkingLogin.login_access_type_str_lookup[ a_type ], a_type ) for a_type in a_types ] try: login_access_type = ClientGUIDialogsQuick.SelectFromList( self, 'select what type of access the login gives to this domain', choice_tuples, sort_tuples = False ) except HydrusExceptions.CancelledException: break login_access_text = ClientNetworkingLogin.login_access_type_default_description_lookup[ login_access_type ] with ClientGUIDialogs.DialogTextEntry( self, 'edit the access description, if needed', default = login_access_text, allow_blank = False ) as dlg: if dlg.ShowModal() == wx.ID_OK: login_access_text = dlg.GetValue() else: break credentials = dict( credentials_tuple ) try: login_script.CheckCanLogin( credentials ) validity = ClientNetworkingLogin.VALIDITY_UNTESTED validity_error_text = '' creds_are_good = True except HydrusExceptions.ValidationException as e: validity = ClientNetworkingLogin.VALIDITY_INVALID validity_error_text = str( e ) creds_are_good = False if not creds_are_good: active = False no_work_until = 0 no_work_until_reason = '' edited_domain_and_login_info = ( login_domain, login_script_key_and_name, credentials_tuple, login_access_type, login_access_text, active, validity, validity_error_text, no_work_until, no_work_until_reason ) self._domains_and_login_info.DeleteDatas( ( domain_and_login_info, ) ) self._domains_and_login_info.AddDatas( ( edited_domain_and_login_info, ) ) self._domains_and_login_info.Sort() def _FlipActive( self ): domain_and_login_infos = self._domains_and_login_info.GetData( only_selected = True ) for domain_and_login_info in domain_and_login_infos: ( login_domain, login_script_key_and_name, credentials_tuple, login_access_type, login_access_text, active, validity, validity_error_text, no_work_until, no_work_until_reason ) = domain_and_login_info active = not active flipped_domain_and_login_info = ( login_domain, login_script_key_and_name, credentials_tuple, login_access_type, login_access_text, active, validity, validity_error_text, no_work_until, no_work_until_reason ) self._domains_and_login_info.DeleteDatas( ( domain_and_login_info, ) ) self._domains_and_login_info.AddDatas( ( flipped_domain_and_login_info, ) ) self._domains_and_login_info.Sort() def _GetLoginScript( self, login_script_key_and_name ): ( login_script_key, login_script_name ) = login_script_key_and_name for login_script in self._login_scripts: if login_script.GetLoginScriptKey() == login_script_key: return login_script for login_script in self._login_scripts: if login_script.GetName() == login_script_name: return login_script raise HydrusExceptions.DataMissing( 'No login script found!' ) def _ScrubDelays( self ): domain_and_login_infos = self._domains_and_login_info.GetData( only_selected = True ) for domain_and_login_info in domain_and_login_infos: ( login_domain, login_script_key_and_name, credentials_tuple, login_access_type, login_access_text, active, validity, validity_error_text, no_work_until, no_work_until_reason ) = domain_and_login_info no_work_until = 0 no_work_until_reason = '' scrubbed_domain_and_login_info = ( login_domain, login_script_key_and_name, credentials_tuple, login_access_type, login_access_text, active, validity, validity_error_text, no_work_until, no_work_until_reason ) self._domains_and_login_info.DeleteDatas( ( domain_and_login_info, ) ) self._domains_and_login_info.AddDatas( ( scrubbed_domain_and_login_info, ) ) self._domains_and_login_info.Sort() def _ScrubInvalidity( self ): domain_and_login_infos = self._domains_and_login_info.GetData( only_selected = True ) for domain_and_login_info in domain_and_login_infos: ( login_domain, login_script_key_and_name, credentials_tuple, login_access_type, login_access_text, active, validity, validity_error_text, no_work_until, no_work_until_reason ) = domain_and_login_info if validity != ClientNetworkingLogin.VALIDITY_INVALID: continue try: try: login_script = self._GetLoginScript( login_script_key_and_name ) except HydrusExceptions.DataMissing: continue credentials = dict( credentials_tuple ) login_script.CheckCanLogin( credentials ) validity = ClientNetworkingLogin.VALIDITY_UNTESTED validity_error_text = '' except HydrusExceptions.ValidationException as e: validity = ClientNetworkingLogin.VALIDITY_INVALID validity_error_text = str( e ) scrubbed_domain_and_login_info = ( login_domain, login_script_key_and_name, credentials_tuple, login_access_type, login_access_text, active, validity, validity_error_text, no_work_until, no_work_until_reason ) self._domains_and_login_info.DeleteDatas( ( domain_and_login_info, ) ) self._domains_and_login_info.AddDatas( ( scrubbed_domain_and_login_info, ) ) self._domains_and_login_info.Sort() def GetDomainsToLoginAfterOK( self ): return self._domains_to_login_after_ok def GetValue( self ): domains_to_login_info = dict() for ( login_domain, login_script_key_and_name, credentials_tuple, login_access_type, login_access_text, active, validity, validity_error_text, no_work_until, no_work_until_reason ) in self._domains_and_login_info.GetData(): credentials = dict( credentials_tuple ) domains_to_login_info[ login_domain ] = ( login_script_key_and_name, credentials, login_access_type, login_access_text, active, validity, validity_error_text, no_work_until, no_work_until_reason ) return domains_to_login_info def GenerateTestNetworkJobPresentationContextFactory( window, network_job_control ): def network_job_presentation_context_factory( network_job ): def wx_set_it( nj ): if not window: return network_job_control.SetNetworkJob( nj ) def enter_call(): wx.CallAfter( wx_set_it, network_job ) def exit_call(): wx.CallAfter( wx_set_it, None ) return ClientImporting.NetworkJobPresentationContext( enter_call, exit_call ) return network_job_presentation_context_factory class ReviewTestResultPanel( ClientGUIScrolledPanels.ReviewPanel ): def __init__( self, parent, test_result ): ClientGUIScrolledPanels.ReviewPanel.__init__( self, parent ) ( name, url, body, self._downloaded_data, new_temp_strings, new_cookie_strings, result ) = test_result self._name = ClientGUICommon.BetterStaticText( self, label = name ) self._url = wx.TextCtrl( self ) self._body = wx.TextCtrl( self, style = wx.TE_MULTILINE ) self._body.SetEditable( False ) min_size = ClientGUIFunctions.ConvertTextToPixels( self._body, ( 64, 3 ) ) self._body.SetMinClientSize( min_size ) self._data_preview = wx.TextCtrl( self, style = wx.TE_MULTILINE ) self._data_preview.SetEditable( False ) min_size = ClientGUIFunctions.ConvertTextToPixels( self._data_preview, ( 64, 8 ) ) self._data_preview.SetMinClientSize( min_size ) self._data_copy_button = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.copy, self._CopyData ) self._data_copy_button.SetToolTip( 'Copy the current example data to the clipboard.' ) self._temp_variables = wx.TextCtrl( self, style = wx.TE_MULTILINE ) self._temp_variables.SetEditable( False ) min_size = ClientGUIFunctions.ConvertTextToPixels( self._temp_variables, ( 64, 6 ) ) self._temp_variables.SetMinClientSize( min_size ) self._cookies = wx.TextCtrl( self, style = wx.TE_MULTILINE ) self._cookies.SetEditable( False ) min_size = ClientGUIFunctions.ConvertTextToPixels( self._cookies, ( 64, 6 ) ) self._cookies.SetMinClientSize( min_size ) self._result = ClientGUICommon.BetterStaticText( self, label = result ) # self._url.SetValue( url ) if body is not None: try: self._body.SetValue( body ) except: self._body.SetValue( str( body ) ) self._data_preview.SetValue( str( self._downloaded_data[:1024] ) ) self._temp_variables.SetValue( os.linesep.join( new_temp_strings ) ) self._cookies.SetValue( os.linesep.join( new_cookie_strings ) ) # rows = [] rows.append( ( 'name: ', self._name ) ) rows.append( ( 'url: ', self._url ) ) rows.append( ( 'body (if set): ', self._body ) ) rows.append( ( 'data: ', self._data_preview ) ) rows.append( ( 'copy data: ', self._data_copy_button ) ) rows.append( ( 'new temp vars: ', self._temp_variables ) ) rows.append( ( 'new cookies: ', self._cookies ) ) rows.append( ( 'result: ', self._result ) ) gridbox = ClientGUICommon.WrapInGrid( self, rows ) self.SetSizer( gridbox ) def _CopyData( self ): HG.client_controller.pub( 'clipboard', 'text', self._downloaded_data ) class EditLoginScriptPanel( ClientGUIScrolledPanels.EditPanel ): def __init__( self, parent, login_script ): ClientGUIScrolledPanels.EditPanel.__init__( self, parent ) self._original_login_script = login_script self._currently_testing = False self._test_domain = '' self._test_credentials = {} # menu_items = [] page_func = HydrusData.Call( ClientPaths.LaunchPathInWebBrowser, os.path.join( HC.HELP_DIR, 'downloader_login.html' ) ) menu_items.append( ( 'normal', 'open the login scripts help', 'Open the help page for login scripts in your web browser.', page_func ) ) help_button = ClientGUICommon.MenuBitmapButton( self, CC.GlobalBMPs.help, menu_items ) help_hbox = ClientGUICommon.WrapInText( help_button, self, 'help for this panel -->', wx.Colour( 0, 0, 255 ) ) # self._name = wx.TextCtrl( self ) # credential_definitions_box_panel = ClientGUICommon.StaticBox( self, 'credential definitions' ) credential_definitions_panel = ClientGUIListCtrl.BetterListCtrlPanel( credential_definitions_box_panel ) columns = [ ( 'name', -1 ), ( 'type', 10 ), ( 'value', 16 ) ] self._credential_definitions = ClientGUIListCtrl.BetterListCtrl( credential_definitions_panel, 'credential_definitions', 4, 16, columns, self._ConvertCredentialDefinitionToListCtrlTuples, use_simple_delete = True, activation_callback = self._EditCredentialDefinitions ) credential_definitions_panel.SetListCtrl( self._credential_definitions ) credential_definitions_panel.AddButton( 'add', self._AddCredentialDefinition ) credential_definitions_panel.AddButton( 'edit', self._EditCredentialDefinitions, enabled_only_on_selection = True ) credential_definitions_panel.AddDeleteButton() # login_steps_box_panel = ClientGUICommon.StaticBox( self, 'login steps' ) columns = [ ( 'name', -1 ), ( 'url', 56 ) ] self._login_steps = ClientGUIListBoxes.QueueListBox( login_steps_box_panel, 5, self._ConvertLoginStepToListBoxString, add_callable = self._AddLoginStep, edit_callable = self._EditLoginStep ) # required_cookies_info_box_panel = ClientGUICommon.StaticBox( self, 'cookies required to consider session logged in' ) self._required_cookies_info = ClientGUIControls.StringMatchToStringMatchDictControl( required_cookies_info_box_panel, login_script.GetRequiredCookiesInfo(), min_height = 4, key_name = 'cookie name' ) # example_domains_info_box_panel = ClientGUICommon.StaticBox( self, 'example domains' ) example_domains_info_panel = ClientGUIListCtrl.BetterListCtrlPanel( example_domains_info_box_panel ) columns = [ ( 'domain', -1 ), ( 'access type', 14 ), ( 'description', 40 ) ] self._example_domains_info = ClientGUIListCtrl.BetterListCtrl( example_domains_info_panel, 'example_domains_info', 6, 16, columns, self._ConvertExampleDomainInfoToListCtrlTuples, use_simple_delete = True, activation_callback = self._EditExampleDomainsInfo ) example_domains_info_panel.SetListCtrl( self._example_domains_info ) example_domains_info_panel.AddButton( 'add', self._AddExampleDomainsInfo ) example_domains_info_panel.AddButton( 'edit', self._EditExampleDomainsInfo, enabled_only_on_selection = True ) example_domains_info_panel.AddDeleteButton() # test_panel = ClientGUICommon.StaticBox( self, 'testing' ) self._test_button = ClientGUICommon.BetterButton( test_panel, 'run test', self._DoTest ) self._test_network_job_control = ClientGUIControls.NetworkJobControl( test_panel ) test_listctrl_panel = ClientGUIListCtrl.BetterListCtrlPanel( test_panel ) columns = [ ( 'step', -1 ), ( 'url', 36 ), ( 'result', 14 ) ] self._test_listctrl = ClientGUIListCtrl.BetterListCtrl( test_listctrl_panel, 'test_login_script_results', 6, 20, columns, self._ConvertTestResultToListCtrlTuples, activation_callback = self._ReviewTestResult ) test_listctrl_panel.SetListCtrl( self._test_listctrl ) test_listctrl_panel.AddButton( 'review', self._ReviewTestResult, enabled_only_on_selection = True ) self._final_test_result = ClientGUICommon.BetterStaticText( test_panel ) # self._name.SetValue( login_script.GetName() ) self._credential_definitions.SetData( login_script.GetCredentialDefinitions() ) self._login_steps.AddDatas( login_script.GetLoginSteps() ) self._example_domains_info.SetData( login_script.GetExampleDomainsInfo() ) # credential_definitions_box_panel.Add( credential_definitions_panel, CC.FLAGS_EXPAND_BOTH_WAYS ) login_steps_box_panel.Add( self._login_steps, CC.FLAGS_EXPAND_BOTH_WAYS ) required_cookies_info_box_panel.Add( self._required_cookies_info, CC.FLAGS_EXPAND_BOTH_WAYS ) example_domains_info_box_panel.Add( example_domains_info_panel, CC.FLAGS_EXPAND_BOTH_WAYS ) test_panel.Add( self._test_button, CC.FLAGS_EXPAND_PERPENDICULAR ) test_panel.Add( self._test_network_job_control, CC.FLAGS_EXPAND_PERPENDICULAR ) test_panel.Add( test_listctrl_panel, CC.FLAGS_EXPAND_BOTH_WAYS ) test_panel.Add( self._final_test_result, CC.FLAGS_EXPAND_PERPENDICULAR ) # rows = [] rows.append( ( 'name: ', self._name ) ) gridbox = ClientGUICommon.WrapInGrid( self, rows ) vbox = ClientGUICommon.BetterBoxSizer( wx.VERTICAL ) vbox.Add( help_hbox, CC.FLAGS_BUTTON_SIZER ) vbox.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR ) vbox.Add( credential_definitions_box_panel, CC.FLAGS_EXPAND_BOTH_WAYS ) vbox.Add( login_steps_box_panel, CC.FLAGS_EXPAND_BOTH_WAYS ) vbox.Add( required_cookies_info_box_panel, CC.FLAGS_EXPAND_BOTH_WAYS ) vbox.Add( example_domains_info_box_panel, CC.FLAGS_EXPAND_BOTH_WAYS ) hbox = ClientGUICommon.BetterBoxSizer( wx.HORIZONTAL ) hbox.Add( vbox, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS ) hbox.Add( test_panel, CC.FLAGS_EXPAND_BOTH_WAYS ) self.SetSizer( hbox ) def _AddCredentialDefinition( self ): new_credential_definition = ClientNetworkingLogin.LoginCredentialDefinition() with ClientGUITopLevelWindows.DialogEdit( self, 'edit parser', frame_key = 'deeply_nested_dialog' ) as dlg_edit: panel = EditLoginCredentialDefinitionPanel( dlg_edit, new_credential_definition ) dlg_edit.SetPanel( panel ) if dlg_edit.ShowModal() == wx.ID_OK: new_credential_definition = panel.GetValue() HydrusSerialisable.SetNonDupeName( new_credential_definition, self._GetExistingCredentialDefinitionNames() ) self._credential_definitions.AddDatas( ( new_credential_definition, ) ) self._credential_definitions.Sort() def _AddExampleDomainsInfo( self ): ( domain, access_type, access_text ) = ( 'example.com', ClientNetworkingLogin.LOGIN_ACCESS_TYPE_NSFW, ClientNetworkingLogin.login_access_type_default_description_lookup[ ClientNetworkingLogin.LOGIN_ACCESS_TYPE_NSFW ] ) with ClientGUIDialogs.DialogTextEntry( self, 'edit the domain', default = domain, allow_blank = False ) as dlg: if dlg.ShowModal() == wx.ID_OK: domain = dlg.GetValue() else: return existing_domains = self._GetExistingDomains() if domain in existing_domains: wx.MessageBox( 'That domain already exists!' ) return a_types = [ ClientNetworkingLogin.LOGIN_ACCESS_TYPE_EVERYTHING, ClientNetworkingLogin.LOGIN_ACCESS_TYPE_NSFW, ClientNetworkingLogin.LOGIN_ACCESS_TYPE_SPECIAL, ClientNetworkingLogin.LOGIN_ACCESS_TYPE_USER_PREFS_ONLY ] choice_tuples = [ ( ClientNetworkingLogin.login_access_type_str_lookup[ a_type ], a_type ) for a_type in a_types ] try: new_access_type = ClientGUIDialogsQuick.SelectFromList( self, 'select what type of access the login gives to this domain', choice_tuples, value_to_select = access_type, sort_tuples = False ) except HydrusExceptions.CancelledException: return if new_access_type != access_type: access_type = new_access_type access_text = ClientNetworkingLogin.login_access_type_default_description_lookup[ access_type ] with ClientGUIDialogs.DialogTextEntry( self, 'edit the access description, if needed', default = access_text, allow_blank = False ) as dlg: if dlg.ShowModal() == wx.ID_OK: access_text = dlg.GetValue() else: return example_domain_info = ( domain, access_type, access_text ) self._example_domains_info.AddDatas( ( example_domain_info, ) ) self._example_domains_info.Sort() def _AddLoginStep( self ): login_step = ClientNetworkingLogin.LoginStep() return self._EditLoginStep( login_step ) def _ConvertCredentialDefinitionToListCtrlTuples( self, credential_definition ): name = credential_definition.GetName() credential_type = credential_definition.GetType() type_string = ClientNetworkingLogin.credential_type_str_lookup[ credential_type ] string_match = credential_definition.GetStringMatch() value = string_match.ToString() pretty_name = name pretty_type_string = type_string pretty_value = value display_tuple = ( pretty_name, pretty_type_string, pretty_value ) sort_tuple = ( name, type_string, value ) return ( display_tuple, sort_tuple ) def _ConvertExampleDomainInfoToListCtrlTuples( self, example_domain_info ): ( domain, access_type, access_text ) = example_domain_info pretty_domain = domain pretty_access_type = ClientNetworkingLogin.login_access_type_str_lookup[ access_type ] pretty_access_text = access_text display_tuple = ( pretty_domain, pretty_access_type, pretty_access_text ) sort_tuple = ( domain, pretty_access_type, access_text ) return ( display_tuple, sort_tuple ) def _ConvertLoginStepToListBoxString( self, login_step ): name = login_step.GetName() return name def _ConvertTestResultToListCtrlTuples( self, test_result ): ( name, url, body, downloaded_data, new_temp_strings, new_cookie_strings, result ) = test_result pretty_name = name pretty_url = url pretty_result = result display_tuple = ( pretty_name, pretty_url, pretty_result ) sort_tuple = ( name, url, result ) return ( display_tuple, sort_tuple ) def _EditCredentialDefinitions( self ): credential_definitions = self._credential_definitions.GetData( only_selected = True ) for credential_definition in credential_definitions: with ClientGUITopLevelWindows.DialogEdit( self, 'edit login script', frame_key = 'deeply_nested_dialog' ) as dlg: panel = EditLoginCredentialDefinitionPanel( dlg, credential_definition ) dlg.SetPanel( panel ) if dlg.ShowModal() == wx.ID_OK: edited_credential_definition = panel.GetValue() self._credential_definitions.DeleteDatas( ( credential_definition, ) ) HydrusSerialisable.SetNonDupeName( edited_credential_definition, self._GetExistingCredentialDefinitionNames() ) self._credential_definitions.AddDatas( ( edited_credential_definition, ) ) else: break self._credential_definitions.Sort() def _DoTest( self ): def wx_add_result( test_result ): if not self: return self._test_listctrl.AddDatas( ( test_result, ) ) def receive_result( test_result ): wx.CallAfter( wx_add_result, test_result ) def clean_up( final_result ): if not self: return wx.MessageBox( final_result ) self._final_test_result.SetLabelText( final_result ) self._test_button.Enable() self._currently_testing = False def do_it( login_script, domain, credentials, network_job_presentation_context_factory ): try: login_result = 'login did not finish' # a potential here is to properly inform the login manager of the domain map and hence read back the invalidation text # but I am catching the info in the raised exception, so nbd really, I think bandwidth_manager = ClientNetworkingBandwidth.NetworkBandwidthManager() session_manager = ClientNetworkingSessions.NetworkSessionManager() domain_manager = HG.client_controller.network_engine.domain_manager.Duplicate() # keep custom headers from current domain stuff login_manager = ClientNetworkingLogin.NetworkLoginManager() network_engine = ClientNetworking.NetworkEngine( HG.client_controller, bandwidth_manager, session_manager, domain_manager, login_manager ) HG.client_controller.CallToThreadLongRunning( network_engine.MainLoop ) network_context = ClientNetworkingContexts.NetworkContext.STATICGenerateForDomain( domain ) login_result = login_script.Start( network_engine, network_context, credentials, network_job_presentation_context_factory = network_job_presentation_context_factory, test_result_callable = wx_add_result ) except Exception as e: login_result = str( e ) HydrusData.ShowException( e ) finally: network_engine.Shutdown() wx.CallAfter( clean_up, login_result ) if self._currently_testing: wx.MessageBox( 'Currently testing already! Please cancel current job!' ) return try: login_script = self.GetValue() except HydrusExceptions.VetoException: return if self._test_domain == '': example_domains = list( login_script.GetExampleDomains() ) example_domains.sort() if len( example_domains ) > 0: self._test_domain = example_domains[0] with ClientGUIDialogs.DialogTextEntry( self, 'edit the domain', default = self._test_domain, allow_blank = False ) as dlg: if dlg.ShowModal() == wx.ID_OK: self._test_domain = dlg.GetValue() else: return credential_definitions = login_script.GetCredentialDefinitions() if len( credential_definitions ) > 0: with ClientGUITopLevelWindows.DialogEdit( self, 'edit login' ) as dlg: panel = EditLoginCredentialsPanel( dlg, credential_definitions, self._test_credentials ) dlg.SetPanel( panel ) if dlg.ShowModal() == wx.ID_OK: self._test_credentials = panel.GetValue() else: return else: self._test_credentials = {} self._test_listctrl.DeleteDatas( self._test_listctrl.GetData() ) self._test_button.Disable() network_job_presentation_context_factory = GenerateTestNetworkJobPresentationContextFactory( self, self._test_network_job_control ) self._currently_testing = True HG.client_controller.CallToThread( do_it, login_script, self._test_domain, self._test_credentials, network_job_presentation_context_factory ) def _EditExampleDomainsInfo( self ): selected_example_domains_info = self._example_domains_info.GetData( only_selected = True ) for example_domain_info in selected_example_domains_info: ( original_domain, access_type, access_text ) = example_domain_info with ClientGUIDialogs.DialogTextEntry( self, 'edit the domain', default = original_domain, allow_blank = False ) as dlg: if dlg.ShowModal() == wx.ID_OK: domain = dlg.GetValue() else: break existing_domains = self._GetExistingDomains() if domain != original_domain and domain in existing_domains: wx.MessageBox( 'That domain already exists!' ) break a_types = [ ClientNetworkingLogin.LOGIN_ACCESS_TYPE_EVERYTHING, ClientNetworkingLogin.LOGIN_ACCESS_TYPE_NSFW, ClientNetworkingLogin.LOGIN_ACCESS_TYPE_SPECIAL, ClientNetworkingLogin.LOGIN_ACCESS_TYPE_USER_PREFS_ONLY ] choice_tuples = [ ( ClientNetworkingLogin.login_access_type_str_lookup[ a_type ], a_type ) for a_type in a_types ] try: new_access_type = ClientGUIDialogsQuick.SelectFromList( self, 'select what type of access the login gives to this domain', choice_tuples, value_to_select = access_type, sort_tuples = False ) except HydrusExceptions.CancelledException: break if new_access_type != access_type: access_type = new_access_type access_text = ClientNetworkingLogin.login_access_type_default_description_lookup[ access_type ] with ClientGUIDialogs.DialogTextEntry( self, 'edit the access description, if needed', default = access_text, allow_blank = False ) as dlg: if dlg.ShowModal() == wx.ID_OK: access_text = dlg.GetValue() else: break self._example_domains_info.DeleteDatas( ( example_domain_info, ) ) edited_example_domain_info = ( domain, access_type, access_text ) self._example_domains_info.AddDatas( ( edited_example_domain_info, ) ) self._example_domains_info.Sort() def _EditLoginStep( self, login_step ): with ClientGUITopLevelWindows.DialogEdit( self, 'edit login step' ) as dlg: panel = EditLoginStepPanel( dlg, login_step ) dlg.SetPanel( panel ) if dlg.ShowModal() == wx.ID_OK: login_step = panel.GetValue() return login_step else: raise HydrusExceptions.VetoException() def _GetExistingCredentialDefinitionNames( self ): return { credential_definition.GetName() for credential_definition in self._credential_definitions.GetData() } def _GetExistingDomains( self ): return { domain for ( domain, access_type, access_text ) in self._example_domains_info.GetData() } def _ReviewTestResult( self ): for test_result in self._test_listctrl.GetData( only_selected = True ): frame = ClientGUITopLevelWindows.FrameThatTakesScrollablePanel( self, 'login test result' ) panel = ReviewTestResultPanel( frame, test_result ) frame.SetPanel( panel ) def GetValue( self ): if self._currently_testing: raise HydrusExceptions.VetoException( 'Currently testing! Please cancel it first!' ) name = self._name.GetValue() login_script_key = self._original_login_script.GetLoginScriptKey() required_cookies_info = self._required_cookies_info.GetValue() credential_definitions = self._credential_definitions.GetData() login_steps = self._login_steps.GetData() example_domains_info = self._example_domains_info.GetData() credential_names = { credential_definition.GetName() for credential_definition in credential_definitions } login_script = ClientNetworkingLogin.LoginScriptDomain( name = name, login_script_key = login_script_key, required_cookies_info = required_cookies_info, credential_definitions = credential_definitions, login_steps = login_steps, example_domains_info = example_domains_info ) login_script.SetLoginScriptKey( login_script_key ) try: login_script.CheckIsValid() except HydrusExceptions.ValidationException as e: message = 'There is a problem with this script. The reason is:' message += os.linesep * 2 message += str( e ) message += os.linesep * 2 message += 'Do you want to proceed with this invalid script, or go back and fix it?' with ClientGUIDialogs.DialogYesNo( self, message, yes_label = 'ok as invalid', no_label = 'go back' ) as dlg: if dlg.ShowModal() != wx.ID_YES: raise HydrusExceptions.VetoException( 'The ok event has been cancelled!' ) return login_script class EditLoginScriptsPanel( ClientGUIScrolledPanels.EditPanel ): def __init__( self, parent, login_scripts ): ClientGUIScrolledPanels.EditPanel.__init__( self, parent ) login_scripts_panel = ClientGUIListCtrl.BetterListCtrlPanel( self ) columns = [ ( 'name', -1 ), ( 'example domains', 40 ) ] self._login_scripts = ClientGUIListCtrl.BetterListCtrl( login_scripts_panel, 'login_scripts', 20, 24, columns, self._ConvertLoginScriptToListCtrlTuples, use_simple_delete = True, activation_callback = self._Edit ) login_scripts_panel.SetListCtrl( self._login_scripts ) login_scripts_panel.AddButton( 'add', self._Add ) login_scripts_panel.AddButton( 'edit', self._Edit, enabled_only_on_selection = True ) login_scripts_panel.AddDeleteButton() login_scripts_panel.AddSeparator() login_scripts_panel.AddImportExportButtons( ( ClientNetworkingLogin.LoginScriptDomain, ), self._AddLoginScript ) login_scripts_panel.AddSeparator() login_scripts_panel.AddDefaultsButton( ClientDefaults.GetDefaultLoginScripts, self._AddLoginScript ) # self._login_scripts.AddDatas( login_scripts ) # vbox = wx.BoxSizer( wx.VERTICAL ) vbox.Add( login_scripts_panel, CC.FLAGS_EXPAND_BOTH_WAYS ) self.SetSizer( vbox ) def _Add( self ): new_login_script = ClientNetworkingLogin.LoginScriptDomain() with ClientGUITopLevelWindows.DialogEdit( self, 'edit login script', frame_key = 'deeply_nested_dialog' ) as dlg_edit: panel = EditLoginScriptPanel( dlg_edit, new_login_script ) dlg_edit.SetPanel( panel ) if dlg_edit.ShowModal() == wx.ID_OK: new_login_script = panel.GetValue() self._AddLoginScript( new_login_script ) self._login_scripts.Sort() def _AddLoginScript( self, login_script ): HydrusSerialisable.SetNonDupeName( login_script, self._GetExistingNames() ) login_script.RegenerateLoginScriptKey() self._login_scripts.AddDatas( ( login_script, ) ) def _ConvertLoginScriptToListCtrlTuples( self, login_script ): name = login_script.GetName() example_domains = list( login_script.GetExampleDomains() ) example_domains.sort() pretty_name = name pretty_example_domains = ', '.join( example_domains ) display_tuple = ( pretty_name, pretty_example_domains ) sort_tuple = ( name, example_domains ) return ( display_tuple, sort_tuple ) def _Edit( self ): login_scripts = self._login_scripts.GetData( only_selected = True ) for login_script in login_scripts: with ClientGUITopLevelWindows.DialogEdit( self, 'edit login script', frame_key = 'deeply_nested_dialog' ) as dlg: panel = EditLoginScriptPanel( dlg, login_script ) dlg.SetPanel( panel ) if dlg.ShowModal() == wx.ID_OK: edited_login_script = panel.GetValue() self._login_scripts.DeleteDatas( ( login_script, ) ) HydrusSerialisable.SetNonDupeName( edited_login_script, self._GetExistingNames() ) self._login_scripts.AddDatas( ( edited_login_script, ) ) else: break self._login_scripts.Sort() def _GetExistingNames( self ): names = { login_script.GetName() for login_script in self._login_scripts.GetData() } return names def GetValue( self ): return self._login_scripts.GetData() class EditLoginStepPanel( ClientGUIScrolledPanels.EditPanel ): def __init__( self, parent, login_step ): ClientGUIScrolledPanels.EditPanel.__init__( self, parent ) # name = login_step.GetName() ( scheme, method, subdomain, path, required_credentials, static_args, temp_args, required_cookies_info, content_parsers ) = login_step.ToTuple() # self._name = wx.TextCtrl( self ) self._method = ClientGUICommon.BetterChoice( self ) self._method.Append( 'GET', 'GET' ) self._method.Append( 'POST', 'POST' ) self._scheme = ClientGUICommon.BetterChoice( self ) self._scheme.Append( 'http', 'http' ) self._scheme.Append( 'https', 'https' ) self._subdomain = ClientGUICommon.NoneableTextCtrl( self, none_phrase = 'none' ) self._path = wx.TextCtrl( self ) required_credentials_panel = ClientGUICommon.StaticBox( self, 'credentials to send' ) self._required_credentials = ClientGUIControls.StringToStringDictControl( required_credentials_panel, required_credentials, min_height = 4, key_name = 'credential name', value_name = 'parameter name' ) # static_args_panel = ClientGUICommon.StaticBox( self, 'static variables to send' ) self._static_args = ClientGUIControls.StringToStringDictControl( static_args_panel, static_args, min_height = 4, key_name = 'parameter name', value_name = 'value' ) # temp_args_panel = ClientGUICommon.StaticBox( self, 'temporary variables to send' ) self._temp_args = ClientGUIControls.StringToStringDictControl( temp_args_panel, temp_args, min_height = 4, key_name = 'temp variable name', value_name = 'parameter name' ) # required_cookies_info_box_panel = ClientGUICommon.StaticBox( self, 'cookies required to consider step successful' ) self._required_cookies_info = ClientGUIControls.StringMatchToStringMatchDictControl( required_cookies_info_box_panel, required_cookies_info, min_height = 4, key_name = 'cookie name' ) # content_parsers_panel = ClientGUICommon.StaticBox( self, 'content parsers' ) test_context_callable = lambda: ( {}, '' ) permitted_content_types = [ HC.CONTENT_TYPE_VARIABLE, HC.CONTENT_TYPE_VETO ] self._content_parsers = ClientGUIParsing.EditContentParsersPanel( content_parsers_panel, test_context_callable, permitted_content_types ) # a test panel a la pageparsers # self._name.SetValue( name ) self._scheme.SetValue( scheme ) self._method.SetValue( method ) self._subdomain.SetValue( subdomain ) self._path.SetValue( path ) self._content_parsers.AddDatas( content_parsers ) # required_credentials_panel.Add( self._required_credentials, CC.FLAGS_EXPAND_BOTH_WAYS ) static_args_panel.Add( self._static_args, CC.FLAGS_EXPAND_BOTH_WAYS ) temp_args_panel.Add( self._temp_args, CC.FLAGS_EXPAND_BOTH_WAYS ) required_cookies_info_box_panel.Add( self._required_cookies_info, CC.FLAGS_EXPAND_BOTH_WAYS ) content_parsers_panel.Add( self._content_parsers, CC.FLAGS_EXPAND_BOTH_WAYS ) # rows = [] rows.append( ( 'name: ', self._name ) ) rows.append( ( 'scheme: ', self._scheme ) ) rows.append( ( 'method: ', self._method ) ) rows.append( ( 'subdomain (replaces www, if present): ', self._subdomain ) ) rows.append( ( 'path: ', self._path ) ) gridbox = ClientGUICommon.WrapInGrid( self, rows ) vbox = ClientGUICommon.BetterBoxSizer( wx.VERTICAL ) vbox.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR ) vbox.Add( required_credentials_panel, CC.FLAGS_EXPAND_BOTH_WAYS ) vbox.Add( static_args_panel, CC.FLAGS_EXPAND_BOTH_WAYS ) vbox.Add( temp_args_panel, CC.FLAGS_EXPAND_BOTH_WAYS ) vbox.Add( required_cookies_info_box_panel, CC.FLAGS_EXPAND_BOTH_WAYS ) vbox.Add( content_parsers_panel, CC.FLAGS_EXPAND_BOTH_WAYS ) self.SetSizer( vbox ) def GetValue( self ): name = self._name.GetValue() scheme = self._scheme.GetValue() method = self._method.GetValue() subdomain = self._subdomain.GetValue() path = self._path.GetValue() required_credentials = self._required_credentials.GetValue() static_args = self._static_args.GetValue() temp_args = self._temp_args.GetValue() required_cookies_info = self._required_cookies_info.GetValue() content_parsers = self._content_parsers.GetData() if subdomain == '': subdomain = None login_step = ClientNetworkingLogin.LoginStep( name = name, scheme = scheme, method = method, subdomain = subdomain, path = path ) login_step.SetComplicatedVariables( required_credentials, static_args, temp_args, required_cookies_info, content_parsers ) return login_step