E-MailRelay
gprotocolmessagestore.cpp
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2024 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/// \file gprotocolmessagestore.cpp
19///
20
21#include "gdef.h"
23#include "gmessagestore.h"
24#include "gstr.h"
25#include "gassert.h"
26#include "glog.h"
27
29 std::unique_ptr<Filter> filter ) :
30 m_store(store) ,
31 m_filter(std::move(filter))
32{
33 m_filter->doneSignal().connect( G::Slot::slot(*this,&ProtocolMessageStore::filterDone) ) ;
34}
35
37{
38 m_filter->doneSignal().disconnect() ;
39}
40
41void GSmtp::ProtocolMessageStore::reset()
42{
43 G_DEBUG( "GSmtp::ProtocolMessageStore::reset" ) ;
44 clear() ;
45}
46
47void GSmtp::ProtocolMessageStore::clear()
48{
49 G_DEBUG( "GSmtp::ProtocolMessageStore::clear" ) ;
50 m_new_msg.reset() ;
51 m_from.erase() ;
52 m_from_info = FromInfo() ;
53 m_filter->cancel() ;
54}
55
56GStore::MessageId GSmtp::ProtocolMessageStore::setFrom( const std::string & from ,
57 const FromInfo & from_info )
58{
59 G_DEBUG( "GSmtp::ProtocolMessageStore::setFrom: " << from ) ;
60
61 if( from.empty() ) // => probably a failure notification message
62 G_WARNING( "GSmtp::ProtocolMessageStore: empty MAIL-FROM return path" ) ;
63
64 G_ASSERT( m_new_msg == nullptr ) ;
65 clear() ; // just in case
66
68 smtp_info.auth = from_info.auth ;
69 smtp_info.body = from_info.body ;
70 smtp_info.address_style = from_info.address_style ;
71 const std::string & from_auth_out = std::string() ;
72 m_new_msg = m_store.newMessage( from , smtp_info , from_auth_out ) ;
73
74 m_from = from ;
75 m_from_info = from_info ;
76 return m_new_msg->id() ;
77}
78
79bool GSmtp::ProtocolMessageStore::addTo( const ToInfo & to_info )
80{
81 G_DEBUG( "GSmtp::ProtocolMessageStore::addTo: " << to_info.status.recipient ) ;
82 G_ASSERT( m_new_msg != nullptr ) ;
83
84 if( to_info.status.recipient.empty() )
85 {
86 return false ;
87 }
88 else if( !to_info.status.is_valid )
89 {
90 G_WARNING( "GSmtp::ProtocolMessage: rejecting recipient \"" << to_info.status.recipient << "\": "
91 << to_info.status.response << (to_info.status.reason.empty()?"":": ") << to_info.status.reason ) ;
92 return false ;
93 }
94 else
95 {
96 m_new_msg->addTo( to_info.status.address , to_info.status.is_local , to_info.address_style ) ;
97 return true ;
98 }
99}
100
101void GSmtp::ProtocolMessageStore::addReceived( const std::string & received_line )
102{
103 G_DEBUG( "GSmtp::ProtocolMessageStore::addReceived" ) ;
104 if( m_new_msg != nullptr )
105 m_new_msg->addContentLine( received_line ) ;
106}
107
108GStore::NewMessage::Status GSmtp::ProtocolMessageStore::addContent( const char * data , std::size_t data_size )
109{
110 G_ASSERT( m_new_msg != nullptr ) ;
111 return m_new_msg->addContent( data , data_size ) ;
112}
113
114std::size_t GSmtp::ProtocolMessageStore::contentSize() const
115{
116 return m_new_msg ? m_new_msg->contentSize() : 0U ;
117}
118
119std::string GSmtp::ProtocolMessageStore::from() const
120{
121 return m_from ;
122}
123
124GSmtp::ProtocolMessage::FromInfo GSmtp::ProtocolMessageStore::fromInfo() const
125{
126 return m_from_info ;
127}
128
129std::string GSmtp::ProtocolMessageStore::bodyType() const
130{
131 return m_from_info.body ;
132}
133
134void GSmtp::ProtocolMessageStore::process( const std::string & session_auth_id ,
135 const std::string & peer_socket_address , const std::string & peer_certificate )
136{
137 try
138 {
139 G_DEBUG( "GSmtp::ProtocolMessageStore::process: \""
140 << session_auth_id << "\", \"" << peer_socket_address << "\"" ) ;
141 G_ASSERT( m_new_msg != nullptr ) ;
142
143 // write ".new" envelope and close the content
144 m_new_msg->prepare( session_auth_id , peer_socket_address , peer_certificate ) ;
145
146 // start filtering
147 G_LOG_MORE( "GSmtp::ProtocolMessageStore::process: filter [" << m_filter->id() << "]: [" << m_new_msg->id().str() << "]" ) ;
148 m_filter->start( m_new_msg->id() ) ;
149 }
150 catch( std::exception & e ) // catch filtering errors, size-limit errors, and file i/o errors
151 {
152 G_WARNING( "GSmtp::ProtocolMessageStore::process: message processing exception: " << e.what() ) ;
153 clear() ;
154 m_processed_signal.emit( { false , GStore::MessageId::none() , 0 , "failed" , e.what() } ) ;
155 }
156}
157
158void GSmtp::ProtocolMessageStore::filterDone( int filter_result )
159{
160 try
161 {
162 G_DEBUG( "GSmtp::ProtocolMessageStore::filterDone: " << filter_result ) ;
163 G_ASSERT( static_cast<int>(m_filter->result()) == filter_result ) ;
164 G_ASSERT( m_new_msg != nullptr ) ;
165
166 const bool ok = filter_result == 0 ;
167 const bool abandon = filter_result == 1 ;
168 const bool rescan = m_filter->special() ;
169
170 std::string filter_response = G::Str::replaced( (ok||abandon) ? std::string() : m_filter->response() , '\t' , ' ' ) ;
171 int filter_response_code = (ok||abandon) ? 0 : m_filter->responseCode() ;
172 std::string filter_reason = (ok||abandon) ? std::string() : m_filter->reason() ;
173
174 G_LOG_IF( !m_filter->quiet() , "GSmtp::ProtocolMessageStore::filterDone: "
175 "filter [" << m_filter->id() << "]: [" << m_new_msg->id().str() << "]: "
176 << m_filter->str(Filter::Type::server) ) ;
177
179 if( ok )
180 {
181 // commit the message to the store
182 m_new_msg->commit( true ) ;
183 message_id = m_new_msg->id() ;
184 }
185 else if( abandon )
186 {
187 // make a token effort to commit the message
188 // but ignore errors and dont set the id
189 m_new_msg->commit( false ) ;
190 }
191 else
192 {
193 G_LOG_S( "GSmtp::ProtocolMessageStore::filterDone: rejected by filter: [" << filter_reason << "]" ) ;
194 }
195
196 if( rescan )
197 {
198 // pick up any new messages create by the filter
199 m_store.rescan() ;
200 }
201
202 clear() ;
203 m_processed_signal.emit( { ok || abandon , message_id , filter_response_code , filter_response , filter_reason } ) ;
204 }
205 catch( std::exception & e ) // catch filtering errors
206 {
207 G_WARNING( "GSmtp::ProtocolMessageStore::filterDone: filter exception: " << e.what() ) ;
208 clear() ;
209 m_processed_signal.emit( { false , GStore::MessageId::none() , 0 , "rejected" , e.what() } ) ;
210 }
211}
212
213GSmtp::ProtocolMessage::ProcessedSignal & GSmtp::ProtocolMessageStore::processedSignal() noexcept
214{
215 return m_processed_signal ;
216}
217
ProtocolMessageStore(GStore::MessageStore &store, std::unique_ptr< Filter >)
Constructor.
~ProtocolMessageStore() override
Destructor.
A somewhat opaque identifer for a GStore::MessageStore message id.
Definition: gmessagestore.h:43
static MessageId none()
Returns an in-valid() id.
A class which allows SMTP messages to be stored and retrieved.
Definition: gmessagestore.h:73
static std::string replaced(const std::string &s, char from, char to)
Returns the string 's' with all occurrences of 'from' replaced by 'to'.
Definition: gstr.cpp:255
Slot< Args... > slot(TSink &sink, void(TSink::*method)(Args...))
A factory function for Slot objects.
Definition: gslot.h:240
STL namespace.
Extra information from the SMTP MAIL-FROM command passed to GSmtp::ProtocolMessage::setFrom().
Information on the SMTP options used when submitted.
Definition: gmessagestore.h:84