1 // src/SmtpClient.cc
2 // This file is part of libpbe; see http://decimail.org
3 // (C) 2004 Philip Endecott
4 
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2 of the License, or
8 // any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 #include "SmtpClient.hh"
19 
20 #include "ip.hh"
21 #include "select.hh"
22 
23 #include <syslog.h>
24 #include <unistd.h>
25 #include <stdio.h>
26 
27 #ifdef __OpenBSD__
28 // Is this really needed?
29 #include <sys/types.h>
30 #endif
31 
32 using namespace std;
33 using namespace pbe;
34 
35 
report(ostream & s) const36   void SmtpError::report(ostream& s) const
37   {
38     s << msg;
39   }
40 
41 
SmtpClient(bool log)42   SmtpClient::SmtpClient(bool log): enable_log(log), connected(false)
43   {}
44 
45 
connect(string server_name,string domain,int port)46   void SmtpClient::connect(string server_name, string domain, int port)
47   {
48     fd = tcp_client_connect(server_name, port);
49 
50     wait_for_reply(220,300);
51     send("EHLO "+domain);
52     wait_for_reply(250,300);
53 
54     connected=true;
55   }
56 
57 
wait_for_reply(int expected_code,int timeout)58   void SmtpClient::wait_for_reply(int expected_code, int timeout)
59   {
60     if (select_rt(fd,timeout)==-1) {
61       throw SmtpError("Timeout");
62     }
63     char buf[513];
64     int n=0;
65     while (1) {
66       int c = read(fd,buf+n,sizeof(buf)-n);
67       buf[n+c+1]='\0';
68       if (enable_log) {
69 	syslog(LOG_MAIL|LOG_DEBUG,"SmtpClient:S: %s",buf);
70       }
71       if (c==-1) {
72 	throw_ErrnoException("read()");
73       }
74       n += c;
75       if (buf[n-1]=='\n') {
76 	break;
77       }
78       if (n==sizeof(buf)) {
79 	throw SmtpError("Command line did not terminate");
80       }
81     }
82     int code;
83     int rc = sscanf(buf,"%d",&code);
84     if (rc!=1) {
85       throw SmtpError("No reply code at start of line");
86     }
87     if (code != expected_code) {
88       throw SmtpError("Unexpected reply: \'"+string(buf)+"\'");
89     }
90   }
91 
92 
send(string d)93   void SmtpClient::send(string d)
94   {
95     if (enable_log) {
96       syslog(LOG_MAIL|LOG_DEBUG,"SmtpClient:C: %s",d.c_str());
97     }
98     d.append("\r\n");
99     // ought to impose a timeout on these writes
100     const char* p = d.data();
101     int n = d.length();
102     int c = 0;
103     while(c<n) {
104       int rc = write(fd,p+c,n-c);
105       if (rc==-1) {
106 	throw_ErrnoException("write()");
107       }
108       c += rc;
109     }
110   }
111 
112 
send_msg(string sender,string recipient,string msg)113   void SmtpClient::send_msg(string sender, string recipient, string msg)
114   {
115     list<string> recipients;
116     recipients.push_back(recipient);
117     send_msg(sender, recipients, msg);
118   }
119 
120 
send_msg(string sender,const list<string> & recipients,string msg)121   void SmtpClient::send_msg(string sender, const list<string>& recipients,
122 			    string msg)
123   {
124     send("MAIL FROM:<"+sender+">");
125     wait_for_reply(250,300);
126     for (list<string>::const_iterator i = recipients.begin();
127 	 i != recipients.end(); ++i) {
128       send("RCPT TO:<"+*i+">");
129       wait_for_reply(250,300); // could get 251 as well
130     }
131     send("DATA");
132     wait_for_reply(354,120);
133     send(msg);
134     send(".");
135     wait_for_reply(250,600);
136   }
137 
138 
disconnect(void)139   void SmtpClient::disconnect(void)
140   {
141     send("QUIT");
142     wait_for_reply(221,300);
143     int rc = close(fd);
144     if (rc==-1) {
145       throw_ErrnoException("close()");
146     }
147     connected=false;
148   }
149