libzypp  17.31.22
metalinkinfo_p.cc
Go to the documentation of this file.
1 /*---------------------------------------------------------------------\
2 | ____ _ __ __ ___ |
3 | |__ / \ / / . \ . \ |
4 | / / \ V /| _/ _/ |
5 | / /__ | | | | | | |
6 | /_____||_| |_| |_| |
7 | |
8 ----------------------------------------------------------------------*/
9 
14 
15 #include "metalinkinfo_p.h"
16 
17 namespace zyppng {
18 
19  namespace {
20 
21  constexpr auto minMetalinkProbeSize = 256; //< The maximum probe size we download before we decide we really got no metalink file
22 
23  MetaDataType looks_like_meta_data( const std::vector<char> &data )
24  {
25  if ( data.empty() )
26  return MetaDataType::None;
27 
28  const char *p = data.data();
29  while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')
30  p++;
31 
32  // If we have a zsync file, it has to start with zsync:
33  if ( !strncasecmp( p, "zsync:", 6 ) ) {
34  return MetaDataType::Zsync;
35  }
36 
37  if (!strncasecmp(p, "<?xml", 5))
38  {
39  while (*p && *p != '>')
40  p++;
41  if (*p == '>')
42  p++;
43  while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')
44  p++;
45  }
46  bool ret = !strncasecmp( p, "<metalink", 9 ) ? true : false;
47  if ( ret )
49 
50  return MetaDataType::None;
51  }
52 
53  MetaDataType looks_like_meta_file( const zypp::Pathname &file )
54  {
55  std::unique_ptr<FILE, decltype(&fclose)> fd( fopen( file.c_str(), "r" ), &fclose );
56  if ( !fd )
57  return MetaDataType::None;
58  return looks_like_meta_data( zyppng::peek_data_fd( fd.get(), 0, minMetalinkProbeSize ) );
59  }
60  }
61 
63  : BasicDownloaderStateBase( parent )
64  {
65  MIL << "Downloading metalink/zsync on " << parent._spec.url() << std::endl;
66  }
67 
68  DlMetaLinkInfoState::DlMetaLinkInfoState(std::shared_ptr<Request> &&prevRequest, DownloadPrivate &parent)
69  : BasicDownloaderStateBase( std::move(prevRequest), parent )
70  {
71  MIL << "Downloading metalink/zsync on " << parent._spec.url() << std::endl;
72  }
73 
74  std::shared_ptr<FinishedState> DlMetaLinkInfoState::transitionToFinished()
75  {
76  MIL << "Downloading on " << stateMachine()._spec.url() << " transition to final state. " << std::endl;
77  return std::make_shared<FinishedState>( std::move(_error), stateMachine() );
78  }
79 
80  std::shared_ptr<PrepareMultiState> DlMetaLinkInfoState::transitionToPrepareMulti()
81  {
82  _request->disconnectSignals();
84  auto nState = std::make_shared<PrepareMultiState>( std::move( _request ), prepareMode, stateMachine() );
85  _request = nullptr;
86  return nState;
87  }
88 
89  bool DlMetaLinkInfoState::initializeRequest(std::shared_ptr<Request> &r )
90  {
91  MIL << "Requesting Metadata info from server!" << std::endl;
92  r->transferSettings().addHeader("Accept: */*, application/x-zsync, application/metalink+xml, application/metalink4+xml");
94  }
95 
97  {
98  // some proxies do not store the content type, so also look at the file to find
99  // out if we received a metalink (bnc#649925)
101  _detectedMetaType = looks_like_meta_file( _request->targetFilePath() );
103  // Move to finished state
104  MIL << "Downloading on " << stateMachine()._spec.url() << " was successful, no metalink/zsync data. " << std::endl;
106  }
107 
108  auto &sm = stateMachine();
109  if ( sm._stopOnMetalink ) {
110  MIL << "Stopping after receiving MetaData as requested" << std::endl;
111  sm._stoppedOnMetalink = true;
113  }
114 
115  // Move to Prepare Multi state
117  MIL << "Downloading on " << sm._spec.url() << " returned a Zsync file " << std::endl;
118  else
119  MIL << "Downloading on " << sm._spec.url() << " returned a Metalink file" << std::endl;
120  _sigGotMetadata.emit();
121  }
122 
123  void DlMetaLinkInfoState::handleRequestProgress(NetworkRequest &req, off_t dltotal, off_t dlnow)
124  {
125  auto &sm = stateMachine();
126 
127  if ( _detectedMetaType == MetaDataType::None && dlnow < minMetalinkProbeSize ) {
128  // can't tell yet, ...
129  return sm._sigAlive.emit( *sm.z_func(), dlnow );
130  }
131 
133  std::string cType = req.contentType();
134  if ( cType.find("application/x-zsync") == 0 )
136  else if ( cType.find("application/metalink+xml") == 0 || cType.find("application/metalink4+xml") == 0 )
138  }
139 
141  _detectedMetaType = looks_like_meta_data( req.peekData( 0, minMetalinkProbeSize ) );
142  }
143 
145  // this is a metalink file change the expected filesize
147  WAR << "Metadata file exceeds 2MB in filesize, aborting."<<std::endl;
148  sm._requestDispatcher->cancel( req, NetworkRequestErrorPrivate::customError( NetworkRequestError::ExceededMaxLen ) );
149  return;
150  }
151 
152  return sm._sigAlive.emit( *sm.z_func(), dlnow );
153 
154  } else {
155  // still no metalink, we assume a normal download, not perfect though
156  if ( !_fallbackMilWritten ) {
157  _fallbackMilWritten = true;
158  MIL << "No Metalink file detected after " << minMetalinkProbeSize << ", falling back to normal progress updates" << std::endl;
159  }
160  return BasicDownloaderStateBase::handleRequestProgress( req, dltotal, dlnow );
161  }
162  }
163 
164 
165 }
#define MIL
Definition: Logger.h:96
DownloadSpec _spec
Definition: base_p.h:98
Store and operate with byte count.
Definition: ByteCount.h:30
Signal< void() > _sigGotMetadata
zypp::ByteCount downloadedByteCount() const
Returns the number of already downloaded bytes as reported by the backend.
Definition: request.cc:1304
const char * c_str() const
String representation.
Definition: Pathname.h:110
Definition: Arch.h:360
static const Unit MB
1000^2 Byte
Definition: ByteCount.h:60
virtual void gotFinished() override
#define WAR
Definition: Logger.h:97
std::vector< char > peekData(off_t offset, size_t count) const
Definition: request.cc:1231
std::shared_ptr< Request > _request
virtual void handleRequestProgress(NetworkRequest &req, off_t dltotal, off_t dlnow)
std::shared_ptr< FinishedState > transitionToFinished()
std::shared_ptr< PrepareMultiState > transitionToPrepareMulti()
virtual bool initializeRequest(std::shared_ptr< Request > &r)
std::vector< char > peek_data_fd(FILE *fd, off_t offset, size_t count)
Definition: request.cc:57
std::string contentType() const
Returns the content type as reported from the server.
Definition: request.cc:1282
bool initializeRequest(std::shared_ptr< Request > &r) override
DlMetaLinkInfoState(DownloadPrivate &parent)
static zyppng::NetworkRequestError customError(NetworkRequestError::Type t, std::string &&errorMsg="", std::map< std::string, boost::any > &&extraInfo={})
const Url & url() const
Definition: downloadspec.cc:50
virtual void handleRequestProgress(NetworkRequest &req, off_t dltotal, off_t dlnow) override