34 const Filter::Config & filter_config ,
35 const std::string & path ) :
36 m_file_store(file_store) ,
37 m_filter_type(filter_type) ,
38 m_exit(0,filter_type) ,
40 m_timeout(filter_config.timeout) ,
42 m_task(*this,es,
"<<filter exec error: __strerror__>>",
G::Root::nobody())
49bool GFilters::ExecutableFilter::quiet()
const
54std::string GFilters::ExecutableFilter::id()
const
56 return m_path.basename() ;
59bool GFilters::ExecutableFilter::special()
const
61 return m_exit.special ;
64GSmtp::Filter::Result GFilters::ExecutableFilter::result()
const
66 return m_exit.result ;
69std::string GFilters::ExecutableFilter::response()
const
71 G_ASSERT( m_exit.ok() || m_exit.abandon() || !m_response.empty() ) ;
72 if( m_exit.ok() || m_exit.abandon() )
78int GFilters::ExecutableFilter::responseCode()
const
80 return m_response_code ;
83std::string GFilters::ExecutableFilter::reason()
const
85 G_ASSERT( m_exit.ok() || m_exit.abandon() || !m_reason.empty() ) ;
86 if( m_exit.ok() || m_exit.abandon() )
94 GStore::FileStore::State state = m_filter_type == Filter::Type::server ?
95 GStore::FileStore::State::New : GStore::FileStore::State::Locked ;
96 G::Path cpath = m_file_store.contentPath( message_id ) ;
97 G::Path epath = m_file_store.envelopePath( message_id , state ) ;
100 args.push_back( cpath.
str() ) ;
101 args.push_back( epath.
str() ) ;
103 G_LOG(
"GFilters::ExecutableFilter::start: " << prefix() <<
": [" << message_id.
str() <<
"]: running " << m_path ) ;
104 m_task.start( commandline ) ;
107 m_timer.startTimer( m_timeout ) ;
110void GFilters::ExecutableFilter::onTimeout()
112 G_WARNING(
"GFilters::ExecutableFilter::onTimeout: " << prefix() <<
" timed out after " << m_timeout <<
"s" ) ;
114 m_exit = Exit( 1 , m_filter_type ) ;
115 G_ASSERT( m_exit.fail() ) ;
116 m_response =
"error" ;
117 m_response_code = 0 ;
118 m_reason =
"timeout" ;
119 m_done_signal.emit(
static_cast<int>(m_exit.result) ) ;
122void GFilters::ExecutableFilter::onTaskDone(
int exit_code ,
const std::string & output )
124 m_timer.cancelTimer() ;
127 std::tie(m_response,m_response_code,m_reason) = parseOutput( output ,
"rejected" ) ;
128 if( m_response.find(
"filter exec error:") == 0U )
130 m_reason = m_response ;
131 m_response =
"rejected" ;
132 m_response_code = 0 ;
135 m_exit = Exit( exit_code , m_filter_type ) ;
138 G_WARNING(
"GFilters::ExecutableFilter::onTaskDone: " << prefix() <<
" failed: "
139 <<
"exit code " << exit_code <<
": [" << m_response <<
"]"
144 m_done_signal.emit(
static_cast<int>(m_exit.result) ) ;
147std::tuple<std::string,int,std::string> GFilters::ExecutableFilter::parseOutput( std::string s ,
148 const std::string & default_ )
150 G_DEBUG(
"GFilters::ExecutableFilter::parseOutput: in: \"" <<
G::Str::printable(s) <<
"\"" ) ;
152 static constexpr auto start_1 =
"<<"_sv ;
153 static constexpr auto end_1 =
">>"_sv ;
154 static constexpr auto start_2 =
"[["_sv ;
155 static constexpr auto end_2 =
"]]"_sv ;
162 for(
auto p = lines.begin() ; p != lines.end() ; )
164 const std::string & line = *p ;
165 std::size_t pos_start = line.find( start_1.data() , 0U , start_1.size() ) ;
166 std::size_t pos_end = line.find( end_1.data() , 0U , end_1.size() ) ;
167 if( pos_start != 0U )
169 pos_start = line.find( start_2.data() , 0U , start_2.size() ) ;
170 pos_end = line.find( end_2.data() , 0U , end_2.size() ) ;
172 if( pos_start == 0U && pos_end != std::string::npos )
178 p = lines.erase( p ) ;
182 G_DEBUG(
"GFilters::ExecutableFilter::parseOutput: out: [" <<
G::Str::join(
"|",lines) <<
"]" ) ;
184 std::string response = ( !lines.empty() && !lines.at(0U).empty() ) ? lines.at(0U) : default_ ;
185 int response_code = 0 ;
186 if( response.size() >= 3U &&
187 ( response[0] ==
'4' || response[0] ==
'5' ) &&
188 ( response[1] >=
'0' && response[1] <=
'9' ) &&
189 ( response[2] >=
'0' && response[2] <=
'9' ) &&
190 ( response.size() == 3U || response[3] ==
' ' || response[3] ==
'\t' ) )
193 response.erase( 0U , response.size() == 3U ? 3U : 4U ) ;
195 std::string reason = ( lines.size() > 1U && !lines.at(1U).empty() ) ? lines.at(1U) : response ;
196 return { response , response_code , reason } ;
201 return m_done_signal ;
204void GFilters::ExecutableFilter::cancel()
207 m_timer.cancelTimer() ;
210std::string GFilters::ExecutableFilter::prefix()
const
212 return G::sv_to_string(strtype(m_filter_type)).append(
" [").append(
id()).append(1U,
']') ;
A Filter class that runs an external helper program.
ExecutableFilter(GNet::EventState, GStore::FileStore &, Filter::Type, const Filter::Config &, const std::string &path)
Constructor.
~ExecutableFilter() override
Destructor.
A lightweight object containing an ExceptionHandler pointer, optional ExceptionSource pointer and opt...
A concrete implementation of the MessageStore interface dealing in paired flat files.
A somewhat opaque identifer for a GStore::MessageStore message id.
std::string str() const
Returns the id string.
A structure representing an external program, holding a path and a set of arguments.
A Path object represents a file system path.
std::string str() const
Returns the path string.
static int toInt(std::string_view s)
Converts string 's' to an int.
static std::string join(std::string_view sep, const StringArray &strings)
Concatenates an array of strings with separators.
static void splitIntoFields(std::string_view in, StringArray &out, char sep, char escape='\0', bool remove_escapes=true)
Splits the string into fields.
static std::string fromInt(int i)
Converts int 'i' to a string.
static std::string printable(const std::string &in, char escape='\\')
Returns a printable representation of the given input string, using chacter code ranges 0x20 to 0x7e ...
static unsigned int replaceAll(std::string &s, std::string_view from, std::string_view to)
Does a global replace on string 's', replacing all occurrences of sub-string 'from' with 'to'.
std::vector< std::string > StringArray
A std::vector of std::strings.