import http.cookies
import threading
import time

from hydrus.core import HydrusConstants as HC
from hydrus.core import HydrusData
from hydrus.core import HydrusExceptions
from hydrus.core import HydrusGlobals as HG
from hydrus.core import HydrusPaths
from hydrus.core import HydrusSerialisable
from hydrus.core.networking import HydrusNetwork
from hydrus.core.networking import HydrusNetworkVariableHandling
from hydrus.core.networking import HydrusNetworking
from hydrus.core.networking import HydrusServerRequest
from hydrus.core.networking import HydrusServerResources

from hydrus.server import ServerFiles

class HydrusResourceBusyCheck( HydrusServerResources.Resource ):
    
    def render_GET( self, request: HydrusServerRequest.HydrusRequest ):
        
        request.setResponseCode( 200 )
        
        if HG.server_busy.locked():
            
            return b'1'
            
        else:
            
            return b'0'
            
        
    
class HydrusResourceHydrusNetwork( HydrusServerResources.HydrusResource ):
    
    BLOCKED_WHEN_BUSY = True
    
    def _callbackParseGETArgs( self, request: HydrusServerRequest.HydrusRequest ):
        
        parsed_request_args = HydrusNetworkVariableHandling.ParseHydrusNetworkGETArgs( request.args )
        
        request.parsed_request_args = parsed_request_args
        
        return request
        
    
    def _callbackParsePOSTArgs( self, request: HydrusServerRequest.HydrusRequest ):
        
        request.content.seek( 0 )
        
        if not request.requestHeaders.hasHeader( 'Content-Type' ):
            
            parsed_request_args = HydrusNetworkVariableHandling.ParsedRequestArguments()
            
        else:
            
            content_types = request.requestHeaders.getRawHeaders( 'Content-Type' )
            
            content_type = content_types[0]
            
            try:
                
                mime = HC.mime_enum_lookup[ content_type ]
                
            except:
                
                raise HydrusExceptions.BadRequestException( 'Did not recognise Content-Type header!' )
                
            
            total_bytes_read = 0
            
            if mime == HC.APPLICATION_JSON:
                
                json_string = request.content.read()
                
                total_bytes_read += len( json_string )
                
                parsed_request_args = HydrusNetworkVariableHandling.ParseNetworkBytesToParsedHydrusArgs( json_string )
                
            else:
                
                ( os_file_handle, temp_path ) = HydrusPaths.GetTempPath()
                
                request.temp_file_info = ( os_file_handle, temp_path )
                
                with open( temp_path, 'wb' ) as f:
                    
                    for block in HydrusPaths.ReadFileLikeAsBlocks( request.content ): 
                        
                        f.write( block )
                        
                        total_bytes_read += len( block )
                        
                    
                
                decompression_bombs_ok = self._DecompressionBombsOK( request )
                
                parsed_request_args = HydrusNetworkVariableHandling.ParseFileArguments( temp_path, decompression_bombs_ok )
                
            
            self._reportDataUsed( request, total_bytes_read )
            
        
        request.parsed_request_args = parsed_request_args
        
        return request
        
    
    def _checkService( self, request: HydrusServerRequest.HydrusRequest ):
        
        if self.BLOCKED_WHEN_BUSY and HG.server_busy.locked():
            
            raise HydrusExceptions.ServerBusyException( 'This server is busy, please try again later.' )
            
        
        return request
        
    
class HydrusResourceAccessKey( HydrusResourceHydrusNetwork ):
    
    def _threadDoGETJob( self, request: HydrusServerRequest.HydrusRequest ):
        
        registration_key = request.parsed_request_args[ 'registration_key' ]
        
        access_key = HG.server_controller.Read( 'access_key', self._service_key, registration_key )
        
        body = HydrusNetworkVariableHandling.DumpHydrusArgsToNetworkBytes( { 'access_key' : access_key } )
        
        response_context = HydrusServerResources.ResponseContext( 200, body = body )
        
        return response_context
        
    
class HydrusResourceAccessKeyVerification( HydrusResourceHydrusNetwork ):
    
    def _threadDoGETJob( self, request: HydrusServerRequest.HydrusRequest ):
        
        access_key = self._parseHydrusNetworkAccessKey( request )
        
        verified = HG.server_controller.Read( 'verify_access_key', self._service_key, access_key )
        
        body = HydrusNetworkVariableHandling.DumpHydrusArgsToNetworkBytes( { 'verified' : verified } )
        
        response_context = HydrusServerResources.ResponseContext( 200, body = body )
        
        return response_context
        
    
class HydrusResourceAutoCreateAccountTypes( HydrusResourceHydrusNetwork ):
    
    def _threadDoGETJob( self, request: HydrusServerRequest.HydrusRequest ):
        
        account_types = HG.server_controller.Read( 'auto_create_account_types', self._service_key )
        
        body = HydrusNetworkVariableHandling.DumpHydrusArgsToNetworkBytes( { 'account_types' : account_types } )
        
        response_context = HydrusServerResources.ResponseContext( 200, body = body )
        
        return response_context
        
    
class HydrusResourceRestrictedAutoCreateRegistrationKey( HydrusResourceHydrusNetwork ):
    
    def _threadDoGETJob( self, request: HydrusServerRequest.HydrusRequest ):
        
        account_type_key = request.parsed_request_args[ 'account_type_key' ]
        
        registration_key = HG.server_controller.Read( 'auto_create_registration_key', self._service_key, account_type_key )
        
        body = HydrusNetworkVariableHandling.DumpHydrusArgsToNetworkBytes( { 'registration_key' : registration_key } )
        
        response_context = HydrusServerResources.ResponseContext( 200, body = body )
        
        return response_context
        
    
class HydrusResourceShutdown( HydrusResourceHydrusNetwork ):
    
    def _threadDoPOSTJob( self, request: HydrusServerRequest.HydrusRequest ):
        
        HG.server_controller.ShutdownFromServer()
        
        response_context = HydrusServerResources.ResponseContext( 200 )
        
        return response_context
        
    
class HydrusResourceSessionKey( HydrusResourceHydrusNetwork ):
    
    def _threadDoGETJob( self, request: HydrusServerRequest.HydrusRequest ):
        
        access_key = self._parseHydrusNetworkAccessKey( request )
        
        ( session_key, expires ) = HG.server_controller.server_session_manager.AddSession( self._service_key, access_key )
        
        now = HydrusData.GetNow()
        
        max_age = expires - now
        
        cookies = [ ( 'session_key', session_key.hex(), { 'max_age' : str( max_age ), 'path' : '/' } ) ]
        
        response_context = HydrusServerResources.ResponseContext( 200, cookies = cookies )
        
        return response_context
        
    
class HydrusResourceRestricted( HydrusResourceHydrusNetwork ):
    
    def _callbackCheckAccountRestrictions( self, request: HydrusServerRequest.HydrusRequest ):
        
        HydrusResourceHydrusNetwork._callbackCheckAccountRestrictions( self, request )
        
        self._checkAccount( request )
        
        self._checkAccountPermissions( request )
        
        return request
        
    
    def _callbackEstablishAccountFromHeader( self, request: HydrusServerRequest.HydrusRequest ):
        
        session_key = None
        
        if request.requestHeaders.hasHeader( 'Cookie' ):
            
            cookie_texts = request.requestHeaders.getRawHeaders( 'Cookie' )
            
            cookie_text = cookie_texts[0]
            
            try:
                
                cookies = http.cookies.SimpleCookie( cookie_text )
                
                if 'session_key' in cookies:
                    
                    # Morsel, for real, ha ha ha
                    morsel = cookies[ 'session_key' ]
                    
                    session_key_hex = morsel.value
                    
                    session_key = bytes.fromhex( session_key_hex )
                    
                
            except:
                
                raise HydrusExceptions.BadRequestException( 'Problem parsing cookies for Session Cookie!' )
                
            
        
        if session_key is None:
            
            access_key = self._parseHydrusNetworkAccessKey( request, key_required = False )
            
            if access_key is None:
                
                raise HydrusExceptions.MissingCredentialsException( 'No credentials found in request!' )
                
            else:
                
                account = HG.server_controller.server_session_manager.GetAccountFromAccessKey( self._service_key, access_key )
                
            
        else:
            
            account = HG.server_controller.server_session_manager.GetAccount( self._service_key, session_key )
            
        
        request.hydrus_account = account
        
        return request
        
    
    def _checkAccount( self, request: HydrusServerRequest.HydrusRequest ):
        
        request.hydrus_account.CheckFunctional()
        
        return request
        
    
    def _checkAccountPermissions( self, request: HydrusServerRequest.HydrusRequest ):
        
        raise NotImplementedError()
        
    
    def _checkBandwidth( self, request: HydrusServerRequest.HydrusRequest ):
        
        if not self._service.BandwidthOK():
            
            raise HydrusExceptions.BandwidthException( 'This service has run out of bandwidth. Please try again later.' )
            
        
        if not HG.server_controller.ServerBandwidthOK():
            
            raise HydrusExceptions.BandwidthException( 'This server has run out of bandwidth. Please try again later.' )
            
        
    
    def _reportDataUsed( self, request, num_bytes ):
        
        HydrusResourceHydrusNetwork._reportDataUsed( self, request, num_bytes )
        
        account = request.hydrus_account
        
        if account is not None:
            
            account.ReportDataUsed( num_bytes )
            
        
    
    def _reportRequestUsed( self, request: HydrusServerRequest.HydrusRequest ):
        
        HydrusResourceHydrusNetwork._reportRequestUsed( self, request )
        
        account = request.hydrus_account
        
        if account is not None:
            
            account.ReportRequestUsed()
            
        
    
class HydrusResourceRestrictedAccount( HydrusResourceRestricted ):
    
    def _checkAccount( self, request: HydrusServerRequest.HydrusRequest ):
        
        # you can always fetch your account (e.g. to be notified that you are banned!)
        
        return request
        
    
    def _checkAccountPermissions( self, request: HydrusServerRequest.HydrusRequest ):
        
        # you can always fetch your account
        
        pass
        
    
    def _threadDoGETJob( self, request: HydrusServerRequest.HydrusRequest ):
        
        account = request.hydrus_account
        
        body = HydrusNetworkVariableHandling.DumpHydrusArgsToNetworkBytes( { 'account' : account } )
        
        response_context = HydrusServerResources.ResponseContext( 200, body = body )
        
        return response_context
        
    
class HydrusResourceRestrictedOptions( HydrusResourceRestricted ):
    
    def _checkAccount( self, request: HydrusServerRequest.HydrusRequest ):
        
        # you can always fetch the options
        
        return request
        
    
    def _checkAccountPermissions( self, request: HydrusServerRequest.HydrusRequest ):
        
        # you can always fetch the options
        
        pass
        
    
    def _threadDoGETJob( self, request: HydrusServerRequest.HydrusRequest ):
        
        service_options = self._service.GetServiceOptions()
        
        body = HydrusNetworkVariableHandling.DumpHydrusArgsToNetworkBytes( { 'service_options' : service_options } )
        
        response_context = HydrusServerResources.ResponseContext( 200, body = body )
        
        return response_context
        
    
class HydrusResourceRestrictedOptionsModify( HydrusResourceRestricted ):
    
    def _checkAccountPermissions( self, request: HydrusServerRequest.HydrusRequest ):
        
        request.hydrus_account.CheckPermission( HC.CONTENT_TYPE_OPTIONS, HC.PERMISSION_ACTION_MODERATE )
        
    
class HydrusResourceRestrictedOptionsModifyNullificationPeriod( HydrusResourceRestrictedOptionsModify ):
    
    def _threadDoPOSTJob( self, request: HydrusServerRequest.HydrusRequest ):
        
        nullification_period = request.parsed_request_args[ 'nullification_period' ]
        
        if nullification_period < HydrusNetwork.MIN_NULLIFICATION_PERIOD:
            
            raise HydrusExceptions.BadRequestException( 'The anonymisation period was too low. It needs to be at least {}.'.format( HydrusData.TimeDeltaToPrettyTimeDelta( HydrusNetwork.MIN_NULLIFICATION_PERIOD ) ) )
            
        
        if nullification_period > HydrusNetwork.MAX_NULLIFICATION_PERIOD:
            
            raise HydrusExceptions.BadRequestException( 'The anonymisation period was too high. It needs to be lower than {}.'.format( HydrusData.TimeDeltaToPrettyTimeDelta( HydrusNetwork.MAX_NULLIFICATION_PERIOD ) ) )
            
        
        old_nullification_period = self._service.GetNullificationPeriod()
        
        if old_nullification_period != nullification_period:
            
            self._service.SetNullificationPeriod( nullification_period )
            
            HydrusData.Print(
                'Account {} changed the anonymisation period to from "{}" to "{}".'.format(
                    request.hydrus_account.GetAccountKey().hex(),
                    HydrusData.TimeDeltaToPrettyTimeDelta( old_nullification_period ),
                    HydrusData.TimeDeltaToPrettyTimeDelta( nullification_period )
                )
            )
            
        
        response_context = HydrusServerResources.ResponseContext( 200 )
        
        return response_context
        
    
class HydrusResourceRestrictedOptionsModifyUpdatePeriod( HydrusResourceRestrictedOptionsModify ):
    
    def _threadDoPOSTJob( self, request: HydrusServerRequest.HydrusRequest ):
        
        update_period = request.parsed_request_args[ 'update_period' ]
        
        if update_period < HydrusNetwork.MIN_UPDATE_PERIOD:
            
            raise HydrusExceptions.BadRequestException( 'The update period was too low. It needs to be at least {}.'.format( HydrusData.TimeDeltaToPrettyTimeDelta( HydrusNetwork.MIN_UPDATE_PERIOD ) ) )
            
        
        if update_period > HydrusNetwork.MAX_UPDATE_PERIOD:
            
            raise HydrusExceptions.BadRequestException( 'The update period was too high. It needs to be lower than {}.'.format( HydrusData.TimeDeltaToPrettyTimeDelta( HydrusNetwork.MAX_UPDATE_PERIOD ) ) )
            
        
        old_update_period = self._service.GetUpdatePeriod()
        
        if old_update_period != update_period:
            
            self._service.SetUpdatePeriod( update_period )
            
            HydrusData.Print(
                'Account {} changed the update period to from "{}" to "{}".'.format(
                    request.hydrus_account.GetAccountKey().hex(),
                    HydrusData.TimeDeltaToPrettyTimeDelta( old_update_period ),
                    HydrusData.TimeDeltaToPrettyTimeDelta( update_period )
                )
            )
            
        
        response_context = HydrusServerResources.ResponseContext( 200 )
        
        return response_context
        
    
class HydrusResourceRestrictedAccountModify( HydrusResourceRestricted ):
    
    def _checkAccountPermissions( self, request: HydrusServerRequest.HydrusRequest ):
        
        request.hydrus_account.CheckPermission( HC.CONTENT_TYPE_ACCOUNTS, HC.PERMISSION_ACTION_MODERATE )
        
    
class HydrusResourceRestrictedAccountInfo( HydrusResourceRestrictedAccountModify ):
    
    def _threadDoGETJob( self, request: HydrusServerRequest.HydrusRequest ):
        
        if 'subject_identifier' not in request.parsed_request_args:
            
            raise HydrusExceptions.BadRequestException( 'I was expecting an account id, but did not get one!' )
            
        
        subject_identifier = request.parsed_request_args[ 'subject_identifier' ]
        
        if subject_identifier.HasAccountKey():
            
            subject_account_key = subject_identifier.GetAccountKey()
            
        else:
            
            raise HydrusExceptions.BadRequestException( 'The subject\'s account identifier did not include an account id!' )
            
        
        subject_account = HG.server_controller.Read( 'account', self._service_key, subject_account_key )
        
        account_info = HG.server_controller.Read( 'account_info', self._service_key, request.hydrus_account, subject_account )
        
        body = HydrusNetworkVariableHandling.DumpHydrusArgsToNetworkBytes( { 'account_info' : account_info } )
        
        response_context = HydrusServerResources.ResponseContext( 200, body = body )
        
        return response_context
        
    
class HydrusResourceRestrictedAccountModifyAccountType( HydrusResourceRestrictedAccountModify ):
    
    def _threadDoPOSTJob( self, request: HydrusServerRequest.HydrusRequest ):
        
        if 'subject_identifier' not in request.parsed_request_args:
            
            raise HydrusExceptions.BadRequestException( 'I was expecting an account id, but did not get one!' )
            
        
        subject_identifier = request.parsed_request_args[ 'subject_identifier' ]
        
        if subject_identifier.HasAccountKey():
            
            subject_account_key = subject_identifier.GetAccountKey()
            
        else:
            
            raise HydrusExceptions.BadRequestException( 'The subject\'s account identifier did not include an account id!' )
            
        
        if 'account_type_key' not in request.parsed_request_args:
            
            raise HydrusExceptions.BadRequestException( 'I was expecting an account type key, but did not get one!' )
            
        
        account_type_key = request.parsed_request_args[ 'account_type_key' ]
        
        HG.server_controller.WriteSynchronous( 'modify_account_account_type', self._service_key, request.hydrus_account, subject_account_key, account_type_key )
        
        response_context = HydrusServerResources.ResponseContext( 200 )
        
        return response_context
        
    
class HydrusResourceRestrictedAccountModifyBan( HydrusResourceRestrictedAccountModify ):
    
    def _threadDoPOSTJob( self, request: HydrusServerRequest.HydrusRequest ):
        
        if 'subject_identifier' not in request.parsed_request_args:
            
            raise HydrusExceptions.BadRequestException( 'I was expecting an account id, but did not get one!' )
            
        
        subject_identifier = request.parsed_request_args[ 'subject_identifier' ]
        
        if subject_identifier.HasAccountKey():
            
            subject_account_key = subject_identifier.GetAccountKey()
            
        else:
            
            raise HydrusExceptions.BadRequestException( 'The subject\'s account identifier did not include an account id!' )
            
        
        if 'reason' not in request.parsed_request_args:
            
            raise HydrusExceptions.BadRequestException( 'I was expecting a reason for the ban, but did not get one!' )
            
        
        if 'expires' not in request.parsed_request_args:
            
            raise HydrusExceptions.BadRequestException( 'I was expecting a new expiration timestamp, but did not get one!' )
            
        
        reason = request.parsed_request_args[ 'reason' ]
        
        if not isinstance( reason, str ):
            
            raise HydrusExceptions.BadRequestException( 'The given ban reason was not a string!' )
            
        
        expires = request.parsed_request_args[ 'expires' ]
        
        expires_is_none = expires is None
        expires_is_positive_integer = isinstance( expires, int ) and expires > 0
        
        expires_is_valid = expires_is_none or expires_is_positive_integer
        
        if not expires_is_valid:
            
            raise HydrusExceptions.BadRequestException( 'The given expiration timestamp was not null or an integer!' )
            
        
        HG.server_controller.WriteSynchronous( 'modify_account_ban', self._service_key, request.hydrus_account, subject_account_key, reason, expires )
        
        response_context = HydrusServerResources.ResponseContext( 200 )
        
        return response_context
        
    
class HydrusResourceRestrictedAccountModifyExpires( HydrusResourceRestrictedAccountModify ):
    
    def _threadDoPOSTJob( self, request: HydrusServerRequest.HydrusRequest ):
        
        if 'subject_identifier' not in request.parsed_request_args:
            
            raise HydrusExceptions.BadRequestException( 'I was expecting an account id, but did not get one!' )
            
        
        subject_identifier = request.parsed_request_args[ 'subject_identifier' ]
        
        if subject_identifier.HasAccountKey():
            
            subject_account_key = subject_identifier.GetAccountKey()
            
        else:
            
            raise HydrusExceptions.BadRequestException( 'The subject\'s account identifier did not include an account id!' )
            
        
        if 'expires' not in request.parsed_request_args:
            
            raise HydrusExceptions.BadRequestException( 'I was expecting a new expiration timestamp, but did not get one!' )
            
        
        expires = request.parsed_request_args[ 'expires' ]
        
        expires_is_none = expires is None
        expires_is_positive_integer = isinstance( expires, int ) and expires > 0
        
        expires_is_valid = expires_is_none or expires_is_positive_integer
        
        if not expires_is_valid:
            
            raise HydrusExceptions.BadRequestException( 'The given expiration timestamp was not None or an integer!' )
            
        
        HG.server_controller.WriteSynchronous( 'modify_account_expires', self._service_key, request.hydrus_account, subject_account_key, expires )
        
        response_context = HydrusServerResources.ResponseContext( 200 )
        
        return response_context
        
    
class HydrusResourceRestrictedAccountModifySetMessage( HydrusResourceRestrictedAccountModify ):
    
    def _threadDoPOSTJob( self, request: HydrusServerRequest.HydrusRequest ):
        
        if 'subject_identifier' not in request.parsed_request_args:
            
            raise HydrusExceptions.BadRequestException( 'I was expecting an account id, but did not get one!' )
            
        
        subject_identifier = request.parsed_request_args[ 'subject_identifier' ]
        
        if subject_identifier.HasAccountKey():
            
            subject_account_key = subject_identifier.GetAccountKey()
            
        else:
            
            raise HydrusExceptions.BadRequestException( 'The subject\'s account identifier did not include an account id!' )
            
        
        if 'message' not in request.parsed_request_args:
            
            raise HydrusExceptions.BadRequestException( 'I was expecting a new message, but did not get one!' )
            
        
        message = request.parsed_request_args[ 'message' ]
        
        if not isinstance( message, str ):
            
            raise HydrusExceptions.BadRequestException( 'The given message was not a string!' )
            
        
        HG.server_controller.WriteSynchronous( 'modify_account_set_message', self._service_key, request.hydrus_account, subject_account_key, message )
        
        response_context = HydrusServerResources.ResponseContext( 200 )
        
        return response_context
        
    
class HydrusResourceRestrictedAccountModifyUnban( HydrusResourceRestrictedAccountModify ):
    
    def _threadDoPOSTJob( self, request: HydrusServerRequest.HydrusRequest ):
        
        if 'subject_identifier' not in request.parsed_request_args:
            
            raise HydrusExceptions.BadRequestException( 'I was expecting an account id, but did not get one!' )
            
        
        subject_identifier = request.parsed_request_args[ 'subject_identifier' ]
        
        if subject_identifier.HasAccountKey():
            
            subject_account_key = subject_identifier.GetAccountKey()
            
        else:
            
            raise HydrusExceptions.BadRequestException( 'The subject\'s account identifier did not include an account id!' )
            
        
        HG.server_controller.WriteSynchronous( 'modify_account_unban', self._service_key, request.hydrus_account, subject_account_key )
        
        response_context = HydrusServerResources.ResponseContext( 200 )
        
        return response_context
        
    
class HydrusResourceRestrictedAccountOtherAccount( HydrusResourceRestrictedAccountModify ):
    
    def _threadDoGETJob( self, request: HydrusServerRequest.HydrusRequest ):
        
        if 'subject_identifier' not in request.parsed_request_args:
            
            raise HydrusExceptions.BadRequestException( 'I was expecting an account identifier for the subject, but did not get one!' )
            
        
        subject_identifier = request.parsed_request_args[ 'subject_identifier' ]
        
        if subject_identifier.HasAccountKey():
            
            subject_account_key = subject_identifier.GetAccountKey()
            
            try:
                
                subject_account = HG.server_controller.Read( 'account', self._service_key, subject_account_key )
                
            except HydrusExceptions.InsufficientCredentialsException as e:
                
                raise HydrusExceptions.NotFoundException( e )
                
            
        elif subject_identifier.HasContent():
            
            subject_content = subject_identifier.GetContent()
            
            subject_account = HG.server_controller.Read( 'account_from_content', self._service_key, subject_content )
            
        else:
            
            raise HydrusExceptions.BadRequestException( 'The subject\'s account identifier did not include an account id or content!' )
            
        
        body = HydrusNetworkVariableHandling.DumpHydrusArgsToNetworkBytes( { 'account' : subject_account } )
        
        response_context = HydrusServerResources.ResponseContext( 200, body = body )
        
        return response_context
        
    
class HydrusResourceRestrictedIP( HydrusResourceRestrictedAccountModify ):
    
    def _threadDoGETJob( self, request: HydrusServerRequest.HydrusRequest ):
        
        hash = request.parsed_request_args[ 'hash' ]
        
        ( ip, timestamp ) = HG.server_controller.Read( 'ip', self._service_key, request.hydrus_account, hash )
        
        body = HydrusNetworkVariableHandling.DumpHydrusArgsToNetworkBytes( { 'ip' : ip, 'timestamp' : timestamp } )
        
        response_context = HydrusServerResources.ResponseContext( 200, body = body )
        
        return response_context
        
    
class HydrusResourceRestrictedAllAccounts( HydrusResourceRestrictedAccountModify ):
    
    def _threadDoGETJob( self, request: HydrusServerRequest.HydrusRequest ):
        
        accounts = HG.server_controller.Read( 'all_accounts', self._service_key, request.hydrus_account )
        
        body = HydrusNetworkVariableHandling.DumpHydrusArgsToNetworkBytes( { 'accounts' : accounts } )
        
        response_context = HydrusServerResources.ResponseContext( 200, body = body )
        
        return response_context
        
    
class HydrusResourceRestrictedAccountTypes( HydrusResourceRestricted ):
    
    def _checkAccountPermissions( self, request: HydrusServerRequest.HydrusRequest ):
        
        if request.IsGET():
            
            request.hydrus_account.CheckPermission( HC.CONTENT_TYPE_ACCOUNTS, HC.PERMISSION_ACTION_CREATE )
            
        elif request.IsPOST():
            
            request.hydrus_account.CheckPermission( HC.CONTENT_TYPE_ACCOUNT_TYPES, HC.PERMISSION_ACTION_MODERATE )
            
        
    
    def _threadDoGETJob( self, request: HydrusServerRequest.HydrusRequest ):
        
        account_types = HG.server_controller.Read( 'account_types', self._service_key, request.hydrus_account )
        
        body = HydrusNetworkVariableHandling.DumpHydrusArgsToNetworkBytes( { 'account_types' : account_types } )
        
        response_context = HydrusServerResources.ResponseContext( 200, body = body )
        
        return response_context
        
    
    def _threadDoPOSTJob( self, request: HydrusServerRequest.HydrusRequest ):
        
        account_types = request.parsed_request_args[ 'account_types' ]
        deletee_account_type_keys_to_new_account_type_keys = request.parsed_request_args[ 'deletee_account_type_keys_to_new_account_type_keys' ]
        
        HG.server_controller.WriteSynchronous( 'account_types', self._service_key, request.hydrus_account, account_types, deletee_account_type_keys_to_new_account_type_keys )
        
        HG.server_controller.server_session_manager.RefreshAccounts( self._service_key )
        
        response_context = HydrusServerResources.ResponseContext( 200 )
        
        return response_context
        
    
class HydrusResourceRestrictedBackup( HydrusResourceRestricted ):
    
    def _checkAccountPermissions( self, request: HydrusServerRequest.HydrusRequest ):
        
        request.hydrus_account.CheckPermission( HC.CONTENT_TYPE_SERVICES, HC.PERMISSION_ACTION_MODERATE )
        
    
    def _threadDoPOSTJob( self, request: HydrusServerRequest.HydrusRequest ):
        
        HG.server_controller.Write( 'backup' )
        
        response_context = HydrusServerResources.ResponseContext( 200 )
        
        return response_context
        
    
class HydrusResourceRestrictedLockOn( HydrusResourceRestricted ):
    
    def _checkAccountPermissions( self, request: HydrusServerRequest.HydrusRequest ):
        
        request.hydrus_account.CheckPermission( HC.CONTENT_TYPE_SERVICES, HC.PERMISSION_ACTION_MODERATE )
        
    
    def _threadDoPOSTJob( self, request: HydrusServerRequest.HydrusRequest ):
        
        locked = HG.server_busy.acquire( False ) # pylint: disable=E1111
        
        if not locked:
            
            raise HydrusExceptions.BadRequestException( 'The server was already locked!' )
            
        
        HG.server_controller.db.PauseAndDisconnect( True )
        
        TIME_BLOCK = 0.25
        
        for i in range( int( 5 / TIME_BLOCK ) ):
            
            if not HG.server_controller.db.IsConnected():
                
                break
                
            
            time.sleep( TIME_BLOCK )
            
        
        response_context = HydrusServerResources.ResponseContext( 200 )
        
        return response_context
        
    
class HydrusResourceRestrictedLockOff( HydrusResourceRestricted ):
    
    BLOCKED_WHEN_BUSY = False
    
    def _checkAccountPermissions( self, request: HydrusServerRequest.HydrusRequest ):
        
        request.hydrus_account.CheckPermission( HC.CONTENT_TYPE_SERVICES, HC.PERMISSION_ACTION_MODERATE )
        
    
    def _threadDoPOSTJob( self, request: HydrusServerRequest.HydrusRequest ):
        
        try:
            
            HG.server_busy.release()
            
        except threading.ThreadError:
            
            raise HydrusExceptions.BadRequestException( 'The server is not busy!' )
            
        
        HG.server_controller.db.PauseAndDisconnect( False )
        
        response_context = HydrusServerResources.ResponseContext( 200 )
        
        return response_context
        
    
class HydrusResourceRestrictedNumPetitions( HydrusResourceRestricted ):
    
    def _checkAccountPermissions( self, request: HydrusServerRequest.HydrusRequest ):
        
        # further permissions checked in the db
        
        request.hydrus_account.CheckAtLeastOnePermission( [ ( content_type, HC.PERMISSION_ACTION_MODERATE ) for content_type in HC.SERVICE_TYPES_TO_CONTENT_TYPES[ self._service.GetServiceType() ] ] )
        
    
    def _threadDoGETJob( self, request: HydrusServerRequest.HydrusRequest ):
        
        # cache this
        petition_count_info = HG.server_controller.Read( 'num_petitions', self._service_key, request.hydrus_account )
        
        body = HydrusNetworkVariableHandling.DumpHydrusArgsToNetworkBytes( { 'num_petitions' : petition_count_info } )
        
        response_context = HydrusServerResources.ResponseContext( 200, body = body )
        
        return response_context
        
    
class HydrusResourceRestrictedPetitionSummaryList( HydrusResourceRestricted ):
    
    def _checkAccountPermissions( self, request: HydrusServerRequest.HydrusRequest ):
        
        content_type = request.parsed_request_args[ 'content_type' ]
        
        request.hydrus_account.CheckPermission( content_type, HC.PERMISSION_ACTION_MODERATE )
        
    
    def _threadDoGETJob( self, request: HydrusServerRequest.HydrusRequest ):
        
        # fetch cached summary list
        # ( account_key, reason, size of petition )
        petition_summary_list = []
        
        body = HydrusNetworkVariableHandling.DumpHydrusArgsToNetworkBytes( { 'petition_summary_list' : petition_summary_list } )
        
        response_context = HydrusServerResources.ResponseContext( 200, body = body )
        
        return response_context
        
    
class HydrusResourceRestrictedPetition( HydrusResourceRestricted ):
    
    def _checkAccountPermissions( self, request: HydrusServerRequest.HydrusRequest ):
        
        content_type = request.parsed_request_args[ 'content_type' ]
        
        request.hydrus_account.CheckPermission( content_type, HC.PERMISSION_ACTION_MODERATE )
        
    
    def _threadDoGETJob( self, request: HydrusServerRequest.HydrusRequest ):
        
        # rewangle this to take an id from the summary list. probably ( account_key, reason_id )
        # and combine petitioned and pending into the same petition
        content_type = request.parsed_request_args[ 'content_type' ]
        status = request.parsed_request_args[ 'status' ]
        
        petition = HG.server_controller.Read( 'petition', self._service_key, request.hydrus_account, content_type, status )
        
        body = HydrusNetworkVariableHandling.DumpHydrusArgsToNetworkBytes( { 'petition' : petition } )
        
        response_context = HydrusServerResources.ResponseContext( 200, body = body )
        
        return response_context
        
    
class HydrusResourceRestrictedRegistrationKeys( HydrusResourceRestricted ):
    
    def _checkAccountPermissions( self, request: HydrusServerRequest.HydrusRequest ):
        
        request.hydrus_account.CheckPermission( HC.CONTENT_TYPE_ACCOUNTS, HC.PERMISSION_ACTION_CREATE )
        
    
    def _threadDoGETJob( self, request: HydrusServerRequest.HydrusRequest ):
        
        num = request.parsed_request_args[ 'num' ]
        account_type_key = request.parsed_request_args[ 'account_type_key' ]
        
        if 'expires' in request.parsed_request_args:
            
            expires = request.parsed_request_args[ 'expires' ]
            
        else:
            
            expires = None
            
        
        registration_keys = HG.server_controller.Read( 'registration_keys', self._service_key, request.hydrus_account, num, account_type_key, expires )
        
        body = HydrusNetworkVariableHandling.DumpHydrusArgsToNetworkBytes( { 'registration_keys' : registration_keys } )
        
        response_context = HydrusServerResources.ResponseContext( 200, body = body )
        
        return response_context
        
    
class HydrusResourceRestrictedRepositoryFile( HydrusResourceRestricted ):
    
    def _checkAccountPermissions( self, request: HydrusServerRequest.HydrusRequest ):
        
        # everyone with a functional account can read files
        
        if request.IsPOST():
            
            request.hydrus_account.CheckPermission( HC.CONTENT_TYPE_FILES, HC.PERMISSION_ACTION_CREATE )
            
        
    
    def _DecompressionBombsOK( self, request: HydrusServerRequest.HydrusRequest ):
        
        if request.hydrus_account is None:
            
            return False
            
        else:
            
            return request.hydrus_account.HasPermission( HC.CONTENT_TYPE_ACCOUNTS, HC.PERMISSION_ACTION_CREATE )
            
        
    
    def _threadDoGETJob( self, request: HydrusServerRequest.HydrusRequest ):
        
        self._checkBandwidth( request )
        
        # no permission check as any functional account can get files
        
        hash = request.parsed_request_args[ 'hash' ]
        
        ( valid, mime ) = HG.server_controller.Read( 'service_has_file', self._service_key, hash )
        
        if not valid:
            
            raise HydrusExceptions.NotFoundException( 'File not found on this service!' )
            
        
        path = ServerFiles.GetFilePath( hash )
        
        response_context = HydrusServerResources.ResponseContext( 200, mime = mime, path = path )
        
        return response_context
        
    
    def _threadDoPOSTJob( self, request: HydrusServerRequest.HydrusRequest ):
        
        file_dict = request.parsed_request_args
        
        if self._service.LogUploaderIPs():
            
            file_dict[ 'ip' ] = request.getClientIP()
            
        
        timestamp = self._service.GetMetadata().GetNextUpdateBegin() + 1
        
        HG.server_controller.WriteSynchronous( 'file', self._service, request.hydrus_account, file_dict, timestamp )
        
        response_context = HydrusServerResources.ResponseContext( 200 )
        
        return response_context
        
    
class HydrusResourceRestrictedRepositoryThumbnail( HydrusResourceRestricted ):
    
    def _checkAccountPermissions( self, request: HydrusServerRequest.HydrusRequest ):
        
        # everyone with a functional account can read thumbs
        
        pass
        
    
    def _threadDoGETJob( self, request: HydrusServerRequest.HydrusRequest ):
        
        self._checkBandwidth( request )
        
        # no permission check as any functional account can get thumbnails
        
        hash = request.parsed_request_args[ 'hash' ]
        
        ( valid, mime ) = HG.server_controller.Read( 'service_has_file', self._service_key, hash )
        
        if not valid:
            
            raise HydrusExceptions.NotFoundException( 'Thumbnail not found on this service!' )
            
        
        if mime not in HC.MIMES_WITH_THUMBNAILS:
            
            raise HydrusExceptions.NotFoundException( 'That mime should not have a thumbnail!' )
            
        
        path = ServerFiles.GetThumbnailPath( hash )
        
        response_context = HydrusServerResources.ResponseContext( 200, mime = HC.APPLICATION_OCTET_STREAM, path = path )
        
        return response_context
        
    
class HydrusResourceRestrictedServices( HydrusResourceRestricted ):
    
    def _checkAccountPermissions( self, request: HydrusServerRequest.HydrusRequest ):
        
        request.hydrus_account.CheckPermission( HC.CONTENT_TYPE_SERVICES, HC.PERMISSION_ACTION_MODERATE )
        
    
    def _threadDoGETJob( self, request: HydrusServerRequest.HydrusRequest ):
        
        services = HG.server_controller.Read( 'services_from_account', request.hydrus_account )
        
        body = HydrusNetworkVariableHandling.DumpHydrusArgsToNetworkBytes( { 'services' : services } )
        
        response_context = HydrusServerResources.ResponseContext( 200, body = body )
        
        return response_context
        
    
    def _threadDoPOSTJob( self, request: HydrusServerRequest.HydrusRequest ):
        
        services = request.parsed_request_args[ 'services' ]
        
        unique_ports = { service.GetPort() for service in services }
        
        if len( unique_ports ) < len( services ):
            
            raise HydrusExceptions.BadRequestException( 'It looks like some of those services share ports! Please give them unique ports!' )
            
        
        with HG.dirty_object_lock:
            
            HG.server_controller.SetServices( services )
            
            service_keys_to_access_keys = HG.server_controller.WriteSynchronous( 'services', request.hydrus_account, services )
            
        
        body = HydrusNetworkVariableHandling.DumpHydrusArgsToNetworkBytes( { 'service_keys_to_access_keys' : service_keys_to_access_keys } )
        
        response_context = HydrusServerResources.ResponseContext( 200, body = body )
        
        return response_context
        
    
class HydrusResourceRestrictedUpdate( HydrusResourceRestricted ):
    
    def _checkAccountPermissions( self, request: HydrusServerRequest.HydrusRequest ):
        
        # everyone with a functional account can read updates
        
        if request.IsPOST():
            
            # further permissions checked in the db
            
            request.hydrus_account.CheckAtLeastOnePermission( [ ( content_type, HC.PERMISSION_ACTION_PETITION ) for content_type in HC.SERVICE_TYPES_TO_CONTENT_TYPES[ self._service.GetServiceType() ] ] )
            
        
    
    def _threadDoGETJob( self, request: HydrusServerRequest.HydrusRequest ):
        
        self._checkBandwidth( request )
        
        # no permissions check as any functional account can get updates
        
        update_hash = request.parsed_request_args[ 'update_hash' ]
        
        if not self._service.HasUpdateHash( update_hash ):
            
            raise HydrusExceptions.NotFoundException( 'This update hash does not exist on this service!' )
            
        
        path = ServerFiles.GetFilePath( update_hash )
        
        response_context = HydrusServerResources.ResponseContext( 200, mime = HC.APPLICATION_OCTET_STREAM, path = path )
        
        return response_context
        
    
    def _threadDoPOSTJob( self, request: HydrusServerRequest.HydrusRequest ):
        
        client_to_server_update = request.parsed_request_args[ 'client_to_server_update' ]
        
        timestamp = self._service.GetMetadata().GetNextUpdateBegin() + 1
        
        HG.server_controller.WriteSynchronous( 'update', self._service_key, request.hydrus_account, client_to_server_update, timestamp )
        
        response_context = HydrusServerResources.ResponseContext( 200 )
        
        return response_context
        
    
class HydrusResourceRestrictedImmediateUpdate( HydrusResourceRestricted ):
    
    def _checkAccountPermissions( self, request: HydrusServerRequest.HydrusRequest ):
        
        request.hydrus_account.CheckAtLeastOnePermission( [ ( content_type, HC.PERMISSION_ACTION_MODERATE ) for content_type in HC.SERVICE_TYPES_TO_CONTENT_TYPES[ self._service.GetServiceType() ] ] )
        
    
    def _threadDoGETJob( self, request: HydrusServerRequest.HydrusRequest ):
        
        updates = HG.server_controller.Read( 'immediate_update', self._service_key, request.hydrus_account )
        
        updates = HydrusSerialisable.SerialisableList( updates )
        
        body = HydrusNetworkVariableHandling.DumpHydrusArgsToNetworkBytes( { 'updates' : updates } )
        
        response_context = HydrusServerResources.ResponseContext( 200, body = body )
        
        return response_context
        
    
class HydrusResourceRestrictedMetadataUpdate( HydrusResourceRestricted ):
    
    def _checkAccountPermissions( self, request: HydrusServerRequest.HydrusRequest ):
        
        pass # everyone with a functional account can get metadata slices
        
    
    def _threadDoGETJob( self, request: HydrusServerRequest.HydrusRequest ):
        
        since = request.parsed_request_args[ 'since' ]
        
        metadata_slice = self._service.GetMetadataSlice( since )
        
        body = HydrusNetworkVariableHandling.DumpHydrusArgsToNetworkBytes( { 'metadata_slice' : metadata_slice } )
        
        response_context = HydrusServerResources.ResponseContext( 200, body = body )
        
        return response_context
        
    
class HydrusResourceRestrictedVacuum( HydrusResourceRestricted ):
    
    def _checkAccountPermissions( self, request: HydrusServerRequest.HydrusRequest ):
        
        request.hydrus_account.CheckPermission( HC.CONTENT_TYPE_SERVICES, HC.PERMISSION_ACTION_MODERATE )
        
    
    def _threadDoPOSTJob( self, request: HydrusServerRequest.HydrusRequest ):
        
        HG.server_controller.Write( 'vacuum' )
        
        response_context = HydrusServerResources.ResponseContext( 200 )
        
        return response_context
        
    
