SMTPClient.Mod 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. MODULE SMTPClient;
  2. (** AUTHOR "TF"; PURPOSE "SMTP client for sending mail"; *)
  3. (* SMTP RFC 821 client *)
  4. IMPORT
  5. Mail, IP, DNS, TCP, Streams, KernelLog;
  6. CONST
  7. Trace = FALSE;
  8. MaxRecipients* = 20;
  9. Ok* = 0;
  10. NotConnected* = 1;
  11. SendFailed* = 101;
  12. TooManyRecipients* = 5001;
  13. TYPE
  14. SMTPSession* = OBJECT(Mail.Sender)
  15. VAR
  16. connection : TCP.Connection;
  17. sendReady, open : BOOLEAN;
  18. r : Streams.Reader;
  19. w* : Streams.Writer;
  20. PROCEDURE &Init*;
  21. BEGIN sendReady := FALSE; open := FALSE
  22. END Init;
  23. PROCEDURE GetSendReady*():BOOLEAN;
  24. BEGIN RETURN sendReady
  25. END GetSendReady;
  26. PROCEDURE GetReplyCode*(VAR code: LONGINT; VAR res: WORD);
  27. VAR msg : ARRAY 256 OF CHAR;
  28. BEGIN
  29. r.Ln(msg);
  30. code := ORD(msg[0]) - ORD("0"); code := code * 10 + ORD(msg[1]) - ORD("0"); code := code * 10 + ORD(msg[2]) - ORD("0");
  31. IF Trace THEN KernelLog.Enter; KernelLog.String(msg); KernelLog.Exit END;
  32. WHILE (msg[3] = "-") & (r.res = Streams.Ok) DO
  33. r.Ln(msg);
  34. IF Trace THEN KernelLog.Enter; KernelLog.String(msg); KernelLog.Exit END
  35. END;
  36. IF r.res = Streams.Ok THEN res := Ok ELSE res := r.res END
  37. END GetReplyCode;
  38. PROCEDURE SendCommand*(CONST cmd, arg : ARRAY OF CHAR; VAR res:WORD);
  39. BEGIN
  40. IF Trace THEN
  41. KernelLog.Enter; KernelLog.String("CMD:"); KernelLog.String(cmd); KernelLog.String(" "); KernelLog.String(arg); KernelLog.Exit;
  42. END;
  43. w.String(cmd); w.String(" "); w.String(arg); w.Ln; w.Update;
  44. IF w.res = Streams.Ok THEN res := Ok ELSE res := w.res END
  45. END SendCommand;
  46. PROCEDURE Open*(CONST server, thisHost : ARRAY OF CHAR; port: LONGINT; VAR result : LONGINT);
  47. VAR fip : IP.Adr;
  48. res: WORD; reply : LONGINT;
  49. BEGIN
  50. result := NotConnected;
  51. DNS.HostByName(server, fip, res);
  52. IF res = DNS.Ok THEN
  53. NEW(connection);
  54. connection.Open(TCP.NilPort, fip, port, res);
  55. IF res = TCP.Ok THEN
  56. open := TRUE;
  57. Streams.OpenReader(r, connection.Receive);
  58. Streams.OpenWriter(w, connection.Send);
  59. GetReplyCode(reply, res);
  60. IF (res = Streams.Ok) & (reply >= 200) & (reply < 300) THEN
  61. SendCommand("HELO", thisHost, res);
  62. IF res = Streams.Ok THEN
  63. GetReplyCode(reply, res);
  64. IF (res = Streams.Ok) & (reply >= 200) & (reply < 300) THEN
  65. sendReady := TRUE;
  66. result := Ok
  67. END
  68. END
  69. ELSE
  70. Close
  71. END
  72. END
  73. END
  74. END Open;
  75. PROCEDURE Close*;
  76. VAR res : WORD;
  77. BEGIN
  78. IF open THEN
  79. sendReady := FALSE; open := FALSE;
  80. SendCommand("QUIT", "", res);
  81. connection.Close
  82. END
  83. END Close;
  84. PROCEDURE StartMailFrom*(CONST fromAddr : ARRAY OF CHAR) : BOOLEAN;
  85. VAR reply: LONGINT; res: WORD;
  86. BEGIN
  87. w.String("MAIL FROM:<"); w.String(fromAddr); w.String(">"); w.Ln; w.Update;
  88. IF w.res = Streams.Ok THEN
  89. GetReplyCode(reply, res);
  90. RETURN (res = Ok) & (reply = 250)
  91. ELSE RETURN FALSE
  92. END;
  93. END StartMailFrom;
  94. PROCEDURE SendTo*(CONST toAddr : ARRAY OF CHAR) :BOOLEAN;
  95. VAR reply: LONGINT; res: WORD;
  96. BEGIN
  97. w.String("RCPT TO:<"); w.String(toAddr); w.String(">"); w.Ln; w.Update;
  98. IF w.res = Streams.Ok THEN
  99. GetReplyCode(reply, res);
  100. RETURN (res = Ok) & (reply = 250)
  101. ELSE RETURN FALSE
  102. END;
  103. END SendTo;
  104. PROCEDURE StartData*() : BOOLEAN;
  105. VAR reply: LONGINT; res: WORD;
  106. BEGIN
  107. SendCommand("DATA", "", res);
  108. IF res = Ok THEN
  109. GetReplyCode(reply, res);
  110. RETURN ((res = Ok) & (reply = 354))
  111. ELSE RETURN FALSE
  112. END
  113. END StartData;
  114. PROCEDURE PrepareToSend*(m: Mail.Message; VAR result : LONGINT);
  115. VAR name, address : Mail.MailAddress; i: LONGINT;
  116. BEGIN
  117. result := SendFailed;
  118. ASSERT(m # NIL);
  119. (* FROM *)
  120. m.GetFrom(name, address);
  121. (* TO *)
  122. IF StartMailFrom(address) THEN
  123. FOR i := 0 TO m.GetNofTo() - 1 DO
  124. m.GetTo(i, name, address); IF ~SendTo(address) THEN Close; RETURN END
  125. END;
  126. FOR i := 0 TO m.GetNofCc() - 1 DO
  127. m.GetCc(i, name, address); IF ~SendTo(address) THEN Close; RETURN END
  128. END;
  129. FOR i := 0 TO m.GetNofBcc() - 1 DO
  130. m.GetBcc(i, name, address); IF ~SendTo(address) THEN Close; RETURN END
  131. END;
  132. ELSE Close; RETURN
  133. END;
  134. (* DATA *)
  135. IF StartData() THEN result := Ok ELSE Close END;
  136. END PrepareToSend;
  137. PROCEDURE SendRawLine*(CONST s : ARRAY OF CHAR);
  138. BEGIN
  139. w.String(s); w.Ln
  140. END SendRawLine;
  141. PROCEDURE FinishSendRaw*() : BOOLEAN;
  142. VAR reply: LONGINT; res: WORD;
  143. BEGIN
  144. w.Update;
  145. GetReplyCode(reply, res);
  146. RETURN (res = Ok) & (reply = 250)
  147. END FinishSendRaw;
  148. PROCEDURE SendComplete*(m: Mail.Message; VAR result : LONGINT);
  149. VAR i: LONGINT;
  150. name, address : Mail.MailAddress;
  151. date, id : ARRAY 64 OF CHAR;
  152. subject, content : ARRAY 256 OF CHAR;
  153. l : Mail.Line;
  154. BEGIN {EXCLUSIVE}
  155. PrepareToSend(m, result);
  156. IF result = 0 THEN
  157. m.GetDate(date);
  158. IF date # "" THEN w.String("Date : "); w.String(date); w.Ln END;
  159. m.GetSubject(subject);
  160. IF subject # "" THEN w.String("Subject : "); w.String(subject); w.Ln END;
  161. m.GetFrom(name, address);
  162. w.String("From:");
  163. IF name # "" THEN
  164. w.String(name); w.String(" <");
  165. w.String(address); w.String(">");
  166. ELSE
  167. w.String(address);
  168. END;
  169. w.Ln;
  170. m.GetSender(name, address);
  171. IF address # "" THEN
  172. w.String("Sender:");
  173. IF name # "" THEN
  174. w.String(name); w.String(" <");
  175. w.String(address); w.String(">");
  176. ELSE
  177. w.String(address);
  178. END;
  179. w.Ln
  180. END;
  181. IF m.GetNofReplyTo() > 0 THEN
  182. w.String("Reply-To:");
  183. FOR i := 0 TO m.GetNofReplyTo() - 1 DO
  184. m.GetReplyTo(i, name, address);
  185. IF name # "" THEN
  186. w.String(name); w.String(" <");
  187. w.String(address); w.String(">");
  188. ELSE
  189. w.String(address);
  190. END;
  191. IF i < m.GetNofReplyTo() - 1 THEN w.String(",") END;
  192. w.Ln;
  193. END
  194. END;
  195. w.String("To:");
  196. FOR i := 0 TO m.GetNofTo() - 1 DO
  197. m.GetTo(i, name, address);
  198. w.Char(" ");
  199. IF name # "" THEN
  200. w.String(name); w.String(" <");
  201. w.String(address); w.String(">");
  202. ELSE
  203. w.String(address);
  204. END;
  205. IF i < m.GetNofTo() - 1 THEN w.String(",") END;
  206. w.Ln;
  207. END;
  208. IF m.GetNofCc() > 0 THEN
  209. w.String("Cc:");
  210. FOR i := 0 TO m.GetNofCc() - 1 DO
  211. m.GetCc(i, name, address);
  212. w.Char(" ");
  213. IF name # "" THEN
  214. w.String(name); w.String(" <");
  215. w.String(address); w.String(">");
  216. ELSE
  217. w.String(address);
  218. END;
  219. IF i < m.GetNofCc() - 1 THEN w.String(",") END;
  220. w.Ln;
  221. END
  222. END;
  223. IF m.GetNofBcc() > 0 THEN
  224. w.String("Bcc:");
  225. FOR i := 0 TO m.GetNofBcc() - 1 DO
  226. m.GetBcc(i, name, address);
  227. w.Char(" ");
  228. IF name # "" THEN
  229. w.String(name); w.String(" <");
  230. w.String(address); w.String(">");
  231. ELSE
  232. w.String(address);
  233. END;
  234. IF i < m.GetNofBcc() - 1 THEN w.String(",") END;
  235. w.Ln;
  236. END;
  237. END;
  238. IF m.GetNofHeaders() > 0 THEN
  239. FOR i := 0 TO m.GetNofHeaders() - 1 DO
  240. m.GetHeader(i, id, content);
  241. w.String(id); w.String(" : "); w.String(content); w.Ln;
  242. END;
  243. END;
  244. w.Ln;
  245. FOR i := 0 TO m.GetNofLines() - 1 DO
  246. m.GetLine(i, l);
  247. IF l.data # NIL THEN w.String(l.data^) END; w.Ln;
  248. END;
  249. w.Ln; w.String("."); w.Ln;
  250. IF FinishSendRaw() THEN result := Ok END
  251. END;
  252. END SendComplete;
  253. END SMTPSession;
  254. END SMTPClient.