2828
2929#include < array>
3030#include < charconv>
31+ #include < chrono>
3132#include < fstream>
3233#include < future>
3334
3435using namespace digidoc ;
3536using namespace digidoc ::util;
3637using namespace std ;
38+ using namespace chrono_literals ;
3739
3840namespace digidoc {
3941
@@ -102,14 +104,14 @@ TSL::TSL(string file)
102104 WARN (" Failed to parse configuration: %s" , path.c_str ());
103105}
104106
105- bool TSL::activate (string territory)
107+ bool TSL::activate (string_view territory)
106108{
107109 if (territory.size () != 2 )
108110 return false ;
109111 if (territory == " GR" )
110- territory = " EL" ; // Greece is EL in EU TL
112+ territory = " EL" ; // Greece is EL in EU TL
111113 string cache = CONF (TSLCache);
112- string path = cache + ' /' + territory + " .xml" ;
114+ string path = cache + ' /' + territory. data () + " .xml" ;
113115 if (File::fileExists (path))
114116 return false ;
115117 ofstream (File::encodeName (path), ofstream::binary) << ' ' ;
@@ -192,75 +194,70 @@ string_view TSL::operatorName() const noexcept
192194 return toString (schemeInformation/" SchemeOperatorName" );
193195}
194196
195- vector<TSL::Service> TSL::parse ()
196- {
197- string url = CONF (TSLUrl);
198- string cache = CONF (TSLCache);
199- vector<X509Cert> cert = CONF (TSLCerts);
200- File::createDirectory (cache);
201- return parse (url, cert, cache, string (File::fileName (url)));
202- }
203-
204197vector<TSL::Service> TSL::parse (const string &url, const vector<X509Cert> &certs,
205- const string &cache, const string & territory)
198+ const string &cache, string_view territory)
206199{
207- vector<Service> list;
208- try {
209- TSL tsl = parseTSL (url, certs, cache, territory);
210- if (tsl.pointers ().empty ())
211- return tsl.services ();
200+ TSL tsl = parseTSL (url, certs, cache, territory);
201+ auto pointers = tsl.pointers ();
202+ if (pointers.empty ())
203+ return tsl.services ();
212204
213- vector< future< vector<TSL::Service> > > futures;
214- for (const TSL::Pointer &p: tsl.pointers ())
215- {
216- if (!File::fileExists (cache + " /" + p.territory + " .xml" ))
217- continue ;
218- futures.push_back (async (launch::async, [p, cache]{
205+ vector< future< vector<TSL::Service> > > futures;
206+ for (const TSL::Pointer &p: pointers)
207+ {
208+ if (!File::fileExists (cache + " /" + p.territory + " .xml" ))
209+ continue ;
210+ futures.push_back (async (launch::async, [p, cache] noexcept -> vector<TSL::Service> {
211+ try {
219212 return parse (p.location , p.certs , cache, p.territory + " .xml" );
220- }));
221- }
222- for (auto &f: futures)
223- {
224- vector<Service> services = f.get ();
225- list.insert (list.end (), make_move_iterator (services.begin ()), make_move_iterator (services.end ()));
226- }
213+ }
214+ catch (const Exception &e)
215+ {
216+ debugException (e);
217+ ERR (" TSL %s Failed to validate list" , p.territory .c_str ());
218+ return {};
219+ }
220+ }));
227221 }
228- catch (const Exception &e)
222+ vector<Service> list;
223+ for (auto &f: futures)
229224 {
230- debugException (e);
231- ERR (" TSL %s Failed to validate list" , territory.c_str ());
232- list.clear ();
225+ vector<Service> services = f.get ();
226+ list.insert (list.end (), make_move_iterator (services.begin ()), make_move_iterator (services.end ()));
233227 }
234228 return list;
235229}
236230
237231TSL TSL::parseTSL (const string &url, const vector<X509Cert> &certs,
238- const string &cache, const string & territory)
232+ const string &cache, string_view territory)
239233{
240234 string path = File::path (cache, territory);
241235 TSL valid;
242236 try {
243237 TSL tsl (path);
244238 tsl.validate (certs);
245239 valid = std::move (tsl);
246- DEBUG (" TSL %s (%llu) signature is valid" , territory. c_str ( ), valid.sequenceNumber ());
240+ DEBUG (" TSL %.* s (%llu) signature is valid" , STR_VIEW_FMT (territory ), valid.sequenceNumber ());
247241
248242 if (valid.isExpired ())
249243 {
250244 if (!CONF (TSLAutoUpdate) && CONF (TSLAllowExpired))
251245 return valid;
252- THROW (" TSL %s (%llu) is expired" , territory. c_str ( ), valid.sequenceNumber ());
246+ THROW (" TSL %.* s (%llu) is expired" , STR_VIEW_FMT (territory ), valid.sequenceNumber ());
253247 }
254248
255- if (CONF (TSLOnlineDigest) && ( File::modifiedTime (valid. path ) < ( time ( nullptr ) - ( 60 * 60 * 24 ))) )
249+ if (CONF (TSLOnlineDigest))
256250 {
257- if (valid.validateETag (url))
258- File::updateModifiedTime (valid.path , time (nullptr ));
251+ auto encPath = File::encodeName (valid.path );
252+ auto now = chrono::file_clock::now ();
253+ error_code ec;
254+ if (filesystem::last_write_time (encPath, ec) < now - 24h && valid.validateETag (url))
255+ filesystem::last_write_time (encPath, now, ec);
259256 }
260257
261258 return valid;
262259 } catch (const Exception &) {
263- ERR (" TSL %s signature is invalid" , territory. c_str ( ));
260+ ERR (" TSL %.* s signature is invalid" , STR_VIEW_FMT (territory ));
264261 if (!CONF (TSLAutoUpdate))
265262 throw ;
266263 }
@@ -272,22 +269,22 @@ TSL TSL::parseTSL(const string &url, const vector<X509Cert> &certs,
272269 tsl.validate (certs);
273270 valid = std::move (tsl);
274271
275- ofstream (File::encodeName (path), ofstream::binary|fstream::trunc)
276- << ifstream (File::encodeName (valid.path ), fstream::binary).rdbuf ();
277272 error_code ec;
278- filesystem::remove (File::encodeName (valid.path ), ec);
273+ filesystem::copy_file (File::encodeName (valid.path ), File::encodeName (path), filesystem::copy_options::overwrite_existing, ec);
274+ if (ec == errc{})
275+ filesystem::remove (File::encodeName (valid.path ), ec);
279276
280277 ofstream (File::encodeName (path + " .etag" ), ofstream::trunc) << etag;
281278
282- DEBUG (" TSL %s (%llu) signature is valid" , territory. c_str ( ), valid.sequenceNumber ());
279+ DEBUG (" TSL %.* s (%llu) signature is valid" , STR_VIEW_FMT (territory ), valid.sequenceNumber ());
283280 } catch (const Exception &) {
284- ERR (" TSL %s signature is invalid" , territory. c_str ( ));
281+ ERR (" TSL %.* s signature is invalid" , STR_VIEW_FMT (territory ));
285282 if (!valid)
286283 throw ;
287284 }
288285
289286 if (valid.isExpired () && !CONF (TSLAllowExpired))
290- THROW (" TSL %s (%llu) is expired" , territory. c_str ( ), valid.sequenceNumber ());
287+ THROW (" TSL %.* s (%llu) is expired" , STR_VIEW_FMT (territory ), valid.sequenceNumber ());
291288
292289 return valid;
293290}
@@ -393,9 +390,9 @@ vector<string> TSL::pivotURLs() const
393390
394391vector<TSL::Pointer> TSL::pointers () const
395392{
396- if (!contains (SCHEMES_URI, type ()))
397- return {};
398393 vector<Pointer> pointer;
394+ if (!contains (SCHEMES_URI, type ()))
395+ return pointer;
399396 for (auto other = schemeInformation/" PointersToOtherTSL" /" OtherTSLPointer" ; other; other++)
400397 {
401398 Pointer p;
@@ -439,12 +436,12 @@ vector<X509Cert> TSL::serviceDigitalIdentity(XMLNode service, string_view ctx)
439436 result.emplace_back (cert);
440437 continue ;
441438 } catch (const Exception &e) {
442- DEBUG (" Failed to parse %.*s certificate, Testing also parse as PEM: %s" , int (ctx. size ()), ctx. data ( ), e.msg ().c_str ());
439+ DEBUG (" Failed to parse %.*s certificate, Testing also parse as PEM: %s" , STR_VIEW_FMT (ctx), e.msg ().c_str ());
443440 }
444441 try {
445442 result.emplace_back (cert, X509Cert::Pem);
446443 } catch (const Exception &e) {
447- DEBUG (" Failed to parse %.*s certificate as PEM: %s" , int (ctx. size ()), ctx. data ( ), e.msg ().c_str ());
444+ DEBUG (" Failed to parse %.*s certificate as PEM: %s" , STR_VIEW_FMT (ctx), e.msg ().c_str ());
448445 }
449446 }
450447 }
@@ -502,9 +499,9 @@ void TSL::validate() const
502499 THROW (" Failed to parse XML" );
503500 auto signature = (*this )/XMLName{" Signature" , DSIG_NS};
504501 if (!signature)
505- THROW (" TSL %s Failed to verify signature" , territory (). data ( ));
502+ THROW (" TSL %.* s Failed to verify signature" , STR_VIEW_FMT ( territory ()));
506503 if (!XMLDocument::verifySignature (signature))
507- THROW (" TSL %s Signature is invalid" , territory (). data ( ));
504+ THROW (" TSL %.* s Signature is invalid" , STR_VIEW_FMT ( territory ()));
508505}
509506
510507void TSL::validate (const vector<X509Cert> &certs, int recursion) const
@@ -521,7 +518,7 @@ void TSL::validate(const vector<X509Cert> &certs, int recursion) const
521518
522519 vector<string> urls = pivotURLs ();
523520 if (urls.empty ())
524- THROW (" TSL %s Signature is signed with untrusted certificate" , territory (). data ( ));
521+ THROW (" TSL %.* s Signature is signed with untrusted certificate" , STR_VIEW_FMT ( territory ()));
525522
526523 // https://ec.europa.eu/tools/lotl/pivot-lotl-explanation.html
527524 string path = File::path (CONF (TSLCache), File::fileName (urls[0 ]));
@@ -604,6 +601,6 @@ bool TSL::validateRemoteDigest(const string &url)
604601 sha.update (is);
605602
606603 if (!digest.empty () && digest != sha.result ())
607- THROW (" TSL %s remote digest does not match local. TSL might be outdated" , territory (). data ( ));
604+ THROW (" TSL %.* s remote digest does not match local. TSL might be outdated" , STR_VIEW_FMT ( territory ()));
608605 return true ;
609606}
0 commit comments