E-MailRelay
gmessageidfilter.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 gmessageidfilter.cpp
19///
20
21#include "gdef.h"
22#include "gmessageidfilter.h"
23#include "gstr.h"
24#include "gstringview.h"
25#include "groot.h"
26#include "gfile.h"
27#include "gdatetime.h"
28#include "gprocess.h"
29#include <fstream>
30#include <sstream>
31
33 Filter::Type filter_type , const Filter::Config & config , const std::string & ) :
34 SimpleFilterBase(es,filter_type,"msgid:") ,
35 m_store(store) ,
36 m_domain(config.domain)
37{
38}
39
40GSmtp::Filter::Result GFilters::MessageIdFilter::run( const GStore::MessageId & message_id ,
41 bool & , GStore::FileStore::State )
42{
43 std::string e = process( m_store.contentPath(message_id) , m_domain ) ;
44 if( !e.empty() )
45 throw Error( e ) ;
46 return Result::ok ;
47}
48
49std::string GFilters::MessageIdFilter::process( const G::Path & path_in , const std::string & domain )
50{
51 std::ifstream in ;
52 {
53 G::Root claim_root ;
54 G::File::open( in , path_in ) ;
55 }
56 if( !in.good() )
57 return "open error" ;
58
59 bool have_id = false ;
60 {
61 static constexpr std::size_t line_limit = 10000U ;
62 std::string line ;
63 line.reserve( 2000U ) ;
64 while( G::Str::readLine( in , line , "\n" , true , line_limit ) && line.size() > 1U && !have_id )
65 have_id = isId( line ) ;
66 if( !in.good() )
67 return "format error" ; // eg. no empty line, line too long
68 }
69
70 if( !have_id )
71 {
72 G::Path path_out = path_in.str().append(".tmp") ;
73 std::ofstream out ;
74 {
75 G::Root claim_root ;
76 G::File::open( out , path_out ) ;
77 }
78 if( !out.good() )
79 return "create error" ;
80
81 out << "Message-ID: " << newId(domain) << "\r\n" ;
82 in.seekg( 0 ) ;
83 G::File::copy( in , out ) ;
84
85 in.close() ;
86 out.close() ;
87 if( !in.eof() )
88 return "read error" ;
89 if( out.fail() )
90 return "write error" ;
91
92 bool ok = false ;
93 {
94 G::Root claim_root ;
95 ok = G::File::renameOnto( path_out , path_in , std::nothrow ) ;
96 }
97 if( !ok )
98 return "rename error" ;
99 }
100 return {} ;
101}
102
103bool GFilters::MessageIdFilter::isId( std::string_view line ) noexcept
104{
105 return line.find(':') != std::string::npos && G::sv_imatch( G::sv_substr_noexcept(line,0U,line.find(':')) , "message-id" ) ;
106}
107
108std::string GFilters::MessageIdFilter::newId( const std::string & domain )
109{
110 std::ostringstream ss ;
112 static int generator = 0 ;
113 ss << "<"
114 << now.s() << "." << now.us() << "."
115 << G::Process::Id().str() << "." << generator++
116 << "@" << domain << ">" ;
117 return ss.str() ;
118}
119
static std::string process(const G::Path &, const std::string &domain)
Edits a content file by adding a message-id if necessary.
MessageIdFilter(GNet::EventState, GStore::FileStore &, Filter::Type, const Filter::Config &, const std::string &spec)
Constructor.
A GSmtp::Filter base class for filters that run synchronously.
A lightweight object containing an ExceptionHandler pointer, optional ExceptionSource pointer and opt...
Definition: geventstate.h:131
A concrete implementation of the MessageStore interface dealing in paired flat files.
Definition: gfilestore.h:56
A somewhat opaque identifer for a GStore::MessageStore message id.
Definition: gmessagestore.h:43
static void open(std::ofstream &, const Path &)
Calls open() on the given output file stream.
Definition: gfile_unix.cpp:56
static bool renameOnto(const Path &from, const Path &to, std::nothrow_t) noexcept
Renames the file, deleting 'to' first if necessary.
Definition: gfile_unix.cpp:144
static bool copy(const Path &from, const Path &to, std::nothrow_t)
Copies a file. Returns false on error.
Definition: gfile.cpp:67
A Path object represents a file system path.
Definition: gpath.h:82
std::string str() const
Returns the path string.
Definition: gpath.h:243
Process-id class.
Definition: gprocess.h:167
A class which acquires the process's special privileges on construction and releases them on destruct...
Definition: groot.h:52
static std::istream & readLine(std::istream &stream, std::string &result, std::string_view eol={}, bool pre_erase_result=true, std::size_t limit=0U)
Reads a line from the stream using the given line terminator, which may be multi-character.
Definition: gstr.cpp:958
Represents a unix-epoch time with microsecond resolution.
Definition: gdatetime.h:140
static SystemTime now()
Factory function for the current time.
Definition: gdatetime.cpp:328
std::time_t s() const noexcept
Returns the number of seconds since the start of the epoch.
Definition: gdatetime.cpp:380
unsigned int us() const
Returns the microsecond fraction.
Definition: gdatetime.cpp:374