Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

relay.py 5.1KB

1 year ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. # -*- test-case-name: twisted.mail.test.test_mail -*-
  2. # Copyright (c) Twisted Matrix Laboratories.
  3. # See LICENSE for details.
  4. """
  5. Support for relaying mail.
  6. """
  7. import os
  8. import pickle
  9. from twisted.internet.address import UNIXAddress
  10. from twisted.mail import smtp
  11. from twisted.python import log
  12. class DomainQueuer:
  13. """
  14. An SMTP domain which add messages to a queue intended for relaying.
  15. """
  16. def __init__(self, service, authenticated=False):
  17. self.service = service
  18. self.authed = authenticated
  19. def exists(self, user):
  20. """
  21. Check whether mail can be relayed to a user.
  22. @type user: L{User}
  23. @param user: A user.
  24. @rtype: no-argument callable which returns L{IMessage <smtp.IMessage>}
  25. provider
  26. @return: A function which takes no arguments and returns a message
  27. receiver for the user.
  28. @raise SMTPBadRcpt: When mail cannot be relayed to the user.
  29. """
  30. if self.willRelay(user.dest, user.protocol):
  31. # The most cursor form of verification of the addresses
  32. orig = filter(None, str(user.orig).split("@", 1))
  33. dest = filter(None, str(user.dest).split("@", 1))
  34. if len(orig) == 2 and len(dest) == 2:
  35. return lambda: self.startMessage(user)
  36. raise smtp.SMTPBadRcpt(user)
  37. def willRelay(self, address, protocol):
  38. """
  39. Check whether we agree to relay.
  40. The default is to relay for all connections over UNIX
  41. sockets and all connections from localhost.
  42. """
  43. peer = protocol.transport.getPeer()
  44. return self.authed or isinstance(peer, UNIXAddress) or peer.host == "127.0.0.1"
  45. def startMessage(self, user):
  46. """
  47. Create an envelope and a message receiver for the relay queue.
  48. @type user: L{User}
  49. @param user: A user.
  50. @rtype: L{IMessage <smtp.IMessage>}
  51. @return: A message receiver.
  52. """
  53. queue = self.service.queue
  54. envelopeFile, smtpMessage = queue.createNewMessage()
  55. with envelopeFile:
  56. log.msg(f"Queueing mail {str(user.orig)!r} -> {str(user.dest)!r}")
  57. pickle.dump([str(user.orig), str(user.dest)], envelopeFile)
  58. return smtpMessage
  59. class RelayerMixin:
  60. # XXX - This is -totally- bogus
  61. # It opens about a -hundred- -billion- files
  62. # and -leaves- them open!
  63. def loadMessages(self, messagePaths):
  64. self.messages = []
  65. self.names = []
  66. for message in messagePaths:
  67. with open(message + "-H", "rb") as fp:
  68. messageContents = pickle.load(fp)
  69. fp = open(message + "-D")
  70. messageContents.append(fp)
  71. self.messages.append(messageContents)
  72. self.names.append(message)
  73. def getMailFrom(self):
  74. if not self.messages:
  75. return None
  76. return self.messages[0][0]
  77. def getMailTo(self):
  78. if not self.messages:
  79. return None
  80. return [self.messages[0][1]]
  81. def getMailData(self):
  82. if not self.messages:
  83. return None
  84. return self.messages[0][2]
  85. def sentMail(self, code, resp, numOk, addresses, log):
  86. """Since we only use one recipient per envelope, this
  87. will be called with 0 or 1 addresses. We probably want
  88. to do something with the error message if we failed.
  89. """
  90. if code in smtp.SUCCESS:
  91. # At least one, i.e. all, recipients successfully delivered
  92. os.remove(self.names[0] + "-D")
  93. os.remove(self.names[0] + "-H")
  94. del self.messages[0]
  95. del self.names[0]
  96. class SMTPRelayer(RelayerMixin, smtp.SMTPClient):
  97. """
  98. A base class for SMTP relayers.
  99. """
  100. def __init__(self, messagePaths, *args, **kw):
  101. """
  102. @type messagePaths: L{list} of L{bytes}
  103. @param messagePaths: The base filename for each message to be relayed.
  104. @type args: 1-L{tuple} of (0) L{bytes} or 2-L{tuple} of
  105. (0) L{bytes}, (1) L{int}
  106. @param args: Positional arguments for L{SMTPClient.__init__}
  107. @type kw: L{dict}
  108. @param kw: Keyword arguments for L{SMTPClient.__init__}
  109. """
  110. smtp.SMTPClient.__init__(self, *args, **kw)
  111. self.loadMessages(messagePaths)
  112. class ESMTPRelayer(RelayerMixin, smtp.ESMTPClient):
  113. """
  114. A base class for ESMTP relayers.
  115. """
  116. def __init__(self, messagePaths, *args, **kw):
  117. """
  118. @type messagePaths: L{list} of L{bytes}
  119. @param messagePaths: The base filename for each message to be relayed.
  120. @type args: 3-L{tuple} of (0) L{bytes}, (1) L{None} or
  121. L{ClientContextFactory
  122. <twisted.internet.ssl.ClientContextFactory>},
  123. (2) L{bytes} or 4-L{tuple} of (0) L{bytes}, (1) L{None}
  124. or L{ClientContextFactory
  125. <twisted.internet.ssl.ClientContextFactory>}, (2) L{bytes},
  126. (3) L{int}
  127. @param args: Positional arguments for L{ESMTPClient.__init__}
  128. @type kw: L{dict}
  129. @param kw: Keyword arguments for L{ESMTPClient.__init__}
  130. """
  131. smtp.ESMTPClient.__init__(self, *args, **kw)
  132. self.loadMessages(messagePaths)