File: Mail.k

package info (click to toggle)
kaya 0.2.0-6
  • links: PTS
  • area: main
  • in suites: etch, etch-m68k
  • size: 3,012 kB
  • ctags: 1,307
  • sloc: cpp: 6,691; haskell: 4,833; sh: 2,868; yacc: 768; makefile: 700; perl: 87
file content (140 lines) | stat: -rw-r--r-- 3,399 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
/** -*-C-*-ish
    Kaya standard library
    Copyright (C) 2004, 2005 Edwin Brady

    This file is distributed under the terms of the GNU Lesser General
    Public Licence. See COPYING for licence.
*/

module Mail;

// Module for talking to an SMTP server
// Maybe add IMAP/POP3 support later?

import Prelude;
import Net;
import Strings;
import Regex;
import Time;

"SMTP Connection handle."
data SMTPConnection = SMTP(NetHandle h, String host);

Exception CantConnect = Exception("Can't Connect",150);
Exception SMTPError(String err) = Exception("SMTP Error:" + err,151);
Exception HeaderError(String err) = Exception("Invalid Header: " + err,152);

"Send a mail.
Sends from address <em>from</em>, to <em>whoto</em>.
Additional headers may be stored in <em>headers</em>.
Message contents <em>body</em> may not contain headers."
public Void sendmail(String from, String whoto, [(String,String)] headers, 
		     String body, String server="localhost", Int port=25) 

{
    // check data and construct message
    blacklist = compile("[\r\n]",[IgnoreCase,Multiline]);
    if (quickMatchWith(blacklist,from)) {
      throw(HeaderError("From is "+from));
    }
    if (quickMatchWith(blacklist,whoto)) {
      throw(HeaderError("To is "+whoto));
    }
    msg = "";
    for header in headers {
      if (quickMatchWith(blacklist,header.fst) || quickMatchWith(blacklist,header.snd)) {
	throw(HeaderError(header.fst+" is "+header.snd));
      }
      msg += header.fst+": "+header.snd+"\n";
    }
    msg += "From: "+from+"\n";
    msg += "To: "+whoto+"\n";
    msg += "Date: "+rfc2822Time(gmtime())+"\n\n"; // end headers
    msg += body;

    c = smtpConnect(server,port);
    smtpFrom(c,from);
    smtpRcpt(c,whoto);
    smtpData(c,msg);
    smtpClose(c);
}

"Create an SMTP connection"
public SMTPConnection smtpConnect(String host, Int port=25)
{
    h = connect(TCP,host,port);
    rcvok = recv(h,255,30);
    if ((words(rcvok)[0])!="220") {
	throw(CantConnect);
    }
    send(h,"HELO "+host+"\n");
    rcvok = recv(h,255,30);
    if ((words(rcvok)[0])!="250") {
	throw(CantConnect);
    }
    return SMTP(h,host);
}

Void smtpFrom(SMTPConnection c, String from)
{
    send(c.h,"MAIL FROM:"+from+"\n");
    rcvok = recv(c.h,255,30);
    if ((words(rcvok)[0])!="250") {
	throw(SMTPError(rcvok));
    }    
}

Void smtpRcpt(SMTPConnection c, String whoto)
{
  addresses = split(",",whoto);
  for address in addresses {
    trim(address);
    send(c.h,"RCPT TO:"+address+"\n");
    rcvok = recv(c.h,255,30);
    if ((words(rcvok)[0])!="250") {
	throw(SMTPError(rcvok));
    }    
  }
}

Void manglePeriod(var String stuff)
{
    slines = lines(stuff);
    newstuff = "";
    for x in slines {
	if (length(x) > 0 && head(x)=='.') {
	    x = "."+x;
	}
	newstuff += x +"\n";
    }
    stuff = newstuff;
}

// Send data. Returns message id.
Void smtpData(SMTPConnection c, String stuff)
{
    send(c.h,"DATA\n");
    rcvok = recv(c.h,255,30);
    if ((words(rcvok)[0])!="354") {
	throw(SMTPError(rcvok));
    }
    // Mangle stuff in case any lines start with a .
    manglePeriod(stuff);
    send(c.h,stuff+"\n");
    send(c.h,".\n");

    rcvok = recv(c.h,255,30);
    if ((words(rcvok)[0])!="250") {
	throw(SMTPError(rcvok));
    }    
}

Void smtpClose(SMTPConnection c)
{
    send(c.h,"QUIT\n");
    rcvok = recv(c.h,255,30);
    if ((words(rcvok)[0])!="221") {
	throw(SMTPError(rcvok));
    }
}