E-MailRelay

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

gfilestore.cpp
1 //
2 // Copyright (C) 2001-2018 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 // gfilestore.cpp
19 //
20
21 #include "gdef.h"
22 #include "gsmtp.h"
23 #include "gnoncopyable.h"
24 #include "gfilestore.h"
25 #include "gnewfile.h"
26 #include "gstoredfile.h"
27 #include "gprocess.h"
28 #include "gdirectory.h"
29 #include "groot.h"
30 #include "gpath.h"
31 #include "gfile.h"
32 #include "gstr.h"
33 #include "gtest.h"
34 #include "glog.h"
35 #include "gassert.h"
36 #include <iostream>
37 #include <fstream>
38
39 namespace GSmtp
40 {
41 class FileIterator ;
42 }
43
44 /// \class GSmtp::FileIterator
45 /// A 'body' class for the MessageStore::Iterator 'handle'. The
46 /// handle/body pattern allows us to copy iterators by value
47 /// and therefore return them from MessageStore::iterator().
48 ///
49 class GSmtp::FileIterator : public MessageStore::IteratorImp , public G::noncopyable
50 {
51 public:
52 FileIterator( FileStore & store , const G::Path & dir , bool lock , bool failures ) ;
53 ~FileIterator() ;
54 virtual unique_ptr<GSmtp::StoredMessage> next() ;
55
56 private:
57 FileStore & m_store ;
58 G::DirectoryList m_iter ;
59 bool m_lock ;
60 bool m_failures ;
61 } ;
62
63 // ===
64
65 GSmtp::FileIterator::FileIterator( FileStore & store , const G::Path & dir , bool lock , bool failures ) :
66 m_store(store) ,
67 m_lock(lock) ,
68 m_failures(failures)
69 {
70 DirectoryReader claim_reader ;
71 m_iter.readType( dir , std::string(failures?".envelope.bad":".envelope") ) ;
72 }
73
74 GSmtp::FileIterator::~FileIterator()
75 {
76 }
77
78 unique_ptr<GSmtp::StoredMessage> GSmtp::FileIterator::next()
79 {
80 while( m_iter.more() )
81 {
82 unique_ptr<StoredFile> m( new StoredFile(m_store,m_iter.filePath()) ) ;
83 if( m_lock && !m->lock() )
84 {
85 G_WARNING( "GSmtp::MessageStore: cannot lock file: \"" << m_iter.filePath() << "\"" ) ;
86 continue ;
87 }
88
89 std::string reason ;
90 const bool check_recipients = m_lock ; // check for no-remote-recipients
91 bool ok = m->readEnvelope(reason,check_recipients) && m->openContent(reason) ;
92 if( ok )
93 return unique_ptr<StoredMessage>(m.release()) ; // up-cast to base
94
95 if( m_lock )
96 m->fail( reason , 0 ) ;
97 else
98 G_WARNING( "GSmtp::MessageStore: ignoring \"" << m_iter.filePath() << "\": " << reason ) ;
99 }
100 return unique_ptr<StoredMessage>() ;
101 }
102
103 // ===
104
105 GSmtp::FileStore::FileStore( const G::Path & dir , bool optimise , unsigned long max_size ) :
106 m_seq(1UL) ,
107 m_dir(dir) ,
108 m_optimise(optimise) ,
109 m_empty(false) ,
110 m_max_size(max_size)
111 {
112 m_pid_modifier = static_cast<unsigned long>(G::DateTime::now().s) % 1000000UL ;
113 checkPath( dir ) ;
114
115 if( G::Test::enabled("message-store-unfail") )
116 unfailAll() ;
117
118 if( G::Test::enabled("message-store-clear") )
119 clearAll() ;
120
121 if( G::Test::enabled("message-store-populate") )
122 createTestMessage() ;
123 }
124
125 std::string GSmtp::FileStore::x()
126 {
127 return "X-MailRelay-" ;
128 }
129
130 std::string GSmtp::FileStore::format( int generation )
131 {
132 if( generation == -2 )
133 return "#2821.3" ; // original
134 else if( generation == -1 )
135 return "#2821.4" ; // new for 1.9
136 else
137 return "#2821.5" ; // new for 2.0
138 }
139
140 bool GSmtp::FileStore::knownFormat( const std::string & format_in )
141 {
142 return
143 format_in == format(0) ||
144 format_in == format(-1) ||
145 format_in == format(-2) ;
146 }
147
148 void GSmtp::FileStore::checkPath( const G::Path & directory_path )
149 {
150 G::Directory dir_test( directory_path ) ;
151 bool ok = false ;
152
153 // fail if not readable (after switching effective userid)
154 {
155 FileWriter claim_writer ;
156 ok = dir_test.valid() ;
157 }
158 if( !ok )
159 {
160 throw InvalidDirectory( directory_path.str() ) ;
161 }
162
163 // warn if not writeable (after switching effective userid)
164 {
165 std::string tmp = G::Directory::tmp() ;
166 FileWriter claim_writer ;
167 ok = dir_test.writeable(tmp) ;
168 }
169 if( !ok )
170 {
171 G_WARNING( "GSmtp::MessageStore: directory not writable: \"" << directory_path << "\"" ) ;
172 }
173 }
174
175 unique_ptr<std::ostream> GSmtp::FileStore::stream( const G::Path & path )
176 {
177 FileWriter claim_writer ;
178 return unique_ptr<std::ostream>( new std::ofstream( path.str().c_str() ,
179 std::ios_base::binary | std::ios_base::out | std::ios_base::trunc ) ) ;
180 }
181
182 G::Path GSmtp::FileStore::contentPath( unsigned long seq ) const
183 {
184 return fullPath( filePrefix(seq) + ".content" ) ;
185 }
186
187 G::Path GSmtp::FileStore::envelopePath( unsigned long seq ) const
188 {
189 return fullPath( filePrefix(seq) + ".envelope" ) ;
190 }
191
192 G::Path GSmtp::FileStore::envelopeWorkingPath( unsigned long seq ) const
193 {
194 return fullPath( filePrefix(seq) + ".envelope.new" ) ;
195 }
196
197 std::string GSmtp::FileStore::filePrefix( unsigned long seq ) const
198 {
199 std::ostringstream ss ;
200 G::Process::Id pid ;
201 ss << "emailrelay." << pid.str() << "." << m_pid_modifier << "." << seq ;
202 return ss.str() ;
203 }
204
205 G::Path GSmtp::FileStore::fullPath( const std::string & filename ) const
206 {
207 G::Path p( m_dir ) ;
208 p.pathAppend( filename ) ;
209 return p ;
210 }
211
212 unsigned long GSmtp::FileStore::newSeq()
213 {
214 m_seq++ ;
215 if( m_seq == 0UL )
216 m_seq++ ;
217 return m_seq ;
218 }
219
220 bool GSmtp::FileStore::empty() const
221 {
222 if( m_optimise )
223 {
224 if( !m_empty )
225 const_cast<FileStore*>(this)->m_empty = emptyCore() ;
226 return m_empty ;
227 }
228 else
229 {
230 return emptyCore() ;
231 }
232 }
233
234 bool GSmtp::FileStore::emptyCore() const
235 {
236 G::DirectoryList list ;
237 DirectoryReader claim_reader ;
238 list.readType( m_dir , ".envelope" , 1U ) ;
239 const bool no_more = !list.more() ;
240 return no_more ;
241 }
242
243 GSmtp::MessageStore::Iterator GSmtp::FileStore::iterator( bool lock )
244 {
245 return MessageStore::Iterator( new FileIterator(*this,m_dir,lock,false) ) ;
246 }
247
248 GSmtp::MessageStore::Iterator GSmtp::FileStore::failures()
249 {
250 return MessageStore::Iterator( new FileIterator(*this,m_dir,false,true) ) ;
251 }
252
253 unique_ptr<GSmtp::StoredMessage> GSmtp::FileStore::get( unsigned long id )
254 {
255 G::Path path = envelopePath( id ) ;
256
257 unique_ptr<StoredFile> message( new StoredFile(*this,path) ) ;
258
259 if( ! message->lock() )
260 throw GetError( path.str() + ": cannot lock the file" ) ;
261
262 std::string reason ;
263 const bool check_recipients = false ; // don't check for no-remote-recipients
264 if( ! message->readEnvelope(reason,check_recipients) )
265 throw GetError( path.str() + ": cannot read the envelope: " + reason ) ;
266
267 if( ! message->openContent(reason) )
268 throw GetError( path.str() + ": cannot read the content: " + reason ) ;
269
270 G_LOG( "GSmtp::FileStore::get: processing message \"" << message->name() << "\"" ) ;
271
272 return unique_ptr<StoredMessage>( message.release() ) ; // up-cast to base
273 }
274
275 unique_ptr<GSmtp::NewMessage> GSmtp::FileStore::newMessage( const std::string & from ,
276 const std::string & from_auth_in , const std::string & from_auth_out )
277 {
278 m_empty = false ;
279 return unique_ptr<NewMessage>( new NewFile(*this,from,from_auth_in,from_auth_out,m_max_size) ) ;
280 }
281
282 void GSmtp::FileStore::updated()
283 {
284 G_DEBUG( "GSmtp::FileStore::updated" ) ;
285 m_update_signal.emit() ;
286 }
287
288 G::Slot::Signal0 & GSmtp::FileStore::messageStoreUpdateSignal()
289 {
290 return m_update_signal ;
291 }
292
293 G::Slot::Signal0 & GSmtp::FileStore::messageStoreRescanSignal()
294 {
295 return m_rescan_signal ;
296 }
297
298 void GSmtp::FileStore::rescan()
299 {
300 messageStoreRescanSignal().emit() ;
301 }
302
303 void GSmtp::FileStore::unfailAll()
304 {
305 MessageStore::Iterator iter( failures() ) ;
306 for(;;)
307 {
308 unique_ptr<StoredMessage> message = iter.next() ;
309 if( message.get() == nullptr )
310 break ;
311 G_DEBUG( "GSmtp::FileStore::unfailAll: " << message->name() ) ;
312 message->unfail() ;
313 }
314 }
315
316 void GSmtp::FileStore::createTestMessage()
317 {
318 // for testing...
319 NewFile f( *this , "test@local.net" , "" ) ;
320 f.addTo( "test@local.net" , false ) ;
321 for( int i = 0 ; i < 1000 ; i++ )
322 {
323 std::string line( 998U , 'a'+(i%26) ) ;
324 f.addText( line.data() , line.size() ) ;
325 }
326 f.prepare( "test" , "127.0.0.1:999" , std::string() ) ;
327 f.commit( true ) ;
328 }
329
330 void GSmtp::FileStore::clearAll()
331 {
332 // for testing...
333 MessageStore::Iterator iter( iterator(true) ) ;
334 for(;;)
335 {
336 unique_ptr<StoredMessage> message = iter.next() ;
337 if( message.get() )
338 message->destroy() ;
339 else
340 break ;
341 }
342 }
343
344 // ===
345
346 GSmtp::FileReader::FileReader()
347 {
348 }
349
350 GSmtp::FileReader::~FileReader()
351 {
352 }
353
354 // ===
355
356 GSmtp::DirectoryReader::DirectoryReader()
357 {
358 }
359
360 GSmtp::DirectoryReader::~DirectoryReader()
361 {
362 }
363
364 // ===
365
366 GSmtp::FileWriter::FileWriter() :
367 G::Root(false) ,
368 G::Process::Umask(G::Process::Umask::Tighter)
369 {
370 }
371
372 GSmtp::FileWriter::~FileWriter()
373 {
374 }
375
GSmtp::StoredFile::readEnvelope
bool readEnvelope(std::string &reason, bool check_for_no_remote_recipients)
Reads the envelope.
Definition: gstoredfile.cpp:81
G::Path::str
std::string str() const
Returns the path string.
Definition: gpath.cpp:290
G::noncopyable
A noncopyable base class (a la boost).
Definition: gnoncopyable.h:34
gdirectory.h
GSmtp::FileStore::contentPath
G::Path contentPath(unsigned long seq) const
Returns the path for a content file.
Definition: gfilestore.cpp:182
GSmtp::FileStore::format
static std::string format(int generation=0)
Returns an identifier for the storage format implemented by this class, or some older generation of i...
Definition: gfilestore.cpp:130
GSmtp
SMTP and message-store classes.
Definition: gadminserver.h:39
GSmtp::FileStore::x
static std::string x()
Returns the prefix for envelope header lines.
Definition: gfilestore.cpp:125
GSmtp::FileWriter::FileWriter
FileWriter()
Default constructor.
Definition: gfilestore.cpp:366
glog.h
GSmtp::FileStore::messageStoreUpdateSignal
virtual G::Slot::Signal0 & messageStoreUpdateSignal() override
Override from GSmtp::MessageStore.
Definition: gfilestore.cpp:288
GSmtp::StoredMessage::unfail
virtual void unfail()=0
Marks the message as unfailed within the store.
GSmtp::StoredMessage::name
virtual std::string name() const =0
Returns some sort of unique identifier for the message.
G::DirectoryList::more
bool more()
Returns true if more and advances by one.
Definition: gdirectory.cpp:117
GSmtp::DirectoryReader
Used by GSmtp::FileStore, GSmtp::NewFile and GSmtp::StoredFile to claim read permissions for reading ...
Definition: gfilestore.h:181
GSmtp::FileStore::messageStoreRescanSignal
virtual G::Slot::Signal0 & messageStoreRescanSignal() override
Override from GSmtp::MessageStore.
Definition: gfilestore.cpp:293
groot.h
GSmtp::StoredMessage::destroy
virtual void destroy()=0
Deletes the message within the store.
GSmtp::FileWriter
Used by GSmtp::FileStore, GSmtp::NewFile and GSmtp::StoredFile to claim write permissions.
Definition: gfilestore.h:197
gtest.h
GSmtp::DirectoryReader::~DirectoryReader
~DirectoryReader()
Destructor. Switches identity back.
Definition: gfilestore.cpp:360
GSmtp::MessageStore::IteratorImp
A base class for MessageStore::Iterator implementations.
Definition: gmessagestore.h:46
G::DateTime::now
static EpochTime now()
Returns the current epoch time.
Definition: gdatetime_unix.cpp:26
G::DirectoryList::readType
void readType(const Path &dir, const std::string &suffix, unsigned int limit=0U)
An initialiser that is to be used after default construction.
Definition: gdirectory.cpp:93
G::Directory
An encapsulation of a file system directory that allows for iterating through the set of contained fi...
Definition: gdirectory.h:45
GSmtp::FileStore::failures
virtual MessageStore::Iterator failures() override
Override from GSmtp::MessageStore.
Definition: gfilestore.cpp:248
GSmtp::FileStore::stream
unique_ptr< std::ostream > stream(const G::Path &path)
Returns a stream to the given content.
Definition: gfilestore.cpp:175
GSmtp::FileStore::iterator
virtual MessageStore::Iterator iterator(bool lock) override
Override from GSmtp::MessageStore.
Definition: gfilestore.cpp:243
gstr.h
gfilestore.h
gassert.h
GSmtp::FileStore::FileStore
FileStore(const G::Path &dir, bool optimise=false, unsigned long max_size=0UL)
Constructor.
Definition: gfilestore.cpp:105
G
Low-level classes.
Definition: garg.h:29
GSmtp::FileIterator
A 'body' class for the MessageStore::Iterator 'handle'.
Definition: gfilestore.cpp:49
G::Test::enabled
static bool enabled()
Returns true if test features are enabled.
Definition: gtest.cpp:69
GSmtp::StoredFile::openContent
bool openContent(std::string &reason)
Opens the content file.
Definition: gstoredfile.cpp:223
gfile.h
gprocess.h
GSmtp::NewFile
A concrete derived class implementing the NewMessage interface.
Definition: gnewfile.h:42
GSmtp::StoredFile
A concete derived class implementing the StoredMessage interface.
Definition: gstoredfile.h:43
G::DirectoryList
A Directory iterator with the same kind of interface as G::DirectoryIterator, but doing all file i/o ...
Definition: gdirectory.h:145
GSmtp::FileReader::~FileReader
~FileReader()
Destructor. Switches identity back.
Definition: gfilestore.cpp:350
GSmtp::FileStore::updated
virtual void updated() override
Override from GSmtp::MessageStore.
Definition: gfilestore.cpp:282
GSmtp::FileStore::newMessage
virtual unique_ptr< NewMessage > newMessage(const std::string &from, const std::string &from_auth_in, const std::string &from_auth_out) override
Override from GSmtp::MessageStore.
Definition: gfilestore.cpp:275
G::Directory::tmp
static std::string tmp()
A convenience function for constructing a filename for writeable().
Definition: gdirectory_unix.cpp:99
gstoredfile.h
G::Slot::Signal0
A signal class for zero-parameter callbacks.
Definition: gslot.h:193
gnoncopyable.h
GSmtp::FileWriter::~FileWriter
~FileWriter()
Destructor. Switches identity back.
Definition: gfilestore.cpp:372
gpath.h
GSmtp::FileStore
A concrete implementation of the MessageStore interface dealing in paired flat files.
Definition: gfilestore.h:58
GSmtp::FileStore::envelopePath
G::Path envelopePath(unsigned long seq) const
Returns the path for an envelope file.
Definition: gfilestore.cpp:187
GSmtp::FileStore::get
virtual unique_ptr< StoredMessage > get(unsigned long id) override
Override from GSmtp::MessageStore.
Definition: gfilestore.cpp:253
GSmtp::DirectoryReader::DirectoryReader
DirectoryReader()
Default constructor.
Definition: gfilestore.cpp:356
GSmtp::FileStore::unfailAll
virtual void unfailAll() override
Override from GSmtp::MessageStore.
Definition: gfilestore.cpp:303
gsmtp.h
GSmtp::FileStore::knownFormat
static bool knownFormat(const std::string &format)
Returns true if the storage format string is recognised and supported for reading.
Definition: gfilestore.cpp:140
gnewfile.h
GSmtp::FileStore::newSeq
unsigned long newSeq()
Hands out a new non-zero sequence number.
Definition: gfilestore.cpp:212
GSmtp::StoredFile::lock
bool lock()
Locks the file by renaming the envelope file.
Definition: gstoredfile.cpp:302
GSmtp::StoredFile::name
virtual std::string name() const override
Override from GSmtp::StoredMessage.
Definition: gstoredfile.cpp:61
G::Path
A Path object represents a file system path.
Definition: gpath.h:72
GSmtp::FileReader::FileReader
FileReader()
Default constructor.
Definition: gfilestore.cpp:346
GSmtp::FileStore::envelopeWorkingPath
G::Path envelopeWorkingPath(unsigned long seq) const
Returns the path for an envelope file which is in the process of being written.
Definition: gfilestore.cpp:192
GSmtp::FileStore::rescan
virtual void rescan() override
Override from GSmtp::MessageStore.
Definition: gfilestore.cpp:298
GSmtp::FileStore::empty
virtual bool empty() const override
Override from GSmtp::MessageStore.
Definition: gfilestore.cpp:220
GSmtp::MessageStore::Iterator
An iterator class for GSmtp::MessageStore.
Definition: gmessagestore.h:55