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 
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 
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 
252 {
253  return m_content_path ;
254 }
255 
std::string str() const
Returns the path string.
Definition: gpath.cpp:135
#define G_LOG_S(expr)
Definition: glog.h:103
G::Path contentPath(unsigned long seq) const
Returns the path for a content file.
Definition: gfilestore.cpp:161
G::Path contentPath() const
Returns the path of the content file.
Definition: gnewfile.cpp:251
static bool copy(const Path &from, const Path &to, const NoThrow &)
Copies a file. Returns false on error.
Definition: gfile.cpp:70
std::string basename() const
Returns the path, excluding drive/directory parts.
Definition: gpath.cpp:162
std::list< std::string > Strings
A std::list of std::strings.
Definition: gstrings.h:39
static std::string encode(const std::string &)
Encodes the given string.
Definition: gxtext.cpp:58
virtual void addTo(const std::string &to, bool local)
Final override from GSmtp::NewMessage.
Definition: gnewfile.cpp:115
std::auto_ptr< std::ostream > stream(const G::Path &path)
Returns a stream to the given content.
Definition: gfilestore.cpp:152
static bool rename(const Path &from, const Path &to, const NoThrow &)
Renames the file. Returns false on error.
Definition: gfile.cpp:46
virtual ~NewFile()
Destructor.
Definition: gnewfile.cpp:60
#define G_ASSERT(test)
Definition: gassert.h:30
NewFile(const std::string &from, FileStore &store, unsigned long max_size=0UL)
Constructor. The FileStore reference is kept.
Definition: gnewfile.cpp:39
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
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
#define G_LOG(expr)
Definition: glog.h:98
virtual void commit()
Final override from GSmtp::NewMessage.
Definition: gnewfile.cpp:107
#define G_DEBUG(expr)
Definition: glog.h:95
static bool remove(const Path &path, const NoThrow &)
Deletes the file or directory. Returns false on error.
Definition: gfile.cpp:29
An overload discriminator class for File methods.
Definition: gfile.h:56
virtual unsigned long id() const
Final override from GSmtp::NewMessage.
Definition: gnewfile.cpp:246
A concrete implementation of the MessageStore interface dealing in paired flat files and with an opti...
Definition: gfilestore.h:62
unsigned long newSeq()
Hands out a new non-zero sequence number.
Definition: gfilestore.cpp:191
virtual bool addText(const std::string &line)
Final override from GSmtp::NewMessage.
Definition: gnewfile.cpp:123
A Path object represents a file system path.
Definition: gpath.h:44