# Path: src/petmail/errors.py
# class ReplayError(Exception):
#     pass
# 
# Path: src/petmail/eventual.py
# def fireEventually(value=None):
#     """This returns a Deferred which will fire in a later reactor turn, after
#     the current call stack has been completed, and after all other deferreds
#     previously scheduled with callEventually().
#     """
#     d = defer.Deferred()
#     eventually(d.callback, value)
#     return d
# 
# Path: src/petmail/util.py
# def equal(a, b):
#     # not vulnerable to timing attack
#     return hashlib.sha256(a).digest() == hashlib.sha256(b).digest()
# 
# def remove_prefix(s_bytes, prefix, errortype=None):
#     if not s_bytes.startswith(prefix):
#         errortype = errortype or BadPrefixError
#         raise errortype("did not see expected '%s' prefix" % (prefix,))
#     return s_bytes[len(prefix):]
# 
# Path: src/petmail/eventsource.py
# class ReconnectingEventSource(service.MultiService,
#                               protocol.ReconnectingClientFactory):
#     def __init__(self, baseurl, connection_starting, handler):
#         service.MultiService.__init__(self)
#         # we don't use any of the basic Factory/ClientFactory methods of
#         # this, just the ReconnectingClientFactory.retry, stopTrying, and
#         # resetDelay methods.
# 
#         self.baseurl = baseurl
#         self.connection_starting = connection_starting
#         self.handler = handler
#         # IService provides self.running, toggled by {start,stop}Service.
#         # self.active is toggled by {,de}activate. If both .running and
#         # .active are True, then we want to have an outstanding EventSource
#         # and will start one if necessary. If either is False, then we don't
#         # want one to be outstanding, and will initiate shutdown.
#         self.active = False
#         self.connector = Connector(self)
#         self.es = None # set we have an outstanding EventSource
#         self.when_stopped = [] # list of Deferreds
# 
#     def isStopped(self):
#         return not self.es
# 
#     def startService(self):
#         service.MultiService.startService(self) # sets self.running
#         self._maybeStart()
# 
#     def stopService(self):
#         # clears self.running
#         d = defer.maybeDeferred(service.MultiService.stopService, self)
#         d.addCallback(self._maybeStop)
#         return d
# 
#     def activate(self):
#         assert not self.active
#         self.active = True
#         self._maybeStart()
# 
#     def deactivate(self):
#         assert self.active # XXX
#         self.active = False
#         return self._maybeStop()
# 
#     def _maybeStart(self):
#         if not (self.active and self.running):
#             return
#         self.continueTrying = True
#         url = self.connection_starting()
#         self.es = EventSource(url, self.handler, self.resetDelay)
#         d = self.es.start()
#         d.addBoth(self._stopped)
# 
#     def _stopped(self, res):
#         self.es = None
#         # we might have stopped because of a connection error, or because of
#         # an intentional shutdown.
#         if self.active and self.running:
#             # we still want to be connected, so schedule a reconnection
#             if isinstance(res, failure.Failure):
#                 log.err(res)
#             self.retry() # will eventually call _maybeStart
#             return
#         # intentional shutdown
#         self.stopTrying()
#         for d in self.when_stopped:
#             eventually(d.callback, None)
#         self.when_stopped = []
# 
#     def _stop_eventsource(self):
#         if self.es:
#             eventually(self.es.cancel)
# 
#     def _maybeStop(self, _=None):
#         self.stopTrying() # cancels timer, calls _stop_eventsource()
#         if not self.es:
#             return defer.succeed(None)
#         d = defer.Deferred()
#         self.when_stopped.append(d)
#         return d


import time, struct
from base64 import urlsafe_b64encode, b64decode
from twisted.application import service
from twisted.web import client
from twisted.python import log
from nacl.secret import SecretBox
from nacl.public import PrivateKey, PublicKey, Box
from ..errors import ReplayError
from ..eventual import fireEventually
from ..util import equal, remove_prefix
from ..eventsource import ReconnectingEventSource
    m = struct.pack(">Q8s", now, RT)
    boxed0 = Box(tmppriv, PublicKey(serverpubkey)).encrypt(m, nonce)
    assert boxed0[:24] == nonce
    boxed = boxed0[24:] # we elide the nonce, always 0
    req = tmppub + boxed
    return req, tmppub

class NotListResponseError(Exception):
    pass
class WrongPubkeyError(Exception):
    pass
class NotFetchResponseError(Exception):
    pass
class WrongFetchTokenError(Exception):
    pass

def decrypt_list_entry(boxed, symkey, tmppub):
    sbox = SecretBox(symkey)
    msg = remove_prefix(sbox.decrypt(boxed),
                        "list:", NotListResponseError)
    (got_tmppub, fetch_token, delete_token,
     length) = struct.unpack(">32s32s32sQ", msg)
    if not equal(got_tmppub, tmppub):
        raise WrongPubkeyError
    return fetch_token, delete_token, length

def decrypt_fetch_response(symkey, fetch_token, boxed):
    msg = remove_prefix(SecretBox(symkey).decrypt(boxed),
                        "fetch:", NotFetchResponseError)
    got_fetch_token = msg[:32]

# Path: src/petmail/errors.py
# class ReplayError(Exception):
#     pass
# 
# Path: src/petmail/eventual.py
# def fireEventually(value=None):
#     """This returns a Deferred which will fire in a later reactor turn, after
#     the current call stack has been completed, and after all other deferreds
#     previously scheduled with callEventually().
#     """
#     d = defer.Deferred()
#     eventually(d.callback, value)
#     return d
# 
# Path: src/petmail/util.py
# def equal(a, b):
#     # not vulnerable to timing attack
#     return hashlib.sha256(a).digest() == hashlib.sha256(b).digest()
# 
# def remove_prefix(s_bytes, prefix, errortype=None):
#     if not s_bytes.startswith(prefix):
#         errortype = errortype or BadPrefixError
#         raise errortype("did not see expected '%s' prefix" % (prefix,))
#     return s_bytes[len(prefix):]
# 
# Path: src/petmail/eventsource.py
# class ReconnectingEventSource(service.MultiService,
#                               protocol.ReconnectingClientFactory):
#     def __init__(self, baseurl, connection_starting, handler):
#         service.MultiService.__init__(self)
#         # we don't use any of the basic Factory/ClientFactory methods of
#         # this, just the ReconnectingClientFactory.retry, stopTrying, and
#         # resetDelay methods.
# 
#         self.baseurl = baseurl
#         self.connection_starting = connection_starting
#         self.handler = handler
#         # IService provides self.running, toggled by {start,stop}Service.
#         # self.active is toggled by {,de}activate. If both .running and
#         # .active are True, then we want to have an outstanding EventSource
#         # and will start one if necessary. If either is False, then we don't
#         # want one to be outstanding, and will initiate shutdown.
#         self.active = False
#         self.connector = Connector(self)
#         self.es = None # set we have an outstanding EventSource
#         self.when_stopped = [] # list of Deferreds
# 
#     def isStopped(self):
#         return not self.es
# 
#     def startService(self):
#         service.MultiService.startService(self) # sets self.running
#         self._maybeStart()
# 
#     def stopService(self):
#         # clears self.running
#         d = defer.maybeDeferred(service.MultiService.stopService, self)
#         d.addCallback(self._maybeStop)
#         return d
# 
#     def activate(self):
#         assert not self.active
#         self.active = True
#         self._maybeStart()
# 
#     def deactivate(self):
#         assert self.active # XXX
#         self.active = False
#         return self._maybeStop()
# 
#     def _maybeStart(self):
#         if not (self.active and self.running):
#             return
#         self.continueTrying = True
#         url = self.connection_starting()
#         self.es = EventSource(url, self.handler, self.resetDelay)
#         d = self.es.start()
#         d.addBoth(self._stopped)
# 
#     def _stopped(self, res):
#         self.es = None
#         # we might have stopped because of a connection error, or because of
#         # an intentional shutdown.
#         if self.active and self.running:
#             # we still want to be connected, so schedule a reconnection
#             if isinstance(res, failure.Failure):
#                 log.err(res)
#             self.retry() # will eventually call _maybeStart
#             return
#         # intentional shutdown
#         self.stopTrying()
#         for d in self.when_stopped:
#             eventually(d.callback, None)
#         self.when_stopped = []
# 
#     def _stop_eventsource(self):
#         if self.es:
#             eventually(self.es.cancel)
# 
#     def _maybeStop(self, _=None):
#         self.stopTrying() # cancels timer, calls _stop_eventsource()
#         if not self.es:
#             return defer.succeed(None)
#         d = defer.Deferred()
#         self.when_stopped.append(d)
#         return d


# Path: src/petmail/mailbox/retrieval.py
import time, struct
from base64 import urlsafe_b64encode, b64decode
from twisted.application import service
from twisted.web import client
from twisted.python import log
from nacl.secret import SecretBox
from nacl.public import PrivateKey, PublicKey, Box
from ..errors import ReplayError
from ..eventual import fireEventually
from ..util import equal, remove_prefix
from ..eventsource import ReconnectingEventSource

    m = struct.pack(">Q8s", now, RT)
    boxed0 = Box(tmppriv, PublicKey(serverpubkey)).encrypt(m, nonce)
    assert boxed0[:24] == nonce
    boxed = boxed0[24:] # we elide the nonce, always 0
    req = tmppub + boxed
    return req, tmppub

class NotListResponseError(Exception):
    pass
class WrongPubkeyError(Exception):
    pass
class NotFetchResponseError(Exception):
    pass
class WrongFetchTokenError(Exception):
    pass

def decrypt_list_entry(boxed, symkey, tmppub):
    sbox = SecretBox(symkey)
    msg = remove_prefix(sbox.decrypt(boxed),
                        "list:", NotListResponseError)
    (got_tmppub, fetch_token, delete_token,
     length) = struct.unpack(">32s32s32sQ", msg)
    if not equal(got_tmppub, tmppub):
        raise WrongPubkeyError
    return fetch_token, delete_token, length

def decrypt_fetch_response(symkey, fetch_token, boxed):
    msg = remove_prefix(SecretBox(symkey).decrypt(boxed),
                        "fetch:", NotFetchResponseError)
    got_fetch_token = msg[:32]

    if not equal(got_fetch_token, fetch_token):