- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

gnewfile.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 // gnewfile.cpp
19 //
20
21 #include "gdef.h"
22 #include "gsmtp.h"
23 #include "gmessagestore.h"
24 #include "gnewfile.h"
25 #include "gmemory.h"
26 #include "gprocess.h"
27 #include "gstrings.h"
28 #include "groot.h"
29 #include "gfile.h"
30 #include "gstr.h"
31 #include "gxtext.h"
32 #include "gassert.h"
33 #include "glog.h"
34 #include <functional>
35 #include <algorithm>
36 #include <iostream>
37 #include <fstream>
38
39 GSmtp::NewFile::NewFile( const std::string & from , FileStore & store , unsigned long max_size ) :
40 m_store(store) ,
41 m_from(from) ,
42 m_committed(false) ,
43 m_eight_bit(false) ,
44 m_saved(false) ,
45 m_size(0UL) ,
46 m_max_size(max_size)
47 {
48 // ask the store for a unique id
49 //
50 m_seq = store.newSeq() ;
51
52 // ask the store for a content stream
53 //
54 m_content_path = m_store.contentPath( m_seq ) ;
55 G_LOG( "GSmtp::NewMessage: content file: " << m_content_path ) ;
56 std::auto_ptr<std::ostream> content_stream = m_store.stream( m_content_path ) ;
57 m_content = content_stream ;
58 }
59
60 GSmtp::NewFile::~NewFile()
61 {
62 try
63 {
64 G_DEBUG( "GSmtp::NewFile::dtor: " << m_content_path ) ;
65 cleanup() ;
66 m_store.updated() ;
67 }
68 catch(...) // dtor
69 {
70 }
71 }
72
73 void GSmtp::NewFile::cleanup()
74 {
75 discardContent() ;
76 if( ! m_committed )
77 {
78 deleteEnvelope() ;
79 deleteContent() ;
80 }
81 }
82
83 std::string GSmtp::NewFile::prepare( const std::string & auth_id , const std::string & peer_socket_address ,
84 const std::string & peer_socket_name , const std::string & peer_certificate )
85 {
86 // flush and close the content file
87 //
88 flushContent() ;
89
90 // write the envelope
91 //
92 m_envelope_path_0 = m_store.envelopeWorkingPath( m_seq ) ;
93 m_envelope_path_1 = m_store.envelopePath( m_seq ) ;
94 if( ! saveEnvelope( auth_id , peer_socket_address , peer_socket_name , peer_certificate ) )
95 throw GSmtp::MessageStore::StorageError( std::string() + "cannot write " + m_envelope_path_0.str() ) ;
96
97 // deliver to local mailboxes
98 //
99 if( m_to_local.size() != 0U )
100 {
101 deliver( m_to_local , m_content_path , m_envelope_path_0 , m_envelope_path_1 ) ;
102 }
103
104 return m_content_path.str() ;
105 }
106
107 void GSmtp::NewFile::commit()
108 {
109 bool ok = commitEnvelope() ;
110 m_committed = true ;
111 if( !ok )
112 throw GSmtp::MessageStore::StorageError( std::string() + "cannot rename to " + m_envelope_path_1.str() ) ;
113 }
114
115 void GSmtp::NewFile::addTo( const std::string & to , bool local )
116 {
117 if( local )
118 m_to_local.push_back( to ) ;
119 else
120 m_to_remote.push_back( to ) ;
121 }
122
123 bool GSmtp::NewFile::addText( const std::string & line )
124 {
125 m_size += ( line.size() + 2U ) ;
126 if( ! m_eight_bit )
127 m_eight_bit = isEightBit( line ) ;
128
129 *(m_content.get()) << line << crlf() ;
130 return m_max_size == 0UL || m_size < m_max_size ;
131 }
132
133 void GSmtp::NewFile::flushContent()
134 {
135 G_ASSERT( m_content.get() != NULL ) ;
136 m_content->flush() ;
137 if( ! m_content->good() )
138 throw GSmtp::MessageStore::WriteError( m_content_path.str() ) ;
139 m_content <<= 0 ;
140 }
141
142 void GSmtp::NewFile::discardContent()
143 {
144 m_content <<= 0 ;
145 }
146
147 void GSmtp::NewFile::deleteContent()
148 {
149 FileWriter claim_writer ;
150 G::File::remove( m_content_path , G::File::NoThrow() ) ;
151 }
152
153 void GSmtp::NewFile::deleteEnvelope()
154 {
155 if( ! m_envelope_path_0.str().empty() )
156 {
157 FileWriter claim_writer ;
158 G::File::remove( m_envelope_path_0 , G::File::NoThrow() ) ;
159 }
160 }
161
162 namespace
163 {
164 struct EightBit : std::unary_function<char,bool>
165 {
166 bool operator()( char c ) { return !! ( static_cast<unsigned char>(c) & 0x80U ) ; }
167 } ;
168 }
169
170 bool GSmtp::NewFile::isEightBit( const std::string & line )
171 {
172 return std::find_if( line.begin() , line.end() , EightBit() ) != line.end() ;
173 }
174
175 bool GSmtp::NewFile::saveEnvelope( const std::string & auth_id , const std::string & peer_socket_address ,
176 const std::string & peer_socket_name , const std::string & peer_certificate ) const
177 {
178 std::auto_ptr<std::ostream> envelope_stream = m_store.stream( m_envelope_path_0 ) ;
179 writeEnvelope( *(envelope_stream.get()) , m_envelope_path_0.str() ,
180 auth_id , peer_socket_address , peer_socket_name , peer_certificate ) ;
181 bool ok = envelope_stream->good() ;
182 return ok ;
183 }
184
185 bool GSmtp::NewFile::commitEnvelope()
186 {
187 FileWriter claim_writer ;
188 m_saved = G::File::rename( m_envelope_path_0 , m_envelope_path_1 , G::File::NoThrow() ) ;
189 return m_saved ;
190 }
191
192 void GSmtp::NewFile::deliver( const G::Strings & /*to*/ ,
193 const G::Path & content_path , const G::Path & envelope_path_now ,
194 const G::Path & envelope_path_later )
195 {
196 // could shell out to "procmail" or "deliver" here, but keep it
197 // simple and within the scope -- just copy into ".local" files
198
199 G_LOG_S( "GSmtp::NewMessage: copying message for local recipient(s): "
200 << content_path.basename() << ".local" ) ;
201
202 FileWriter claim_writer ;
203 G::File::copy( content_path.str() , content_path.str()+".local" ) ;
204 G::File::copy( envelope_path_now.str() , envelope_path_later.str()+".local" ) ;
205 }
206
207 void GSmtp::NewFile::writeEnvelope( std::ostream & stream , const std::string & where ,
208 const std::string & auth_id , const std::string & peer_socket_address ,
209 const std::string & peer_socket_name , const std::string & peer_certificate_in ) const
210 {
211 G_LOG( "GSmtp::NewMessage: envelope file: " << where ) ;
212
213 std::string peer_certificate = peer_certificate_in ;
214 G::Str::replaceAll( peer_certificate , "\n" , "" ) ;
215
216 const std::string x( m_store.x() ) ;
217
218 stream << x << "Format: " << m_store.format() << crlf() ;
219 stream << x << "Content: " << (m_eight_bit?"8":"7") << "bit" << crlf() ;
220 stream << x << "From: " << m_from << crlf() ;
221 stream << x << "ToCount: " << (m_to_local.size()+m_to_remote.size()) << crlf() ;
222 {
223 G::Strings::const_iterator to_p = m_to_local.begin() ;
224 for( ; to_p != m_to_local.end() ; ++to_p )
225 stream << x << "To-Local: " << *to_p << crlf() ;
226 }
227 {
228 G::Strings::const_iterator to_p = m_to_remote.begin() ;
229 for( ; to_p != m_to_remote.end() ; ++to_p )
230 stream << x << "To-Remote: " << *to_p << crlf() ;
231 }
232 stream << x << "Authentication: " << G::Xtext::encode(auth_id) << crlf() ;
233 stream << x << "Client: " << peer_socket_address << crlf() ;
234 stream << x << "ClientName: " << G::Xtext::encode(peer_socket_name) << crlf() ;
235 stream << x << "ClientCertificate: " << peer_certificate << crlf() ;
236 stream << x << "End: 1" << crlf() ;
237 stream.flush() ;
238 }
239
240 const std::string & GSmtp::NewFile::crlf() const
241 {
242 static const std::string s( "\015\012" ) ;
243 return s ;
244 }
245
246 unsigned long GSmtp::NewFile::id() const
247 {
248 return m_seq ;
249 }
250
251 G::Path GSmtp::NewFile::contentPath() const
252 {
253 return m_content_path ;
254 }
255
G::Path::str
std::string str() const
Returns the path string.
Definition: gpath.cpp:135
G_LOG_S
#define G_LOG_S(expr)
Definition: glog.h:103
GSmtp::FileStore::contentPath
G::Path contentPath(unsigned long seq) const
Returns the path for a content file.
Definition: gfilestore.cpp:161
GSmtp::NewFile::contentPath
G::Path contentPath() const
Returns the path of the content file.
Definition: gnewfile.cpp:251
gstrings.h
G::File::copy
static bool copy(const Path &from, const Path &to, const NoThrow &)
Copies a file. Returns false on error.
Definition: gfile.cpp:70
glog.h
G::Path::basename
std::string basename() const
Returns the path, excluding drive/directory parts.
Definition: gpath.cpp:162
G::Strings
std::list< std::string > Strings
A std::list of std::strings.
Definition: gstrings.h:39
G::Xtext::encode
static std::string encode(const std::string &)
Encodes the given string.
Definition: gxtext.cpp:58
GSmtp::NewFile::addTo
virtual void addTo(const std::string &to, bool local)
Final override from GSmtp::NewMessage.
Definition: gnewfile.cpp:115
groot.h
GSmtp::FileStore::stream
std::auto_ptr< std::ostream > stream(const G::Path &path)
Returns a stream to the given content.
Definition: gfilestore.cpp:152
G::File::rename
static bool rename(const Path &from, const Path &to, const NoThrow &)
Renames the file. Returns false on error.
Definition: gfile.cpp:46
GSmtp::NewFile::~NewFile
virtual ~NewFile()
Destructor.
Definition: gnewfile.cpp:60
G_ASSERT
#define G_ASSERT(test)
Definition: gassert.h:30
gstr.h
GSmtp::NewFile::NewFile
NewFile(const std::string &from, FileStore &store, unsigned long max_size=0UL)
Constructor. The FileStore reference is kept.
Definition: gnewfile.cpp:39
gassert.h
G::Str::replaceAll
static unsigned int replaceAll(std::string &s, const std::string &from, const std::string &to)
Does a global replace on string 's', replacing all occurences of sub-string 'from' with 'to'...
Definition: gstr.cpp:78
GSmtp::NewFile::prepare
virtual std::string prepare(const std::string &auth_id, const std::string &peer_socket_address, const std::string &peer_socket_name, const std::string &peer_certificate)
Final override from GSmtp::NewMessage.
Definition: gnewfile.cpp:83
G_LOG
#define G_LOG(expr)
Definition: glog.h:98
gfile.h
gprocess.h
gmessagestore.h
GSmtp::NewFile::commit
virtual void commit()
Final override from GSmtp::NewMessage.
Definition: gnewfile.cpp:107
G_DEBUG
#define G_DEBUG(expr)
Definition: glog.h:95
G::File::remove
static bool remove(const Path &path, const NoThrow &)
Deletes the file or directory. Returns false on error.
Definition: gfile.cpp:29
gxtext.h
G::File::NoThrow
An overload discriminator class for File methods.
Definition: gfile.h:56
GSmtp::NewFile::id
virtual unsigned long id() const
Final override from GSmtp::NewMessage.
Definition: gnewfile.cpp:246
GSmtp::FileStore
A concrete implementation of the MessageStore interface dealing in paired flat files and with an opti...
Definition: gfilestore.h:62
gsmtp.h
gnewfile.h
GSmtp::FileStore::newSeq
unsigned long newSeq()
Hands out a new non-zero sequence number.
Definition: gfilestore.cpp:191
GSmtp::NewFile::addText
virtual bool addText(const std::string &line)
Final override from GSmtp::NewMessage.
Definition: gnewfile.cpp:123
gmemory.h
G::Path
A Path object represents a file system path.
Definition: gpath.h:44