]> git.saurik.com Git - apt.git/blob - apt-pkg/acquire-method.cc
CMake: test/libapt: Use a prebuilt GTest library if available
[apt.git] / apt-pkg / acquire-method.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: acquire-method.cc,v 1.27.2.1 2003/12/24 23:09:17 mdz Exp $
4 /* ######################################################################
5
6 Acquire Method
7
8 This is a skeleton class that implements most of the functionality
9 of a method and some useful functions to make method implementation
10 simpler. The methods all derive this and specialize it. The most
11 complex implementation is the http method which needs to provide
12 pipelining, it runs the message engine at the same time it is
13 downloading files..
14
15 ##################################################################### */
16 /*}}}*/
17 // Include Files /*{{{*/
18 #include <config.h>
19
20 #include <apt-pkg/acquire-method.h>
21 #include <apt-pkg/error.h>
22 #include <apt-pkg/configuration.h>
23 #include <apt-pkg/strutl.h>
24 #include <apt-pkg/fileutl.h>
25 #include <apt-pkg/hashes.h>
26 #include <apt-pkg/md5.h>
27 #include <apt-pkg/sha1.h>
28 #include <apt-pkg/sha2.h>
29
30 #include <stdarg.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <string>
35 #include <vector>
36 #include <iostream>
37 #include <stdio.h>
38 /*}}}*/
39
40 using namespace std;
41
42 // AcqMethod::pkgAcqMethod - Constructor /*{{{*/
43 // ---------------------------------------------------------------------
44 /* This constructs the initialization text */
45 pkgAcqMethod::pkgAcqMethod(const char *Ver,unsigned long Flags)
46 {
47 std::cout << "100 Capabilities\n"
48 << "Version: " << Ver << "\n";
49
50 if ((Flags & SingleInstance) == SingleInstance)
51 std::cout << "Single-Instance: true\n";
52
53 if ((Flags & Pipeline) == Pipeline)
54 std::cout << "Pipeline: true\n";
55
56 if ((Flags & SendConfig) == SendConfig)
57 std::cout << "Send-Config: true\n";
58
59 if ((Flags & LocalOnly) == LocalOnly)
60 std::cout <<"Local-Only: true\n";
61
62 if ((Flags & NeedsCleanup) == NeedsCleanup)
63 std::cout << "Needs-Cleanup: true\n";
64
65 if ((Flags & Removable) == Removable)
66 std::cout << "Removable: true\n";
67
68 std::cout << "\n" << std::flush;
69
70 SetNonBlock(STDIN_FILENO,true);
71
72 Queue = 0;
73 QueueBack = 0;
74 }
75 /*}}}*/
76 // AcqMethod::Fail - A fetch has failed /*{{{*/
77 // ---------------------------------------------------------------------
78 /* */
79 void pkgAcqMethod::Fail(bool Transient)
80 {
81 string Err = "Undetermined Error";
82 if (_error->empty() == false)
83 {
84 Err.clear();
85 while (_error->empty() == false)
86 {
87 std::string msg;
88 if (_error->PopMessage(msg))
89 {
90 if (Err.empty() == false)
91 Err.append("\n");
92 Err.append(msg);
93 }
94 }
95 }
96 Fail(Err, Transient);
97 }
98 /*}}}*/
99 // AcqMethod::Fail - A fetch has failed /*{{{*/
100 // ---------------------------------------------------------------------
101 /* */
102 void pkgAcqMethod::Fail(string Err,bool Transient)
103 {
104 // Strip out junk from the error messages
105 for (string::iterator I = Err.begin(); I != Err.end(); ++I)
106 {
107 if (*I == '\r')
108 *I = ' ';
109 if (*I == '\n')
110 *I = ' ';
111 }
112
113 if (Queue != 0)
114 {
115 std::cout << "400 URI Failure\nURI: " << Queue->Uri << "\n"
116 << "Message: " << Err;
117 if (IP.empty() == false && _config->FindB("Acquire::Failure::ShowIP", true) == true)
118 std::cout << " " << IP;
119 std::cout << "\n";
120 Dequeue();
121 }
122 else
123 std::cout << "400 URI Failure\nURI: <UNKNOWN>\nMessage: " << Err << "\n";
124
125 if(FailReason.empty() == false)
126 std::cout << "FailReason: " << FailReason << "\n";
127 if (UsedMirror.empty() == false)
128 std::cout << "UsedMirror: " << UsedMirror << "\n";
129 // Set the transient flag
130 if (Transient == true)
131 std::cout << "Transient-Failure: true\n";
132
133 std::cout << "\n" << std::flush;
134 }
135 /*}}}*/
136 // AcqMethod::DropPrivsOrDie - Drop privileges or die /*{{{*/
137 // ---------------------------------------------------------------------
138 /* */
139 void pkgAcqMethod::DropPrivsOrDie()
140 {
141 if (!DropPrivileges()) {
142 Fail(false);
143 exit(112); /* call the european emergency number */
144 }
145 }
146
147 /*}}}*/
148 // AcqMethod::URIStart - Indicate a download is starting /*{{{*/
149 // ---------------------------------------------------------------------
150 /* */
151 void pkgAcqMethod::URIStart(FetchResult &Res)
152 {
153 if (Queue == 0)
154 abort();
155
156 std::cout << "200 URI Start\n"
157 << "URI: " << Queue->Uri << "\n";
158 if (Res.Size != 0)
159 std::cout << "Size: " << std::to_string(Res.Size) << "\n";
160
161 if (Res.LastModified != 0)
162 std::cout << "Last-Modified: " << TimeRFC1123(Res.LastModified, true) << "\n";
163
164 if (Res.ResumePoint != 0)
165 std::cout << "Resume-Point: " << std::to_string(Res.ResumePoint) << "\n";
166
167 if (UsedMirror.empty() == false)
168 std::cout << "UsedMirror: " << UsedMirror << "\n";
169
170 std::cout << "\n" << std::flush;
171 }
172 /*}}}*/
173 // AcqMethod::URIDone - A URI is finished /*{{{*/
174 // ---------------------------------------------------------------------
175 /* */
176 static void printHashStringList(HashStringList const * const list)
177 {
178 for (HashStringList::const_iterator hash = list->begin(); hash != list->end(); ++hash)
179 {
180 // very old compatibility name for MD5Sum
181 if (hash->HashType() == "MD5Sum")
182 std::cout << "MD5-Hash: " << hash->HashValue() << "\n";
183 std::cout << hash->HashType() << "-Hash: " << hash->HashValue() << "\n";
184 }
185 }
186 void pkgAcqMethod::URIDone(FetchResult &Res, FetchResult *Alt)
187 {
188 if (Queue == 0)
189 abort();
190
191 std::cout << "201 URI Done\n"
192 << "URI: " << Queue->Uri << "\n";
193
194 if (Res.Filename.empty() == false)
195 std::cout << "Filename: " << Res.Filename << "\n";
196
197 if (Res.Size != 0)
198 std::cout << "Size: " << std::to_string(Res.Size) << "\n";
199
200 if (Res.LastModified != 0)
201 std::cout << "Last-Modified: " << TimeRFC1123(Res.LastModified, true) << "\n";
202
203 printHashStringList(&Res.Hashes);
204
205 if (UsedMirror.empty() == false)
206 std::cout << "UsedMirror: " << UsedMirror << "\n";
207 if (Res.GPGVOutput.empty() == false)
208 {
209 std::cout << "GPGVOutput:\n";
210 for (vector<string>::const_iterator I = Res.GPGVOutput.begin();
211 I != Res.GPGVOutput.end(); ++I)
212 std::cout << " " << *I << "\n";
213 }
214
215 if (Res.ResumePoint != 0)
216 std::cout << "Resume-Point: " << std::to_string(Res.ResumePoint) << "\n";
217
218 if (Res.IMSHit == true)
219 std::cout << "IMS-Hit: true\n";
220
221 if (Alt != 0)
222 {
223 if (Alt->Filename.empty() == false)
224 std::cout << "Alt-Filename: " << Alt->Filename << "\n";
225
226 if (Alt->Size != 0)
227 std::cout << "Alt-Size: " << std::to_string(Alt->Size) << "\n";
228
229 if (Alt->LastModified != 0)
230 std::cout << "Alt-Last-Modified: " << TimeRFC1123(Alt->LastModified, true) << "\n";
231
232 printHashStringList(&Alt->Hashes);
233
234 if (Alt->IMSHit == true)
235 std::cout << "Alt-IMS-Hit: true\n";
236 }
237
238 std::cout << "\n" << std::flush;
239 Dequeue();
240 }
241 /*}}}*/
242 // AcqMethod::MediaFail - Syncronous request for new media /*{{{*/
243 // ---------------------------------------------------------------------
244 /* This sends a 403 Media Failure message to the APT and waits for it
245 to be ackd */
246 bool pkgAcqMethod::MediaFail(string Required,string Drive)
247 {
248 fprintf(stdout, "403 Media Failure\nMedia: %s\nDrive: %s\n",
249 Required.c_str(),Drive.c_str());
250 std::cout << "\n" << std::flush;
251
252 vector<string> MyMessages;
253
254 /* Here we read messages until we find a 603, each non 603 message is
255 appended to the main message list for later processing */
256 while (1)
257 {
258 if (WaitFd(STDIN_FILENO) == false)
259 return false;
260
261 if (ReadMessages(STDIN_FILENO,MyMessages) == false)
262 return false;
263
264 string Message = MyMessages.front();
265 MyMessages.erase(MyMessages.begin());
266
267 // Fetch the message number
268 char *End;
269 int Number = strtol(Message.c_str(),&End,10);
270 if (End == Message.c_str())
271 {
272 cerr << "Malformed message!" << endl;
273 exit(100);
274 }
275
276 // Change ack
277 if (Number == 603)
278 {
279 while (MyMessages.empty() == false)
280 {
281 Messages.push_back(MyMessages.front());
282 MyMessages.erase(MyMessages.begin());
283 }
284
285 return !StringToBool(LookupTag(Message,"Failed"),false);
286 }
287
288 Messages.push_back(Message);
289 }
290 }
291 /*}}}*/
292 // AcqMethod::Configuration - Handle the configuration message /*{{{*/
293 // ---------------------------------------------------------------------
294 /* This parses each configuration entry and puts it into the _config
295 Configuration class. */
296 bool pkgAcqMethod::Configuration(string Message)
297 {
298 ::Configuration &Cnf = *_config;
299
300 const char *I = Message.c_str();
301 const char *MsgEnd = I + Message.length();
302
303 unsigned int Length = strlen("Config-Item");
304 for (; I + Length < MsgEnd; I++)
305 {
306 // Not a config item
307 if (I[Length] != ':' || stringcasecmp(I,I+Length,"Config-Item") != 0)
308 continue;
309
310 I += Length + 1;
311
312 for (; I < MsgEnd && *I == ' '; I++);
313 const char *Equals = (const char*) memchr(I, '=', MsgEnd - I);
314 if (Equals == NULL)
315 return false;
316 const char *End = (const char*) memchr(Equals, '\n', MsgEnd - Equals);
317 if (End == NULL)
318 End = MsgEnd;
319
320 Cnf.Set(DeQuoteString(string(I,Equals-I)),
321 DeQuoteString(string(Equals+1,End-Equals-1)));
322 I = End;
323 }
324
325 return true;
326 }
327 /*}}}*/
328 // AcqMethod::Run - Run the message engine /*{{{*/
329 // ---------------------------------------------------------------------
330 /* Fetch any messages and execute them. In single mode it returns 1 if
331 there are no more available messages - any other result is a
332 fatal failure code! */
333 int pkgAcqMethod::Run(bool Single)
334 {
335 while (1)
336 {
337 // Block if the message queue is empty
338 if (Messages.empty() == true)
339 {
340 if (Single == false)
341 if (WaitFd(STDIN_FILENO) == false)
342 break;
343 if (ReadMessages(STDIN_FILENO,Messages) == false)
344 break;
345 }
346
347 // Single mode exits if the message queue is empty
348 if (Single == true && Messages.empty() == true)
349 return -1;
350
351 string Message = Messages.front();
352 Messages.erase(Messages.begin());
353
354 // Fetch the message number
355 char *End;
356 int Number = strtol(Message.c_str(),&End,10);
357 if (End == Message.c_str())
358 {
359 cerr << "Malformed message!" << endl;
360 return 100;
361 }
362
363 switch (Number)
364 {
365 case 601:
366 if (Configuration(Message) == false)
367 return 100;
368 break;
369
370 case 600:
371 {
372 FetchItem *Tmp = new FetchItem;
373
374 Tmp->Uri = LookupTag(Message,"URI");
375 Tmp->DestFile = LookupTag(Message,"FileName");
376 if (RFC1123StrToTime(LookupTag(Message,"Last-Modified").c_str(),Tmp->LastModified) == false)
377 Tmp->LastModified = 0;
378 Tmp->IndexFile = StringToBool(LookupTag(Message,"Index-File"),false);
379 Tmp->FailIgnore = StringToBool(LookupTag(Message,"Fail-Ignore"),false);
380 Tmp->ExpectedHashes = HashStringList();
381 for (char const * const * t = HashString::SupportedHashes(); *t != NULL; ++t)
382 {
383 std::string tag = "Expected-";
384 tag.append(*t);
385 std::string const hash = LookupTag(Message, tag.c_str());
386 if (hash.empty() == false)
387 Tmp->ExpectedHashes.push_back(HashString(*t, hash));
388 }
389 char *End;
390 if (Tmp->ExpectedHashes.FileSize() > 0)
391 Tmp->MaximumSize = Tmp->ExpectedHashes.FileSize();
392 else
393 Tmp->MaximumSize = strtoll(LookupTag(Message, "Maximum-Size", "0").c_str(), &End, 10);
394 Tmp->Next = 0;
395
396 // Append it to the list
397 FetchItem **I = &Queue;
398 for (; *I != 0; I = &(*I)->Next);
399 *I = Tmp;
400 if (QueueBack == 0)
401 QueueBack = Tmp;
402
403 // Notify that this item is to be fetched.
404 if (URIAcquire(Message, Tmp) == false)
405 Fail();
406
407 break;
408 }
409 }
410 }
411
412 Exit();
413 return 0;
414 }
415 /*}}}*/
416 // AcqMethod::PrintStatus - privately really send a log/status message /*{{{*/
417 void pkgAcqMethod::PrintStatus(char const * const header, const char* Format,
418 va_list &args) const
419 {
420 string CurrentURI = "<UNKNOWN>";
421 if (Queue != 0)
422 CurrentURI = Queue->Uri;
423 if (UsedMirror.empty() == true)
424 fprintf(stdout, "%s\nURI: %s\nMessage: ",
425 header, CurrentURI.c_str());
426 else
427 fprintf(stdout, "%s\nURI: %s\nUsedMirror: %s\nMessage: ",
428 header, CurrentURI.c_str(), UsedMirror.c_str());
429 vfprintf(stdout,Format,args);
430 std::cout << "\n\n" << std::flush;
431 }
432 /*}}}*/
433 // AcqMethod::Log - Send a log message /*{{{*/
434 // ---------------------------------------------------------------------
435 /* */
436 void pkgAcqMethod::Log(const char *Format,...)
437 {
438 va_list args;
439 va_start(args,Format);
440 PrintStatus("101 Log", Format, args);
441 va_end(args);
442 }
443 /*}}}*/
444 // AcqMethod::Status - Send a status message /*{{{*/
445 // ---------------------------------------------------------------------
446 /* */
447 void pkgAcqMethod::Status(const char *Format,...)
448 {
449 va_list args;
450 va_start(args,Format);
451 PrintStatus("102 Status", Format, args);
452 va_end(args);
453 }
454 /*}}}*/
455 // AcqMethod::Redirect - Send a redirect message /*{{{*/
456 // ---------------------------------------------------------------------
457 /* This method sends the redirect message and dequeues the item as
458 * the worker will enqueue again later on to the right queue */
459 void pkgAcqMethod::Redirect(const string &NewURI)
460 {
461 std::cout << "103 Redirect\nURI: " << Queue->Uri << "\n"
462 << "New-URI: " << NewURI << "\n"
463 << "\n" << std::flush;
464 Dequeue();
465 }
466 /*}}}*/
467 // AcqMethod::FetchResult::FetchResult - Constructor /*{{{*/
468 // ---------------------------------------------------------------------
469 /* */
470 pkgAcqMethod::FetchResult::FetchResult() : LastModified(0),
471 IMSHit(false), Size(0), ResumePoint(0), d(NULL)
472 {
473 }
474 /*}}}*/
475 // AcqMethod::FetchResult::TakeHashes - Load hashes /*{{{*/
476 // ---------------------------------------------------------------------
477 /* This hides the number of hashes we are supporting from the caller.
478 It just deals with the hash class. */
479 void pkgAcqMethod::FetchResult::TakeHashes(class Hashes &Hash)
480 {
481 Hashes = Hash.GetHashStringList();
482 }
483 /*}}}*/
484 void pkgAcqMethod::Dequeue() { /*{{{*/
485 FetchItem const * const Tmp = Queue;
486 Queue = Queue->Next;
487 if (Tmp == QueueBack)
488 QueueBack = Queue;
489 delete Tmp;
490 }
491 /*}}}*/
492 pkgAcqMethod::~pkgAcqMethod() {}
493
494 pkgAcqMethod::FetchItem::FetchItem() :
495 Next(nullptr), DestFileFd(-1), LastModified(0), IndexFile(false),
496 FailIgnore(false), MaximumSize(0), d(nullptr)
497 {}
498 pkgAcqMethod::FetchItem::~FetchItem() {}
499
500 pkgAcqMethod::FetchResult::~FetchResult() {}