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
  
class protocol 
{ 
  // Maybe this should be the other way around? 
  inherit .NNTP.protocol; 
} 
 
class client 
{ 
  inherit protocol; 
 
  constant reply_codes = 
  ([ 211:"System status, or system help reply", 
     214:"Help message", 
     220:"<host> Service ready", 
     221:"<host> Service closing transmission channel", 
     250:"Requested mail action okay, completed", 
     251:"User not local; will forward to <forward-path>", 
     354:"Start mail input; end with <CRLF>.<CRLF>", 
     421:"<host> Service not available, closing transmission channel " 
         "[This may be a reply to any command if the service knows it " 
         "must shut down]", 
     450:"Requested mail action not taken: mailbox unavailable " 
         "[E.g., mailbox busy]", 
     451:"Requested action aborted: local error in processing", 
     452:"Requested action not taken: insufficient system storage", 
     500:"Syntax error, command unrecognized " 
         "[This may include errors such as command line too long]", 
     501:"Syntax error in parameters or arguments", 
     502:"Command not implemented", 
     503:"Bad sequence of commands", 
     504:"Command parameter not implemented", 
     550:"Requested action not taken: mailbox unavailable " 
         "[E.g., mailbox not found, no access]", 
     551:"User not local; please try <forward-path>", 
     552:"Requested mail action aborted: exceeded storage allocation", 
     553:"Requested action not taken: mailbox name not allowed " 
         "[E.g., mailbox syntax incorrect]", 
     554:"Transaction failed" ]); 
 
  static private int cmd(string c, string|void comment) 
  { 
    int r = command(c); 
    switch(r) { 
    case 200..399: 
      break; 
    default: 
      throw(({"SMTP: "+c+"\n"+(comment?"SMTP: "+comment+"\n":"")+ 
              "SMTP: "+reply_codes[r]+"\n", backtrace()})); 
    } 
    return r; 
  } 
 
  void create(void|string server, int|void port) 
  { 
    if(!server) 
    { 
      // Lookup MX record here (Using DNS.pmod) 
      object dns=master()->resolv("Protocols")["DNS"]->client(); 
      server=dns->get_primary_mx(gethostname()); 
    } 
 
    if(!port) 
      port = 25; 
 
    if(!connect(server, port)) 
    { 
      throw(({"Failed to connect to mail server.\n",backtrace()})); 
    } 
 
    if(readreturncode()/100 != 2) 
      throw(({"Connection refused by SMTP server.\n",backtrace()})); 
 
    if(catch(cmd("EHLO "+gethostname()))) 
      cmd("HELO "+gethostname(), "greeting failed."); 
  } 
   
  void send_message(string from, string *to, string body) 
  { 
    cmd("MAIL FROM: "+from); 
    foreach(to, string t) 
      cmd("RCPT TO: "+t); 
    cmd("DATA"); 
    cmd(body+"\r\n."); 
    cmd("QUIT"); 
  } 
 
  void simple_mail(string to, string subject, string from, string msg) 
  { 
    send_message(from, ({ to }), 
                 (string)MIME.Message(msg, (["mime-version":"1.0", 
                                             "subject":subject, 
                                             "from":from, 
                                             "to":to, 
                                             "content-type": 
                                               "text/plain;charset=iso-8859-1", 
                                             "content-transfer-encoding": 
                                               "8bit"]))); 
  } 
}