# Path: autopush/db.py
# def generate_last_connect():
#     # type: () -> int
#     """Generate a last_connect
# 
#     This intentionally generates a limited set of keys for each month in a
#     known sequence. For each month, there's 24 hours * 10 random numbers for
#     a total of 240 keys per month depending on when the user migrates forward.
# 
#     """
#     today = datetime.datetime.today()
#     val = "".join([
#         str(today.year),
#         str(today.month).zfill(2),
#         str(today.hour).zfill(2),
#         str(random.randint(0, 10)).zfill(4),
#     ])
#     return int(val)
# 
# def hasher(uaid):
#     # type: (str) -> str
#     """Hashes a key using a key_hash if present"""
#     if key_hash:
#         return generate_hash(key_hash, uaid)
#     return uaid
# 
# Path: autopush/exceptions.py
# class InvalidRequest(AutopushException):
#     """Invalid request exception, may include custom status_code and message
#     to write for the error"""
#     def __init__(self, message, status_code=400, errno=None, headers=None):
#         super(AutopushException, self).__init__(message)
#         self.status_code = status_code
#         self.errno = errno
#         self.headers = {} if headers is None else headers
# 
# class ItemNotFound(Exception):
#     """Signals missing DynamoDB Item data"""
# 
# class RouterException(AutopushException):
#     """Exception if routing has failed, may include a custom status_code and
#     body to write to the response.
# 
#     """
#     def __init__(self, message, status_code=500, response_body="",
#                  router_data=None, headers=None, log_exception=True,
#                  errno=None, logged_status=None, **kwargs):
#         """Create a new RouterException"""
#         super(AutopushException, self).__init__(message)
#         self.status_code = status_code
#         self.headers = {} if headers is None else headers
#         self.log_exception = log_exception
#         self.response_body = response_body or message
#         self.errno = errno
#         self.logged_status = logged_status
#         self.extra = kwargs
# 
# Path: autopush/types.py

# 
# Path: autopush/utils.py
# def generate_hash(key, payload):
#     # type: (str, str) -> str
#     """Generate a HMAC for the uaid using the secret
# 
#     :returns: HMAC hash and the nonce used as a tuple (nonce, hash).
# 
#     """
#     h = hmac.new(key=key, msg=payload, digestmod=hashlib.sha256)
#     return h.hexdigest()
# 
# def ms_time():
#     # type: () -> int
#     """Return current time.time call as ms and a Python int"""
#     return int(time.time() * MS_MULT)
# 
# Path: autopush/web/base.py
# AUTH_SCHEMES = ["bearer", "webpush", "vapid"]
# PREF_SCHEME = "webpush"
# DEFAULT_ERR_URL = ("http://autopush.readthedocs.io/en/latest/http.html"
#                    "#error-codes")
# class ThreadedValidate(object):
# class NotFoundHandler(BaseHandler):
# class BaseWebHandler(BaseHandler):
#     def __init__(self, schema):
#     def _validate_request(self, request_handler, *args, **kwargs):
#     def _call_func(self, result, func, request_handler):
#     def _track_validation_timing(self, result, request_handler, start_time):
#     def _decorator(self, func):
#         def wrapper(request_handler, *args, **kwargs):
#     def validate(cls, schema):
#     def get(self, *args, **kwargs):
#     def initialize(self):
#     def routers(self):
#     def prepare(self):
#     def options(self, *args, **kwargs):
#     def head(self, *args, **kwargs):
#     def _write_response(self, status_code, errno, message=None, error=None,
#                         headers=None,
#                         url=DEFAULT_ERR_URL,
#                         router_type=None,
#                         vapid=None):
#     def _validation_err(self, fail):
#     def _response_err(self, fail):
#     def _boto_err(self, fail):
#     def _router_response(self, response, router_type, vapid):
#     def _router_fail_err(self, fail, router_type=None, vapid=False, uaid=None):
#     def _write_validation_err(self, errors):
#     def _db_error_handling(self, d):
#     def _track_timing(self, status_code=None):


import re
import uuid
import simplejson as json
from marshmallow_polyfield import PolyField
from typing import (  # noqa
    Optional,
    Set,
    Tuple
)
from cryptography.hazmat.primitives import constant_time
from marshmallow import (
    Schema,
    fields,
    pre_load,
    post_load,
    validate,
    validates,
    validates_schema,
)
from twisted.internet.defer import Deferred  # noqa
from twisted.internet.threads import deferToThread
from autopush.db import generate_last_connect, hasher
from autopush.exceptions import InvalidRequest, ItemNotFound, RouterException
from autopush.types import JSONDict  # noqa
from autopush.utils import generate_hash, ms_time
from autopush.web.base import (
    threaded_validate,
    BaseWebHandler,
    PREF_SCHEME,
    AUTH_SCHEMES
)
        # type: (str, JSONDict, uuid.UUID) -> Deferred
        """HTTP PUT

        Update router type/data for a UAID.

        """
        d = deferToThread(self._register_user, uaid, router_type, router_data)
        d.addCallback(self._success)
        d.addErrback(self._router_fail_err)
        d.addErrback(self._response_err)
        return d

    post = put

    @threaded_validate(UnregisterUaidSchema)
    def delete(self, uaid):
        # type: (uuid.UUID) -> Deferred
        """HTTP DELETE

        Delete all pending records for the given UAID

        """
        d = deferToThread(self._delete_uaid, uaid)
        d.addCallback(self._success)
        d.addErrback(self._uaid_not_found_err)
        d.addErrback(self._response_err)
        return d

    def _delete_uaid(self, uaid):
        self.log.debug(format="Dropping User", code=101,

# Path: autopush/db.py
# def generate_last_connect():
#     # type: () -> int
#     """Generate a last_connect
# 
#     This intentionally generates a limited set of keys for each month in a
#     known sequence. For each month, there's 24 hours * 10 random numbers for
#     a total of 240 keys per month depending on when the user migrates forward.
# 
#     """
#     today = datetime.datetime.today()
#     val = "".join([
#         str(today.year),
#         str(today.month).zfill(2),
#         str(today.hour).zfill(2),
#         str(random.randint(0, 10)).zfill(4),
#     ])
#     return int(val)
# 
# def hasher(uaid):
#     # type: (str) -> str
#     """Hashes a key using a key_hash if present"""
#     if key_hash:
#         return generate_hash(key_hash, uaid)
#     return uaid
# 
# Path: autopush/exceptions.py
# class InvalidRequest(AutopushException):
#     """Invalid request exception, may include custom status_code and message
#     to write for the error"""
#     def __init__(self, message, status_code=400, errno=None, headers=None):
#         super(AutopushException, self).__init__(message)
#         self.status_code = status_code
#         self.errno = errno
#         self.headers = {} if headers is None else headers
# 
# class ItemNotFound(Exception):
#     """Signals missing DynamoDB Item data"""
# 
# class RouterException(AutopushException):
#     """Exception if routing has failed, may include a custom status_code and
#     body to write to the response.
# 
#     """
#     def __init__(self, message, status_code=500, response_body="",
#                  router_data=None, headers=None, log_exception=True,
#                  errno=None, logged_status=None, **kwargs):
#         """Create a new RouterException"""
#         super(AutopushException, self).__init__(message)
#         self.status_code = status_code
#         self.headers = {} if headers is None else headers
#         self.log_exception = log_exception
#         self.response_body = response_body or message
#         self.errno = errno
#         self.logged_status = logged_status
#         self.extra = kwargs
# 
# Path: autopush/types.py

# 
# Path: autopush/utils.py
# def generate_hash(key, payload):
#     # type: (str, str) -> str
#     """Generate a HMAC for the uaid using the secret
# 
#     :returns: HMAC hash and the nonce used as a tuple (nonce, hash).
# 
#     """
#     h = hmac.new(key=key, msg=payload, digestmod=hashlib.sha256)
#     return h.hexdigest()
# 
# def ms_time():
#     # type: () -> int
#     """Return current time.time call as ms and a Python int"""
#     return int(time.time() * MS_MULT)
# 
# Path: autopush/web/base.py
# AUTH_SCHEMES = ["bearer", "webpush", "vapid"]
# PREF_SCHEME = "webpush"
# DEFAULT_ERR_URL = ("http://autopush.readthedocs.io/en/latest/http.html"
#                    "#error-codes")
# class ThreadedValidate(object):
# class NotFoundHandler(BaseHandler):
# class BaseWebHandler(BaseHandler):
#     def __init__(self, schema):
#     def _validate_request(self, request_handler, *args, **kwargs):
#     def _call_func(self, result, func, request_handler):
#     def _track_validation_timing(self, result, request_handler, start_time):
#     def _decorator(self, func):
#         def wrapper(request_handler, *args, **kwargs):
#     def validate(cls, schema):
#     def get(self, *args, **kwargs):
#     def initialize(self):
#     def routers(self):
#     def prepare(self):
#     def options(self, *args, **kwargs):
#     def head(self, *args, **kwargs):
#     def _write_response(self, status_code, errno, message=None, error=None,
#                         headers=None,
#                         url=DEFAULT_ERR_URL,
#                         router_type=None,
#                         vapid=None):
#     def _validation_err(self, fail):
#     def _response_err(self, fail):
#     def _boto_err(self, fail):
#     def _router_response(self, response, router_type, vapid):
#     def _router_fail_err(self, fail, router_type=None, vapid=False, uaid=None):
#     def _write_validation_err(self, errors):
#     def _db_error_handling(self, d):
#     def _track_timing(self, status_code=None):


# Path: autopush/web/registration.py
import re
import uuid
import simplejson as json
from marshmallow_polyfield import PolyField
from typing import (  # noqa
    Optional,
    Set,
    Tuple
)
from cryptography.hazmat.primitives import constant_time
from marshmallow import (
    Schema,
    fields,
    pre_load,
    post_load,
    validate,
    validates,
    validates_schema,
)
from twisted.internet.defer import Deferred  # noqa
from twisted.internet.threads import deferToThread
from autopush.db import generate_last_connect, hasher
from autopush.exceptions import InvalidRequest, ItemNotFound, RouterException
from autopush.types import JSONDict  # noqa
from autopush.utils import generate_hash, ms_time
from autopush.web.base import (
    threaded_validate,
    BaseWebHandler,
    PREF_SCHEME,
    AUTH_SCHEMES
)

        # type: (str, JSONDict, uuid.UUID) -> Deferred
        """HTTP PUT

        Update router type/data for a UAID.

        """
        d = deferToThread(self._register_user, uaid, router_type, router_data)
        d.addCallback(self._success)
        d.addErrback(self._router_fail_err)
        d.addErrback(self._response_err)
        return d

    post = put

    @threaded_validate(UnregisterUaidSchema)
    def delete(self, uaid):
        # type: (uuid.UUID) -> Deferred
        """HTTP DELETE

        Delete all pending records for the given UAID

        """
        d = deferToThread(self._delete_uaid, uaid)
        d.addCallback(self._success)
        d.addErrback(self._uaid_not_found_err)
        d.addErrback(self._response_err)
        return d

    def _delete_uaid(self, uaid):
        self.log.debug(format="Dropping User", code=101,

                       uaid_hash=hasher(uaid.hex))