apt  2.2.4
About: Apt (Advanced Package Tool) is a management system for software packages (Debian/Ubuntu). Release series 2.2.
  Fossies Dox: apt-2.2.4.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

acquire-method.cc
Go to the documentation of this file.
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 /* ######################################################################
4 
5  Acquire Method
6 
7  This is a skeleton class that implements most of the functionality
8  of a method and some useful functions to make method implementation
9  simpler. The methods all derive this and specialize it. The most
10  complex implementation is the http method which needs to provide
11  pipelining, it runs the message engine at the same time it is
12  downloading files..
13 
14  ##################################################################### */
15  /*}}}*/
16 // Include Files /*{{{*/
17 #include <config.h>
18 
19 #include <apt-pkg/acquire-method.h>
20 #include <apt-pkg/configuration.h>
21 #include <apt-pkg/error.h>
22 #include <apt-pkg/fileutl.h>
23 #include <apt-pkg/hashes.h>
24 #include <apt-pkg/strutl.h>
25 
26 #include <algorithm>
27 #include <iostream>
28 #include <iterator>
29 #include <sstream>
30 #include <string>
31 #include <vector>
32 #include <stdarg.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
37  /*}}}*/
38 
39 using namespace std;
40 
41 // poor mans unordered_map::try_emplace for C++11 as it is a C++17 feature /*{{{*/
42 template <typename Arg>
43 static void try_emplace(std::unordered_map<std::string, std::string> &fields, std::string &&name, Arg &&value)
44 {
45  if (fields.find(name) == fields.end())
46  fields.emplace(std::move(name), std::forward<Arg>(value));
47 }
48  /*}}}*/
49 
50 // AcqMethod::pkgAcqMethod - Constructor /*{{{*/
51 // ---------------------------------------------------------------------
52 /* This constructs the initialization text */
53 pkgAcqMethod::pkgAcqMethod(const char *Ver,unsigned long Flags)
54 {
55  std::unordered_map<std::string, std::string> fields;
56  try_emplace(fields, "Version", Ver);
58  try_emplace(fields, "Single-Instance", "true");
59 
60  if ((Flags & Pipeline) == Pipeline)
61  try_emplace(fields, "Pipeline", "true");
62 
63  if ((Flags & SendConfig) == SendConfig)
64  try_emplace(fields, "Send-Config", "true");
65 
66  if ((Flags & LocalOnly) == LocalOnly)
67  try_emplace(fields, "Local-Only", "true");
68 
69  if ((Flags & NeedsCleanup) == NeedsCleanup)
70  try_emplace(fields, "Needs-Cleanup", "true");
71 
72  if ((Flags & Removable) == Removable)
73  try_emplace(fields, "Removable", "true");
74 
75  if ((Flags & AuxRequests) == AuxRequests)
76  try_emplace(fields, "AuxRequests", "true");
77 
79  try_emplace(fields, "Send-URI-Encoded", "true");
80 
81  SendMessage("100 Capabilities", std::move(fields));
82 
83  SetNonBlock(STDIN_FILENO,true);
84 
85  Queue = 0;
86  QueueBack = 0;
87 }
88  /*}}}*/
89 void pkgAcqMethod::SendMessage(std::string const &header, std::unordered_map<std::string, std::string> &&fields) /*{{{*/
90 {
91  auto CheckKey = [](std::string const &str) {
92  // Space, hyphen-minus, and alphanum are allowed for keys/headers.
93  return str.find_first_not_of(" -0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz") == std::string::npos;
94  };
95 
96  auto CheckValue = [](std::string const &str) {
97  return std::all_of(str.begin(), str.end(), [](unsigned char c) -> bool {
98  return c > 127 // unicode
99  || (c > 31 && c < 127) // printable chars
100  || c == '\n' || c == '\t'; // special whitespace
101  });
102  };
103 
104  auto Error = [this]() {
105  _error->Error("SECURITY: Message contains control characters, rejecting.");
106  _error->DumpErrors();
107  SendMessage("400 URI Failure", {{"URI", "<UNKNOWN>"}, {"Message", "SECURITY: Message contains control characters, rejecting."}});
108  abort();
109  };
110 
111  if (!CheckKey(header))
112  return Error();
113 
114  for (auto const &f : fields)
115  {
116  if (!CheckKey(f.first))
117  return Error();
118  if (!CheckValue(f.second))
119  return Error();
120  }
121 
122  std::cout << header << '\n';
123  for (auto const &f : fields)
124  {
125  if (f.second.empty())
126  continue;
127  std::cout << f.first << ": ";
128  auto const lines = VectorizeString(f.second, '\n');
129  if (likely(lines.empty() == false))
130  {
131  std::copy(lines.begin(), std::prev(lines.end()), std::ostream_iterator<std::string>(std::cout, "\n "));
132  std::cout << *lines.rbegin();
133  }
134  std::cout << '\n';
135  }
136  std::cout << '\n'
137  << std::flush;
138 }
139  /*}}}*/
140 // AcqMethod::Fail - A fetch has failed /*{{{*/
141 // ---------------------------------------------------------------------
142 /* */
143 void pkgAcqMethod::Fail(bool Transient)
144 {
145 
146  Fail("", Transient);
147 }
148  /*}}}*/
149 // AcqMethod::Fail - A fetch has failed /*{{{*/
150 void pkgAcqMethod::Fail(string Err, bool Transient)
151 {
152 
153  if (not _error->empty())
154  {
155  while (not _error->empty())
156  {
157  std::string msg;
158  if (_error->PopMessage(msg))
159  {
160  if (not Err.empty())
161  Err.append("\n");
162  Err.append(msg);
163  }
164  }
165  }
166  if (Err.empty())
167  Err = "Undetermined Error";
168 
169  // Strip out junk from the error messages
170  std::transform(Err.begin(), Err.end(), Err.begin(), [](char const c) {
171  if (c == '\r' || c == '\n')
172  return ' ';
173  return c;
174  });
175  if (IP.empty() == false && _config->FindB("Acquire::Failure::ShowIP", true) == true)
176  Err.append(" ").append(IP);
177 
178  std::unordered_map<std::string, std::string> fields;
179  if (Queue != nullptr)
180  try_emplace(fields, "URI", Queue->Uri);
181  else
182  try_emplace(fields, "URI", "<UNKNOWN>");
183  try_emplace(fields, "Message", Err);
184 
185  if(FailReason.empty() == false)
186  try_emplace(fields, "FailReason", FailReason);
187  if (UsedMirror.empty() == false)
188  try_emplace(fields, "UsedMirror", UsedMirror);
189  if (Transient == true)
190  try_emplace(fields, "Transient-Failure", "true");
191 
192  SendMessage("400 URI Failure", std::move(fields));
193 
194  if (Queue != nullptr)
195  Dequeue();
196 }
197  /*}}}*/
198 // AcqMethod::DropPrivsOrDie - Drop privileges or die /*{{{*/
199 // ---------------------------------------------------------------------
200 /* */
202 {
203  if (!DropPrivileges()) {
204  Fail(false);
205  exit(112); /* call the european emergency number */
206  }
207 }
208 
209  /*}}}*/
210 // AcqMethod::URIStart - Indicate a download is starting /*{{{*/
212 {
213  if (Queue == 0)
214  abort();
215 
216  std::unordered_map<std::string, std::string> fields;
217  try_emplace(fields, "URI", Queue->Uri);
218  if (Res.Size != 0)
219  try_emplace(fields, "Size", std::to_string(Res.Size));
220  if (Res.LastModified != 0)
221  try_emplace(fields, "Last-Modified", TimeRFC1123(Res.LastModified, true));
222  if (Res.ResumePoint != 0)
223  try_emplace(fields, "Resume-Point", std::to_string(Res.ResumePoint));
224  if (UsedMirror.empty() == false)
225  try_emplace(fields, "UsedMirror", UsedMirror);
226 
227  SendMessage("200 URI Start", std::move(fields));
228 }
229  /*}}}*/
230 // AcqMethod::URIDone - A URI is finished /*{{{*/
231 static void printHashStringList(std::unordered_map<std::string, std::string> &fields, std::string const &Prefix, HashStringList const &list)
232 {
233  for (auto const &hash : list)
234  {
235  // very old compatibility name for MD5Sum
236  if (hash.HashType() == "MD5Sum")
237  try_emplace(fields, Prefix + "MD5-Hash", hash.HashValue());
238  try_emplace(fields, Prefix + hash.HashType() + "-Hash", hash.HashValue());
239  }
240 }
242 {
243  if (Queue == 0)
244  abort();
245 
246  std::unordered_map<std::string, std::string> fields;
247  try_emplace(fields, "URI", Queue->Uri);
248  if (Res.Filename.empty() == false)
249  try_emplace(fields, "Filename", Res.Filename);
250  if (Res.Size != 0)
251  try_emplace(fields, "Size", std::to_string(Res.Size));
252  if (Res.LastModified != 0)
253  try_emplace(fields, "Last-Modified", TimeRFC1123(Res.LastModified, true));
254  printHashStringList(fields, "", Res.Hashes);
255 
256  if (UsedMirror.empty() == false)
257  try_emplace(fields, "UsedMirror", UsedMirror);
258  if (Res.GPGVOutput.empty() == false)
259  {
260  std::ostringstream os;
261  std::copy(Res.GPGVOutput.begin(), Res.GPGVOutput.end() - 1, std::ostream_iterator<std::string>(os, "\n"));
262  os << *Res.GPGVOutput.rbegin();
263  try_emplace(fields, "GPGVOutput", os.str());
264  }
265  if (Res.ResumePoint != 0)
266  try_emplace(fields, "Resume-Point", std::to_string(Res.ResumePoint));
267  if (Res.IMSHit == true)
268  try_emplace(fields, "IMS-Hit", "true");
269 
270  if (Alt != nullptr)
271  {
272  if (Alt->Filename.empty() == false)
273  try_emplace(fields, "Alt-Filename", Alt->Filename);
274  if (Alt->Size != 0)
275  try_emplace(fields, "Alt-Size", std::to_string(Alt->Size));
276  if (Alt->LastModified != 0)
277  try_emplace(fields, "Alt-Last-Modified", TimeRFC1123(Alt->LastModified, true));
278  if (Alt->IMSHit == true)
279  try_emplace(fields, "Alt-IMS-Hit", "true");
280  printHashStringList(fields, "Alt-", Alt->Hashes);
281  }
282 
283  SendMessage("201 URI Done", std::move(fields));
284  Dequeue();
285 }
286  /*}}}*/
287 // AcqMethod::MediaFail - Synchronous request for new media /*{{{*/
288 // ---------------------------------------------------------------------
289 /* This sends a 403 Media Failure message to the APT and waits for it
290  to be ackd */
291 bool pkgAcqMethod::MediaFail(string Required,string Drive)
292 {
293  fprintf(stdout, "403 Media Failure\nMedia: %s\nDrive: %s\n",
294  Required.c_str(),Drive.c_str());
295  std::cout << "\n" << std::flush;
296 
297  vector<string> MyMessages;
298 
299  /* Here we read messages until we find a 603, each non 603 message is
300  appended to the main message list for later processing */
301  while (1)
302  {
303  if (WaitFd(STDIN_FILENO) == false)
304  return false;
305 
306  if (ReadMessages(STDIN_FILENO,MyMessages) == false)
307  return false;
308 
309  string Message = MyMessages.front();
310  MyMessages.erase(MyMessages.begin());
311 
312  // Fetch the message number
313  char *End;
314  int Number = strtol(Message.c_str(),&End,10);
315  if (End == Message.c_str())
316  {
317  cerr << "Malformed message!" << endl;
318  exit(100);
319  }
320 
321  // Change ack
322  if (Number == 603)
323  {
324  while (MyMessages.empty() == false)
325  {
326  Messages.push_back(MyMessages.front());
327  MyMessages.erase(MyMessages.begin());
328  }
329 
330  return !StringToBool(LookupTag(Message,"Failed"),false);
331  }
332 
333  Messages.push_back(Message);
334  }
335 }
336  /*}}}*/
337 // AcqMethod::Configuration - Handle the configuration message /*{{{*/
338 // ---------------------------------------------------------------------
339 /* This parses each configuration entry and puts it into the _config
340  Configuration class. */
341 bool pkgAcqMethod::Configuration(string Message)
342 {
343  ::Configuration &Cnf = *_config;
344 
345  const char *I = Message.c_str();
346  const char *MsgEnd = I + Message.length();
347 
348  unsigned int Length = strlen("Config-Item");
349  for (; I + Length < MsgEnd; I++)
350  {
351  // Not a config item
352  if (I[Length] != ':' || stringcasecmp(I,I+Length,"Config-Item") != 0)
353  continue;
354 
355  I += Length + 1;
356 
357  for (; I < MsgEnd && *I == ' '; I++);
358  const char *Equals = (const char*) memchr(I, '=', MsgEnd - I);
359  if (Equals == NULL)
360  return false;
361  const char *End = (const char*) memchr(Equals, '\n', MsgEnd - Equals);
362  if (End == NULL)
363  End = MsgEnd;
364 
365  Cnf.Set(DeQuoteString(string(I,Equals-I)),
366  DeQuoteString(string(Equals+1,End-Equals-1)));
367  I = End;
368  }
369 
370  return true;
371 }
372  /*}}}*/
373 // AcqMethod::Run - Run the message engine /*{{{*/
374 // ---------------------------------------------------------------------
375 /* Fetch any messages and execute them. In single mode it returns 1 if
376  there are no more available messages - any other result is a
377  fatal failure code! */
378 int pkgAcqMethod::Run(bool Single)
379 {
380  while (1)
381  {
382  // Block if the message queue is empty
383  if (Messages.empty() == true)
384  {
385  if (Single == false)
386  if (WaitFd(STDIN_FILENO) == false)
387  break;
388  if (ReadMessages(STDIN_FILENO,Messages) == false)
389  break;
390  }
391 
392  // Single mode exits if the message queue is empty
393  if (Single == true && Messages.empty() == true)
394  return -1;
395 
396  string Message = Messages.front();
397  Messages.erase(Messages.begin());
398 
399  // Fetch the message number
400  char *End;
401  int Number = strtol(Message.c_str(),&End,10);
402  if (End == Message.c_str())
403  {
404  cerr << "Malformed message!" << endl;
405  return 100;
406  }
407 
408  switch (Number)
409  {
410  case 601:
411  if (Configuration(Message) == false)
412  return 100;
413  break;
414 
415  case 600:
416  {
417  FetchItem *Tmp = new FetchItem;
418 
419  Tmp->Uri = LookupTag(Message,"URI");
420  Tmp->Proxy(LookupTag(Message, "Proxy"));
421  Tmp->DestFile = LookupTag(Message,"FileName");
422  if (RFC1123StrToTime(LookupTag(Message,"Last-Modified"),Tmp->LastModified) == false)
423  Tmp->LastModified = 0;
424  Tmp->IndexFile = StringToBool(LookupTag(Message,"Index-File"),false);
425  Tmp->FailIgnore = StringToBool(LookupTag(Message,"Fail-Ignore"),false);
427  for (char const * const * t = HashString::SupportedHashes(); *t != NULL; ++t)
428  {
429  std::string tag = "Expected-";
430  tag.append(*t);
431  std::string const hash = LookupTag(Message, tag.c_str());
432  if (hash.empty() == false)
433  Tmp->ExpectedHashes.push_back(HashString(*t, hash));
434  }
435  char *End;
436  if (Tmp->ExpectedHashes.FileSize() > 0)
437  Tmp->MaximumSize = Tmp->ExpectedHashes.FileSize();
438  else
439  Tmp->MaximumSize = strtoll(LookupTag(Message, "Maximum-Size", "0").c_str(), &End, 10);
440  Tmp->Next = 0;
441 
442  // Append it to the list
443  FetchItem **I = &Queue;
444  for (; *I != 0; I = &(*I)->Next);
445  *I = Tmp;
446  if (QueueBack == 0)
447  QueueBack = Tmp;
448 
449  // Notify that this item is to be fetched.
450  if (URIAcquire(Message, Tmp) == false)
451  Fail();
452 
453  break;
454  }
455  }
456  }
457 
458  Exit();
459  return 0;
460 }
461  /*}}}*/
462 // AcqMethod::PrintStatus - privately really send a log/status message /*{{{*/
463 void pkgAcqMethod::PrintStatus(char const * const header, const char* Format,
464  va_list &args) const
465 {
466  string CurrentURI = "<UNKNOWN>";
467  if (Queue != 0)
468  CurrentURI = Queue->Uri;
469  if (UsedMirror.empty() == true)
470  fprintf(stdout, "%s\nURI: %s\nMessage: ",
471  header, CurrentURI.c_str());
472  else
473  fprintf(stdout, "%s\nURI: %s\nUsedMirror: %s\nMessage: ",
474  header, CurrentURI.c_str(), UsedMirror.c_str());
475  vfprintf(stdout,Format,args);
476  std::cout << "\n\n" << std::flush;
477 }
478  /*}}}*/
479 // AcqMethod::Log - Send a log message /*{{{*/
480 // ---------------------------------------------------------------------
481 /* */
482 void pkgAcqMethod::Log(const char *Format,...)
483 {
484  va_list args;
485  ssize_t size = 400;
486  std::ostringstream outstr;
487  while (true) {
488  bool ret;
489  va_start(args,Format);
490  ret = iovprintf(outstr, Format, args, size);
491  va_end(args);
492  if (ret == true)
493  break;
494  }
495  std::unordered_map<std::string, std::string> fields;
496  if (Queue != 0)
497  try_emplace(fields, "URI", Queue->Uri);
498  else
499  try_emplace(fields, "URI", "<UNKNOWN>");
500  if (not UsedMirror.empty())
501  try_emplace(fields, "UsedMirror", UsedMirror);
502  try_emplace(fields, "Message", outstr.str());
503  SendMessage("101 Log", std::move(fields));
504 }
505  /*}}}*/
506 // AcqMethod::Status - Send a status message /*{{{*/
507 // ---------------------------------------------------------------------
508 /* */
509 void pkgAcqMethod::Status(const char *Format,...)
510 {
511  va_list args;
512  ssize_t size = 400;
513  std::ostringstream outstr;
514  while (true) {
515  bool ret;
516  va_start(args,Format);
517  ret = iovprintf(outstr, Format, args, size);
518  va_end(args);
519  if (ret == true)
520  break;
521  }
522  std::unordered_map<std::string, std::string> fields;
523  if (Queue != 0)
524  try_emplace(fields, "URI", Queue->Uri);
525  else
526  try_emplace(fields, "URI", "<UNKNOWN>");
527  if (not UsedMirror.empty())
528  try_emplace(fields, "UsedMirror", UsedMirror);
529  try_emplace(fields, "Message", outstr.str());
530  SendMessage("102 Status", std::move(fields));
531 }
532  /*}}}*/
533 // AcqMethod::Redirect - Send a redirect message /*{{{*/
534 // ---------------------------------------------------------------------
535 /* This method sends the redirect message and dequeues the item as
536  * the worker will enqueue again later on to the right queue */
537 void pkgAcqMethod::Redirect(const string &NewURI)
538 {
539  if (NewURI.find_first_not_of(" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~") != std::string::npos)
540  {
541  _error->Error("SECURITY: URL redirect target contains control characters, rejecting.");
542  Fail();
543  return;
544  }
545  std::unordered_map<std::string, std::string> fields;
546  try_emplace(fields, "URI", Queue->Uri);
547  try_emplace(fields, "New-URI", NewURI);
548  SendMessage("103 Redirect", std::move(fields));
549  Dequeue();
550 }
551  /*}}}*/
552 // AcqMethod::FetchResult::FetchResult - Constructor /*{{{*/
553 // ---------------------------------------------------------------------
554 /* */
556  IMSHit(false), Size(0), ResumePoint(0), d(NULL)
557 {
558 }
559  /*}}}*/
560 // AcqMethod::FetchResult::TakeHashes - Load hashes /*{{{*/
561 // ---------------------------------------------------------------------
562 /* This hides the number of hashes we are supporting from the caller.
563  It just deals with the hash class. */
565 {
566  Hashes = Hash.GetHashStringList();
567 }
568  /*}}}*/
569 void pkgAcqMethod::Dequeue() { /*{{{*/
570  FetchItem const * const Tmp = Queue;
571  Queue = Queue->Next;
572  if (Tmp == QueueBack)
573  QueueBack = Queue;
574  delete Tmp;
575 }
576  /*}}}*/
578 
580 {
581  std::string Proxy;
582 };
583 
584 pkgAcqMethod::FetchItem::FetchItem() : Next(nullptr), DestFileFd(-1), LastModified(0), IndexFile(false),
585  FailIgnore(false), MaximumSize(0), d(new Private)
586 {}
587 
589 {
590  return d->Proxy;
591 }
592 
593 void pkgAcqMethod::FetchItem::Proxy(std::string const &Proxy)
594 {
595  d->Proxy = Proxy;
596 }
597 
599 
static bool std::string const metaIndex const *const pkgAcqMetaClearSig *const pkgAcquire::Item *const I
return false
static char const *const msg
static void try_emplace(std::unordered_map< std::string, std::string > &fields, std::string &&name, Arg &&value)
static void printHashStringList(std::unordered_map< std::string, std::string > &fields, std::string const &Prefix, HashStringList const &list)
Provide access methods to various configuration settings.
bool push_back(const HashString &hashString)
Definition: hashes.cc:232
unsigned long long FileSize() const
Definition: hashes.cc:210
static APT_PURE const char ** SupportedHashes()
Definition: hashes.cc:135
Definition: hashes.h:170
HashStringList GetHashStringList()
Definition: hashes.cc:431
std::string UsedMirror
virtual bool Configuration(std::string Message)
void Log(const char *Format,...)
FetchItem * QueueBack
virtual void Exit()
void DropPrivsOrDie()
void Status(const char *Format,...)
std::vector< std::string > Messages
virtual bool URIAcquire(std::string const &, FetchItem *Itm)
virtual void URIStart(FetchResult &Res)
std::string IP
APT_HIDDEN void Dequeue()
std::string FailReason
void Redirect(const std::string &NewURI)
bool MediaFail(std::string Required, std::string Drive)
FetchItem * Queue
virtual ~pkgAcqMethod()
pkgAcqMethod(const char *Ver, unsigned long Flags=0)
void SendMessage(std::string const &header, std::unordered_map< std::string, std::string > &&fields)
const char * Format
int Run(bool Single=false)
void Fail(bool Transient=false)
virtual void URIDone(FetchResult &Res, FetchResult *Alt=0)
Configuration * _config
void SetNonBlock(int Fd, bool Block)
Definition: fileutl.cc:804
bool WaitFd(int Fd, bool write, unsigned long timeout)
Definition: fileutl.cc:819
bool DropPrivileges()
Drop privileges.
Definition: fileutl.cc:3260
URI Proxy
Definition: ftp.cc:66
HashStringList ExpectedHashes
unsigned long long MaximumSize
unsigned long long Size
std::vector< std::string > GPGVOutput
void TakeHashes(class Hashes &Hash)
unsigned long long ResumePoint
int stringcasecmp(const char *A, const char *AEnd, const char *B, const char *BEnd)
Definition: strutl.cc:685
int StringToBool(const string &Text, int Default)
Definition: strutl.cc:820
bool RFC1123StrToTime(std::string const &str, time_t &time)
Definition: strutl.cc:1040
vector< string > VectorizeString(string const &haystack, char const &split)
Definition: strutl.cc:1308
string DeQuoteString(const string &Str)
Definition: strutl.cc:404
bool ReadMessages(int Fd, vector< string > &List)
Definition: strutl.cc:883
bool iovprintf(std::ostream &out, const char *format, va_list &args, ssize_t &size)
Definition: strutl.cc:1418
std::string LookupTag(const std::string &Message, const char *TagC, const char *Default)
Definition: strutl.cc:742
string TimeRFC1123(time_t Date, bool const NumericTimezone)
Definition: strutl.cc:853