Advertisement
Guest User

Untitled

a guest
Jun 23rd, 2017
509
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 4.51 KB | None | 0 0
  1. (require 'sb-bsd-sockets)
  2.  
  3. (defpackage :repsmtpd
  4. (:use :cl :sb-bsd-sockets))
  5.  
  6. (in-package :repsmtpd)
  7.  
  8. (defconstant +listen-port+ 12345)
  9. (defconstant +listen-addr+ "0.0.0.0")
  10. (defconstant +server-name+ "repsmtpd.org")
  11. (defconstant +mbox-dir+ "lisp/mbox/")
  12.  
  13. (defstruct conn state stream from to (data ""))
  14.  
  15. (defvar *server-socket* nil)
  16. (defvar *command-handlers* nil)
  17.  
  18. (defun split-string (s c)
  19. (loop for i = 0 then (1+ j) as j = (position c s :start i)
  20. collect (subseq s i j) while j))
  21.  
  22. (defun concat (l)
  23. (apply 'concatenate 'string l))
  24.  
  25. (defun intersperse (x l)
  26. (cdr (loop for i in l collect x collect l)))
  27.  
  28. (defun get-command (s)
  29. (string-upcase (car (remove-if (lambda (s) (string= "" s))
  30. (split-string s #\space)))))
  31.  
  32. (defun strip-crlf (s)
  33. (if (eql s :eof)
  34. :eof
  35. (string-right-trim '(#\return #\linefeed) s)))
  36.  
  37. (defun say (conn code msg)
  38. (let ((s (conn-stream conn)))
  39. (format s "~a ~a~a~a" (write-to-string code) msg #\return
  40. #\linefeed)
  41. (format *standard-output* "sending ~a ~a~&"
  42. (write-to-string code) msg)
  43. (finish-output *standard-output*)
  44. (finish-output s)))
  45.  
  46. (defun process-ehlo (c args)
  47. (declare (ignore args))
  48. (say c 250 (format nil "~a lol hello" +server-name+))
  49. (setf (conn-state c) 'got-helo))
  50.  
  51. (defun process-quit (c args)
  52. (declare (ignore args))
  53. (say c 221 "bye!")
  54. (close (conn-stream c)))
  55.  
  56. (defun process-mail (c args)
  57. (if (not (eql (conn-state c) 'got-helo))
  58. (say c 503 "bad command sequence")
  59. (cond
  60. ((not (search "MAIL FROM:" args :end2 12
  61. :test 'char-equal))
  62. (say c 500 "unknown command"))
  63. (t (let ((from (string-left-trim " " (concat
  64. (cdr (split-string args #\:)))))) ;XXX intersperse ;
  65. (setf (conn-from c) from)
  66. (setf (conn-state c) 'got-mail)
  67. (say c 250 "OK"))))))
  68.  
  69. (defun process-rcpt (c args)
  70. (if (not (or (eql (conn-state c) 'got-mail)
  71. (eql (conn-state c) 'got-rcpt)))
  72. (say c 503 "bad command sequence")
  73. (cond
  74. ((not (search "RCPT TO:" args :end2 10
  75. :test 'char-equal))
  76. (say c 500 "unknown command"))
  77. (t (let ((to (string-left-trim " " (concat
  78. (cdr (split-string args #\:)))))) ;XXX intersperse ;
  79. (setf (conn-to c) (cons to (conn-to c)))
  80. (setf (conn-state c) 'got-rcpt)
  81. (say c 250 "OK"))))))
  82.  
  83. (defun process-data (c args)
  84. (declare (ignore args))
  85. (if (not (eql (conn-state c) 'got-rcpt))
  86. (say c 503 "bad command sequence")
  87. (progn (say c 354 "go ahead")
  88. (setf (conn-state c) 'wait-data))))
  89.  
  90. (defun register-handlers ()
  91. (setf *command-handlers* '(("EHLO" . process-ehlo) ; XXX
  92. ("HELO" . process-ehlo)
  93. ("MAIL" . process-mail)
  94. ("RCPT" . process-rcpt)
  95. ("DATA" . process-data)
  96. ("QUIT" . process-quit))))
  97.  
  98. (defun deliver-one (c to)
  99. (let ((path (concat (list +mbox-dir+ to))))
  100. (with-open-file (f path :direction :output :if-exists :append
  101. :if-does-not-exist :create)
  102. (format f "~&~%From blah@blah.com Sat Jan 3 01:05:34 1996~&")
  103. (format f "~a" (conn-data c)))))
  104.  
  105. (defun deliver (c)
  106. (pprint c)
  107. (finish-output *standard-output*)
  108. (mapcar (lambda (to) (deliver-one c to)) (conn-to c)))
  109.  
  110. (defun get-data (c l)
  111. (if (string= l ".")
  112. (progn
  113. (say c 250 "OK")
  114. (setf (conn-state c) 'got-data)
  115. (deliver c))
  116. (setf (conn-data c) (format nil "~a~a~a" (conn-data c)
  117. #\linefeed l))))
  118.  
  119. (defun process-command (conn l)
  120. (let* ((command (get-command l))
  121. (handler (assoc command *command-handlers* :test 'string=)))
  122. (format *standard-output* "got ~s~&" l)
  123. (finish-output *standard-output*)
  124. (if (eql handler nil)
  125. (say conn 500 "unknown command")
  126. (apply (cdr handler) conn (list l)))))
  127.  
  128. (defun process-connection (s)
  129. (let ((conn (make-conn :state 'greeting :stream s)))
  130. (say conn 220 "lol what")
  131. (loop while (open-stream-p s)
  132. for l = (strip-crlf (read-line s nil :eof))
  133. while (not (eql l :eof))
  134. do (case (conn-state conn)
  135. ('wait-data (get-data conn l))
  136. (otherwise (process-command conn l))))))
  137.  
  138. (defun start-server ()
  139. (setf *server-socket* (make-instance 'inet-socket :type :stream))
  140. (setf (sockopt-reuse-address *server-socket*) t)
  141. (socket-bind *server-socket* (make-inet-address +listen-addr+)
  142. +listen-port+)
  143. (socket-listen *server-socket* 0))
  144.  
  145. (start-server)
  146. (register-handlers)
  147. (loop for socket = (socket-accept *server-socket*)
  148. for stream = (socket-make-stream socket :input t :output t
  149. :element-type 'character :external-format :latin-1)
  150. do
  151. (process-connection stream)
  152. (socket-close socket))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement