- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

gspamclient.cpp
Go to the documentation of this file.
1 //
2 // Copyright (C) 2001-2013 Graeme Walker <graeme_walker@users.sourceforge.net>
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 // ===
17 //
18 // gspamclient.cpp
19 //
20
21 #include "gdef.h"
22 #include "gnet.h"
23 #include "gsmtp.h"
24 #include "gstr.h"
25 #include "gfile.h"
26 #include "gspamclient.h"
27 #include "gassert.h"
28
29 GSmtp::SpamClient::SpamClient( const GNet::ResolverInfo & resolver_info ,
30 unsigned int connect_timeout , unsigned int response_timeout ) :
31 GNet::Client(resolver_info,connect_timeout,response_timeout,0U,"\n") ,
32 m_in_size(0UL) ,
33 m_in_lines(0UL) ,
34 m_out_size(0UL) ,
35 m_out_lines(0UL) ,
36 m_header_out_index(0U) ,
37 m_timer(*this,&SpamClient::onTimeout,*this)
38 {
39 G_DEBUG( "GSmtp::SpamClient::ctor: " << resolver_info.displayString() << ": "
40 << connect_timeout << " " << response_timeout ) ;
41 }
42
43 GSmtp::SpamClient::~SpamClient()
44 {
45 }
46
47 bool GSmtp::SpamClient::busy() const
48 {
49 return m_in.get() != NULL || m_out.get() != NULL ;
50 }
51
52 void GSmtp::SpamClient::onDelete( const std::string & , bool )
53 {
54 }
55
56 void GSmtp::SpamClient::onDeleteImp( const std::string & reason , bool b )
57 {
58 // we have to override onDeleteImp() rather than onDelete() so that we
59 // can get in early enough to guarantee that every request gets a response
60
61 if( !reason.empty() )
62 G_WARNING( "GSmtp::SpamClient::onDeleteImp: error: " << reason ) ;
63
64 if( busy() )
65 {
66 m_in <<= 0 ;
67 m_out <<= 0 ;
68 eventSignal().emit( "spam" , m_out_size >= headerBodyLength() ? headerResult() : reason ) ;
69 }
70 Base::onDeleteImp( reason , b ) ; // use typedef because of ms compiler bug
71 }
72
73 void GSmtp::SpamClient::request( const std::string & path )
74 {
75 G_DEBUG( "GSmtp::SpamClient::request: \"" << path << "\"" ) ;
76 if( busy() )
77 throw ProtocolError() ;
78
79 m_path = path ;
80 m_in <<= new std::ifstream( path.c_str() , std::ios_base::binary | std::ios_base::in ) ;
81 m_in_lines = 0UL ;
82 m_in_size = 0UL ;
83
84 std::string username = "spam" ; // TODO -- configurable username in SPAMC protocol? environment variable?
85 m_header_out.push_back( std::string() + "PROCESS SPAMC/1.4" ) ;
86 m_header_out.push_back( std::string() + "User: " + username ) ;
87 m_header_out.push_back( std::string() + "Content-length: " + G::File::sizeString(m_path) ) ;
88 m_header_out.push_back( std::string() ) ;
89 m_header_out_index = 0U ;
90
91 m_timer.startTimer( 0U ) ;
92 m_header_in.clear() ;
93 }
94
95 void GSmtp::SpamClient::onConnect()
96 {
97 G_DEBUG( "GSmtp::SpamClient::onConnect" ) ;
98 if( busy() )
99 sendContent() ;
100 }
101
102 void GSmtp::SpamClient::onTimeout()
103 {
104 if( connected() )
105 sendContent() ;
106 }
107
108 void GSmtp::SpamClient::onSendComplete()
109 {
110 sendContent() ;
111 }
112
113 void GSmtp::SpamClient::sendContent()
114 {
115 std::string line ;
116 while( nextContentLine(line) )
117 {
118 if( !send( line + "\r\n" ) )
119 break ;
120 }
121 }
122
123 bool GSmtp::SpamClient::nextContentLine( std::string & line )
124 {
125 bool ok = false ;
126 if( m_in.get() != NULL )
127 {
128 if( m_header_out_index < m_header_out.size() )
129 {
130 line = m_header_out[m_header_out_index++] ;
131 G_LOG( "GSmtp::SpamClient::sendContent: spam>>: \"" << G::Str::printable(line) << "\"" ) ;
132 ok = true ;
133 }
134 else
135 {
136 std::istream & stream = *(m_in.get()) ;
137 if( stream.good() )
138 {
139 G::Str::readLineFrom( stream , "\r\n" , line ) ;
140 ok = !! stream ;
141 }
142 if( ok )
143 {
144 m_in_lines++ ;
145 m_in_size += ( line.length() + 2U ) ;
146 }
147 else
148 {
149 G_LOG( "GSmtp::SpamClient::addBody: spam>>: [" << m_in_lines
150 << " lines of body text, " << m_in_size << " bytes]" ) ;
151
152 // stop sending, start receiving
153 turnRound() ;
154 }
155 }
156 }
157 return ok ;
158 }
159
160 void GSmtp::SpamClient::turnRound()
161 {
162 // send eof
163 socket().shutdown() ;
164
165 // close content file for reading, reopen for writing (keeping busy() true)
166 m_in <<= 0 ;
167 m_out <<= new std::ofstream( m_path.c_str() , std::ios_base::binary | std::ios_base::out | std::ios_base::trunc ) ;
168 m_out_size = 0UL ;
169 m_out_lines = 0UL ;
170 }
171
172 void GSmtp::SpamClient::onSecure( const std::string & )
173 {
174 }
175
176 bool GSmtp::SpamClient::onReceive( const std::string & line )
177 {
178 if( m_in.get() != NULL )
179 throw ProtocolError( G::Str::printable(line) ) ; // the spamd has sent a response too early
180
181 if( ! haveCompleteHeader() )
182 addHeader( line ) ;
183 else
184 addBody( line ) ;
185
186 return true ;
187 }
188
189 bool GSmtp::SpamClient::haveCompleteHeader() const
190 {
191 return m_header_in.size() != 0U && m_header_in.back().empty() ;
192 }
193
194 void GSmtp::SpamClient::addHeader( const std::string & line )
195 {
196 G_LOG( "GSmtp::SpamClient::onReceive: spam<<: \"" << G::Str::printable(G::Str::trimmed(line,"\r\n")) << "\"" ) ;
197 m_header_in.push_back( G::Str::trimmed(line,"\r\n") ) ;
198 m_n = 0UL ;
199 }
200
201 void GSmtp::SpamClient::addBody( const std::string & line )
202 {
203 if( m_out.get() != NULL && m_out_size < headerBodyLength() )
204 {
205 std::ostream & out = *m_out.get() ;
206 out << line << "\n" ;
207 m_out_size += line.length() + 1U ;
208 m_out_lines++ ;
209
210 if( m_out_size >= headerBodyLength() )
211 G_LOG( "GSmtp::SpamClient::addBody: spam<<: [" << m_out_lines
212 << " lines of body text, " << m_out_size << " bytes]" ) ;
213 }
214 }
215
216 std::string GSmtp::SpamClient::headerResult() const
217 {
218 return G::Str::lower( part(headerLine("Spam:"),1U,"true") ) == "true" ? "marked as spam" : std::string() ;
219 }
220
221 std::string GSmtp::SpamClient::part( const std::string & line , unsigned int i , const std::string & default_ ) const
222 {
223 StringArray part ;
224 G::Str::splitIntoTokens( line , part , "\t :" ) ;
225 return part.size() > i ? part[i] : default_ ;
226 }
227
228 unsigned long GSmtp::SpamClient::headerBodyLength() const
229 {
230 if( m_n == 0UL )
231 (const_cast<SpamClient*>(this))->m_n = G::Str::toULong( part(headerLine("Content-length:"),1U,"0") ) ;
232 return m_n ;
233 }
234
235 std::string GSmtp::SpamClient::headerLine( const std::string & key , const std::string & default_ ) const
236 {
237 for( StringArray::const_iterator p = m_header_in.begin() ; p != m_header_in.end() ; ++p )
238 {
239 if( (*p).find(key) == 0U )
240 return *p ;
241 }
242 return default_ ;
243 }
244
GSmtp::SpamClient::busy
bool busy() const
Returns true after request() and before the subsequent event signal.
Definition: gspamclient.cpp:47
G::Str::printable
static std::string printable(const std::string &in, char escape= '\\')
Returns a printable represention of the given input string.
Definition: gstr.cpp:507
GNet
Network classes.
GSmtp::SpamClient::onReceive
virtual bool onReceive(const std::string &)
Final override from GNet::Client.
Definition: gspamclient.cpp:176
gspamclient.h
G::StringArray
std::vector< std::string > StringArray
A std::vector of std::strings.
Definition: gstrings.h:44
GNet::ResolverInfo
A class that holds a host/service name pair and optionally the results of a name-to-address lookup...
Definition: gresolverinfo.h:48
G::Str::splitIntoTokens
static void splitIntoTokens(const std::string &in, Strings &out, const std::string &ws)
Splits the string into 'ws'-delimited tokens.
Definition: gstr.cpp:714
GSmtp::SpamClient::onDeleteImp
virtual void onDeleteImp(const std::string &, bool)
Final override from GNet::Client.
Definition: gspamclient.cpp:56
G::Str::lower
static std::string lower(const std::string &s)
Returns a copy of 's' in which all uppercase characters have been replaced by lowercase characters...
Definition: gstr.cpp:408
GSmtp::SpamClient::onSendComplete
virtual void onSendComplete()
Final override from GNet::BufferedClient.
Definition: gspamclient.cpp:108
gstr.h
gassert.h
GSmtp::SpamClient::request
void request(const std::string &)
Issues a request.
Definition: gspamclient.cpp:73
GSmtp::SpamClient::~SpamClient
virtual ~SpamClient()
Destructor.
Definition: gspamclient.cpp:43
G_LOG
#define G_LOG(expr)
Definition: glog.h:98
GSmtp::SpamClient::onConnect
virtual void onConnect()
Final override from GNet::SimpleClient.
Definition: gspamclient.cpp:95
gfile.h
GSmtp::SpamClient::onDelete
virtual void onDelete(const std::string &, bool)
Final override from GNet::HeapClient.
Definition: gspamclient.cpp:52
GSmtp::SpamClient
A client class that interacts with a remote process using a protocol somewhat similar to the spamassa...
Definition: gspamclient.h:47
GSmtp::SpamClient::onSecure
virtual void onSecure(const std::string &)
Final override from GNet::SocketProtocol.
Definition: gspamclient.cpp:172
G::Str::trimmed
static std::string trimmed(const std::string &s, const std::string &ws)
Returns a trim()med version of s.
Definition: gstr.cpp:138
G::Str::readLineFrom
static std::string readLineFrom(std::istream &stream, const std::string &eol=std::string())
Reads a line from the stream using the given line terminator.
Definition: gstr.cpp:536
G_DEBUG
#define G_DEBUG(expr)
Definition: glog.h:95
GSmtp::Client
A class which acts as an SMTP client, extracting messages from a message store and forwarding them to...
Definition: gsmtpclient.h:55
GNet::ResolverInfo::displayString
std::string displayString(bool simple=false) const
Returns a string representation for logging and debug.
Definition: gresolverinfo.cpp:116
G::File::sizeString
static std::string sizeString(const Path &file)
Returns the file's size in string format.
Definition: gfile_unix.cpp:67
GSmtp::SpamClient::SpamClient
SpamClient(const GNet::ResolverInfo &host_and_service, unsigned int connect_timeout, unsigned int response_timeout)
Constructor.
Definition: gspamclient.cpp:29
gsmtp.h
G::Str::toULong
static unsigned long toULong(const std::string &s, bool limited=false)
Converts string 's' to an unsigned long.
Definition: gstr.cpp:362
G_WARNING
#define G_WARNING(expr)
Definition: glog.h:107