#!/usr/bin/python3

from abc import ABCMeta, abstractmethod

import logging
import json

class AbstractChecker(metaclass=ABCMeta):
    """Base class for custom checker scripts

    Individual checkers should import `BaseChecker` which does the
    right thing in terms of backend depending on whether you test
    locally or the checker is run during the Contest. They must
    implement the place_flag and check_flag methods and may add
    individual __init__ code. You may override the run method but need
    to keep it morally the same

    ident used for store/retrieve are supposed to match
    [A-Za-z0-9_-]+. It must be uniq for every (service,target)
    """
    def __init__(self, tick, team, service, ip):
        self._team = team
        self._ip = ip
        self._tick = tick
        self._service = service
        self._tickduration = 300
        self._lookback = 5

    @property
    def tick(self):
        """Returns the current tick"""
        return self._tick

    def check_service(self):
        """ Check if the service is running as expected"""
        return 0

    @abstractmethod
    def check_flag(self, tick):
        """Check for the flag from tick on the tested team's server

        To be reimplemented by users
        """
        pass

    @abstractmethod
    def place_flag(self):
        """Place flag on the tested team's server

        To be reimplemented by users"""
        pass

    def store_yaml(self, ident, yaml):
        return self.store_blob(ident, json.dumps(yaml).encode('utf-8'))

    @abstractmethod
    def store_blob(self, ident, blob):
        "store binary blob on persistent storage"
        pass

    def retrieve_yaml(self, ident):
        try:
            return json.loads(self.retrieve_blob(ident).decode('utf-8'))
        except ValueError:
            return None

    @abstractmethod
    def retrieve_blob(self, ident):
        "return binary blob from persistent storage"
        pass

    @abstractmethod
    def get_flag(self, tick, payload=None):
        "returns the flag for tick possibly including payload"
        pass

    def run(self):
        logging.debug("Placing flag")
        result = self.place_flag()
        if result != 0:
            return result

        logging.debug("General Service Checks")
        result = self.check_service()
        if result != 0:
            return result

        oldesttick = max(self._tick - self._lookback, -1)
        for tick in range(self._tick, oldesttick, -1):
            logging.debug("Checking for flag of tick %d", tick)
            result = self.check_flag(tick)
            if result != 0:
                return result

        return 0

@@ -80,16 +80,46 @@
 ort json
+%0Aimport socket%0Aimport requests
 %0A%0Aclass 
@@ -2133,32 +2133,49 @@
  def run(self):%0A
+        try:%0A    
         logging.
@@ -2196,32 +2196,36 @@
  flag%22)%0A        
+ 
+   
 result = self.pl
@@ -2235,32 +2235,36 @@
 _flag()%0A        
+   
+ 
 if result != 0:%0A
@@ -2267,32 +2267,36 @@
  0:%0A            
+    
 return result%0A%0A 
@@ -2294,32 +2294,36 @@
 result%0A%0A        
+ 
+   
 logging.debug(%22G
@@ -2346,32 +2346,36 @@
 hecks%22)%0A        
+   
+ 
 result = self.ch
@@ -2388,32 +2388,36 @@
 rvice()%0A        
+    
 if result != 0:%0A
@@ -2420,32 +2420,36 @@
  0:%0A            
+ 
+   
 return result%0A%0A 
@@ -2455,16 +2455,20 @@
         
+   
+ 
 oldestti
@@ -2517,16 +2517,20 @@
         
+    
 for tick
@@ -2572,32 +2572,36 @@
 1):%0A            
+ 
+   
 logging.debug(%22C
@@ -2640,32 +2640,36 @@
 ck)%0A            
+   
+ 
 result = self.ch
@@ -2687,32 +2687,36 @@
 ck)%0A            
+    
 if result != 0:%0A
@@ -2723,32 +2723,36 @@
                 
+ 
+   
 return result%0A%0A 
@@ -2750,25 +2750,29 @@
 result%0A%0A        
+   
+ 
 return 0%0A
@@ -2755,24 +2755,141 @@
 t%0A%0A            return 0%0A
+        except socket.timeout:%0A            return 1%0A        except requests.exceptions.Timeout:%0A            return 1%0A

Handle requests / socket timeouts