E-MailRelay
gprotocolmessageforward.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 gprotocolmessageforward.cpp
19///
20
21#include "gdef.h"
24#include "gmessagestore.h"
25#include "gstr.h"
26#include "glog.h"
27
29 GStore::MessageStore & store , FilterFactoryBase & ff , std::unique_ptr<ProtocolMessage> pm ,
30 const GSmtp::Client::Config & client_config ,
31 const GAuth::SaslClientSecrets & client_secrets ,
32 const std::string & forward_to , int forward_to_family ) :
33 m_es(es) ,
34 m_store(store) ,
35 m_ff(ff) ,
36 m_client_location(forward_to,forward_to_family) ,
37 m_client_config(client_config) ,
38 m_client_secrets(client_secrets) ,
39 m_pm(pm.release()) ,
40 m_id(GStore::MessageId::none()) ,
41 m_processed_signal(true)
42{
43 // signal plumbing to receive 'done' events
44 m_pm->processedSignal().connect( G::Slot::slot(*this,&ProtocolMessageForward::protocolMessageProcessed) ) ;
45 m_client_ptr.deleteSignal().connect( G::Slot::slot(*this,&ProtocolMessageForward::clientDone) ) ;
46}
47
49{
50 m_pm->processedSignal().disconnect() ;
51 m_client_ptr.deleteSignal().disconnect() ;
52 if( m_client_ptr.get() != nullptr )
53 m_client_ptr->messageDoneSignal().disconnect() ;
54}
55
56GSmtp::ProtocolMessage::ProcessedSignal & GSmtp::ProtocolMessageForward::processedSignal() noexcept
57{
58 return m_processed_signal ;
59}
60
61void GSmtp::ProtocolMessageForward::reset()
62{
63 m_pm->reset() ;
64 m_client_ptr.reset() ;
65}
66
67void GSmtp::ProtocolMessageForward::clear()
68{
69 m_pm->clear() ;
70}
71
72GStore::MessageId GSmtp::ProtocolMessageForward::setFrom( const std::string & from , const FromInfo & from_info )
73{
74 return m_pm->setFrom( from , from_info ) ;
75}
76
77GSmtp::ProtocolMessage::FromInfo GSmtp::ProtocolMessageForward::fromInfo() const
78{
79 return m_pm->fromInfo() ;
80}
81
82std::string GSmtp::ProtocolMessageForward::bodyType() const
83{
84 return m_pm->bodyType() ;
85}
86
87bool GSmtp::ProtocolMessageForward::addTo( const ToInfo & to_info )
88{
89 return m_pm->addTo( to_info ) ;
90}
91
92void GSmtp::ProtocolMessageForward::addReceived( const std::string & line )
93{
94 m_pm->addReceived( line ) ;
95}
96
97GStore::NewMessage::Status GSmtp::ProtocolMessageForward::addContent( const char * line_data , std::size_t line_size )
98{
99 return m_pm->addContent( line_data , line_size ) ;
100}
101
102std::size_t GSmtp::ProtocolMessageForward::contentSize() const
103{
104 return m_pm->contentSize() ;
105}
106
107std::string GSmtp::ProtocolMessageForward::from() const
108{
109 return m_pm->from() ;
110}
111
112void GSmtp::ProtocolMessageForward::process( const std::string & auth_id , const std::string & peer_socket_address ,
113 const std::string & peer_certificate )
114{
115 // commit to the store -- forward when the commit is complete
116 m_processed_signal.reset() ; // one-shot reset
117 m_pm->process( auth_id , peer_socket_address , peer_certificate ) ;
118}
119
120void GSmtp::ProtocolMessageForward::protocolMessageProcessed( const ProtocolMessage::ProcessedInfo & info )
121{
122 G_ASSERT( info.response.find('\t') == std::string::npos ) ;
123 G_DEBUG( "ProtocolMessageForward::protocolMessageProcessed: " << (info.success?1:0) << " "
124 << info.id.str() << " [" << info.response << "] [" << info.reason << "]" ) ;
125
126 G::CallFrame this_( m_call_stack ) ;
127 if( info.success && info.id.valid() )
128 {
129 m_id = info.id ;
130
131 // the message is now stored -- start the forwarding using the Client object
132 bool nothing_to_do = false ;
133 std::string error = forward( info.id , nothing_to_do ) ;
134
135 if( this_.deleted() ) return ; // just in case
136 if( nothing_to_do )
137 {
138 // no remote recipients
139 m_processed_signal.emit( { true , info.id , 0 , std::string() , std::string() } ) ;
140 }
141 else if( !error.empty() )
142 {
143 // immediate failure or no recipients etc
144 m_processed_signal.emit( { false , info.id , 0 , "forwarding failed" , error } ) ;
145 }
146 }
147 else
148 {
149 // filter fail, or filter abandon, or message storage failed
150 m_processed_signal.emit( info ) ;
151 }
152}
153
154std::string GSmtp::ProtocolMessageForward::forward( const GStore::MessageId & id , bool & nothing_to_do )
155{
156 try
157 {
158 nothing_to_do = false ;
159 G_DEBUG( "GSmtp::ProtocolMessageForward::forward: forwarding message " << id.str() ) ;
160
161 std::unique_ptr<GStore::StoredMessage> message = m_store.get( id ) ;
162 if( message->toCount() == 0U )
163 {
164 nothing_to_do = true ;
165 }
166 else
167 {
168 if( m_client_ptr.get() == nullptr )
169 {
170 m_client_ptr.reset( std::make_unique<Forward>( m_es.eh(m_client_ptr) ,
171 m_ff , m_client_location , m_client_secrets , m_client_config ) ) ;
172
173 m_client_ptr->messageDoneSignal().connect( G::Slot::slot( *this ,
174 &GSmtp::ProtocolMessageForward::messageDone ) ) ;
175 }
176 m_client_ptr->sendMessage( std::unique_ptr<GStore::StoredMessage>(message.release()) ) ;
177 }
178 return {} ;
179 }
180 catch( std::exception & e ) // send forwarding errors back to the remote client via the server protocol
181 {
182 G_WARNING( "GSmtp::ProtocolMessageForward::forward: forwarding exception: " << e.what() ) ;
183 std::string e_what = e.what() ;
184 if( e_what.empty() )
185 e_what = "exception" ;
186 return e_what ;
187 }
188}
189
190void GSmtp::ProtocolMessageForward::messageDone( const Client::MessageDoneInfo & info )
191{
192 G_DEBUG( "GSmtp::ProtocolMessageForward::messageDone: \"" << info.response << "\"" ) ;
193 const bool ok = info.response.empty() ;
194 m_processed_signal.emit( { ok , m_id , ok?0:info.response_code , info.response , std::string() } ) ;
195}
196
197void GSmtp::ProtocolMessageForward::clientDone( const std::string & reason )
198{
199 G_DEBUG( "GSmtp::ProtocolMessageForward::clientDone: \"" << reason << "\"" ) ;
200 const bool ok = reason.empty() ;
201 m_processed_signal.emit( { ok , m_id , 0 , ok?"":"forwarding failed" , reason } ) ;
202}
203
An interface used by GAuth::SaslClient to obtain a client id and its authentication secret.
G::Slot::Signal< const std::string & > & deleteSignal() noexcept
A signal that is triggered as the client is deleted following an exception handled by this class.
Definition: gclientptr.cpp:37
A lightweight object containing an ExceptionHandler pointer, optional ExceptionSource pointer and opt...
Definition: geventstate.h:131
A factory interface for making GSmtp::Filter message processors.
ProtocolMessageForward(GNet::EventState, GStore::MessageStore &store, FilterFactoryBase &, std::unique_ptr< ProtocolMessage > pm, const GSmtp::Client::Config &client_config, const GAuth::SaslClientSecrets &client_secrets, const std::string &forward_to, int forward_to_family)
Constructor.
~ProtocolMessageForward() override
Destructor.
A somewhat opaque identifer for a GStore::MessageStore message id.
Definition: gmessagestore.h:43
A class which allows SMTP messages to be stored and retrieved.
Definition: gmessagestore.h:73
An object to represent a nested execution context.
Definition: gcall.h:86
Message store classes.
Definition: genvelope.cpp:30
Slot< Args... > slot(TSink &sink, void(TSink::*method)(Args...))
A factory function for Slot objects.
Definition: gslot.h:240
A structure containing GSmtp::Client configuration parameters.
Definition: gsmtpclient.h:67
Extra information from the SMTP MAIL-FROM command passed to GSmtp::ProtocolMessage::setFrom().