]> git.saurik.com Git - apt.git/blob - apt-pkg/acquire-method.cc
better non-virtual metaIndex.LocalFileName() implementation
[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 _error->PopMessage(Err);
84 _error->Discard();
85 Fail(Err,Transient);
86 }
87 /*}}}*/
88 // AcqMethod::Fail - A fetch has failed /*{{{*/
89 // ---------------------------------------------------------------------
90 /* */
91 void pkgAcqMethod::Fail(string Err,bool Transient)
92 {
93 // Strip out junk from the error messages
94 for (string::iterator I = Err.begin(); I != Err.end(); ++I)
95 {
96 if (*I == '\r')
97 *I = ' ';
98 if (*I == '\n')
99 *I = ' ';
100 }
101
102 if (Queue != 0)
103 {
104 std::cout << "400 URI Failure\nURI: " << Queue->Uri << "\n"
105 << "Message: " << Err;
106 if (IP.empty() == false && _config->FindB("Acquire::Failure::ShowIP", true) == true)
107 std::cout << " " << IP;
108 std::cout << "\n";
109 Dequeue();
110 }
111 else
112 std::cout << "400 URI Failure\nURI: <UNKNOWN>\nMessage: " << Err << "\n";
113
114 if(FailReason.empty() == false)
115 std::cout << "FailReason: " << FailReason << "\n";
116 if (UsedMirror.empty() == false)
117 std::cout << "UsedMirror: " << UsedMirror << "\n";
118 // Set the transient flag
119 if (Transient == true)
120 std::cout << "Transient-Failure: true\n";
121
122 std::cout << "\n" << std::flush;
123 }
124 /*}}}*/
125 // AcqMethod::DropPrivsOrDie - Drop privileges or die /*{{{*/
126 // ---------------------------------------------------------------------
127 /* */
128 void pkgAcqMethod::DropPrivsOrDie()
129 {
130 if (!DropPrivileges()) {
131 Fail(false);
132 exit(112); /* call the european emergency number */
133 }
134 }
135
136 /*}}}*/
137 // AcqMethod::URIStart - Indicate a download is starting /*{{{*/
138 // ---------------------------------------------------------------------
139 /* */
140 void pkgAcqMethod::URIStart(FetchResult &Res)
141 {
142 if (Queue == 0)
143 abort();
144
145 std::cout << "200 URI Start\n"
146 << "URI: " << Queue->Uri << "\n";
147 if (Res.Size != 0)
148 std::cout << "Size: " << Res.Size << "\n";
149
150 if (Res.LastModified != 0)
151 std::cout << "Last-Modified: " << TimeRFC1123(Res.LastModified) << "\n";
152
153 if (Res.ResumePoint != 0)
154 std::cout << "Resume-Point: " << Res.ResumePoint << "\n";
155
156 if (UsedMirror.empty() == false)
157 std::cout << "UsedMirror: " << UsedMirror << "\n";
158
159 std::cout << "\n" << std::flush;
160 }
161 /*}}}*/
162 // AcqMethod::URIDone - A URI is finished /*{{{*/
163 // ---------------------------------------------------------------------
164 /* */
165 static void printHashStringList(HashStringList const * const list)
166 {
167 for (HashStringList::const_iterator hash = list->begin(); hash != list->end(); ++hash)
168 {
169 // very old compatibility name for MD5Sum
170 if (hash->HashType() == "MD5Sum")
171 std::cout << "MD5-Hash: " << hash->HashValue() << "\n";
172 std::cout << hash->HashType() << "-Hash: " << hash->HashValue() << "\n";
173 }
174 }
175 void pkgAcqMethod::URIDone(FetchResult &Res, FetchResult *Alt)
176 {
177 if (Queue == 0)
178 abort();
179
180 std::cout << "201 URI Done\n"
181 << "URI: " << Queue->Uri << "\n";
182
183 if (Res.Filename.empty() == false)
184 std::cout << "Filename: " << Res.Filename << "\n";
185
186 if (Res.Size != 0)
187 std::cout << "Size: " << Res.Size << "\n";
188
189 if (Res.LastModified != 0)
190 std::cout << "Last-Modified: " << TimeRFC1123(Res.LastModified) << "\n";
191
192 printHashStringList(&Res.Hashes);
193
194 if (UsedMirror.empty() == false)
195 std::cout << "UsedMirror: " << UsedMirror << "\n";
196 if (Res.GPGVOutput.empty() == false)
197 {
198 std::cout << "GPGVOutput:\n";
199 for (vector<string>::const_iterator I = Res.GPGVOutput.begin();
200 I != Res.GPGVOutput.end(); ++I)
201 std::cout << " " << *I << "\n";
202 }
203
204 if (Res.ResumePoint != 0)
205 std::cout << "Resume-Point: " << Res.ResumePoint << "\n";
206
207 if (Res.IMSHit == true)
208 std::cout << "IMS-Hit: true\n";
209
210 if (Alt != 0)
211 {
212 if (Alt->Filename.empty() == false)
213 std::cout << "Alt-Filename: " << Alt->Filename << "\n";
214
215 if (Alt->Size != 0)
216 std::cout << "Alt-Size: " << Alt->Size << "\n";
217
218 if (Alt->LastModified != 0)
219 std::cout << "Alt-Last-Modified: " << TimeRFC1123(Alt->LastModified) << "\n";
220
221 printHashStringList(&Alt->Hashes);
222
223 if (Alt->IMSHit == true)
224 std::cout << "Alt-IMS-Hit: true\n";
225 }
226
227 std::cout << "\n" << std::flush;
228 Dequeue();
229 }
230 /*}}}*/
231 // AcqMethod::MediaFail - Syncronous request for new media /*{{{*/
232 // ---------------------------------------------------------------------
233 /* This sends a 403 Media Failure message to the APT and waits for it
234 to be ackd */
235 bool pkgAcqMethod::MediaFail(string Required,string Drive)
236 {
237 fprintf(stdout, "403 Media Failure\nMedia: %s\nDrive: %s\n",
238 Required.c_str(),Drive.c_str());
239 std::cout << "\n" << std::flush;
240
241 vector<string> MyMessages;
242
243 /* Here we read messages until we find a 603, each non 603 message is
244 appended to the main message list for later processing */
245 while (1)
246 {
247 if (WaitFd(STDIN_FILENO) == false)
248 return false;
249
250 if (ReadMessages(STDIN_FILENO,MyMessages) == false)
251 return false;
252
253 string Message = MyMessages.front();
254 MyMessages.erase(MyMessages.begin());
255
256 // Fetch the message number
257 char *End;
258 int Number = strtol(Message.c_str(),&End,10);
259 if (End == Message.c_str())
260 {
261 cerr << "Malformed message!" << endl;
262 exit(100);
263 }
264
265 // Change ack
266 if (Number == 603)
267 {
268 while (MyMessages.empty() == false)
269 {
270 Messages.push_back(MyMessages.front());
271 MyMessages.erase(MyMessages.begin());
272 }
273
274 return !StringToBool(LookupTag(Message,"Failed"),false);
275 }
276
277 Messages.push_back(Message);
278 }
279 }
280 /*}}}*/
281 // AcqMethod::Configuration - Handle the configuration message /*{{{*/
282 // ---------------------------------------------------------------------
283 /* This parses each configuration entry and puts it into the _config
284 Configuration class. */
285 bool pkgAcqMethod::Configuration(string Message)
286 {
287 ::Configuration &Cnf = *_config;
288
289 const char *I = Message.c_str();
290 const char *MsgEnd = I + Message.length();
291
292 unsigned int Length = strlen("Config-Item");
293 for (; I + Length < MsgEnd; I++)
294 {
295 // Not a config item
296 if (I[Length] != ':' || stringcasecmp(I,I+Length,"Config-Item") != 0)
297 continue;
298
299 I += Length + 1;
300
301 for (; I < MsgEnd && *I == ' '; I++);
302 const char *Equals = (const char*) memchr(I, '=', MsgEnd - I);
303 if (Equals == NULL)
304 return false;
305 const char *End = (const char*) memchr(Equals, '\n', MsgEnd - Equals);
306 if (End == NULL)
307 End = MsgEnd;
308
309 Cnf.Set(DeQuoteString(string(I,Equals-I)),
310 DeQuoteString(string(Equals+1,End-Equals-1)));
311 I = End;
312 }
313
314 return true;
315 }
316 /*}}}*/
317 // AcqMethod::Run - Run the message engine /*{{{*/
318 // ---------------------------------------------------------------------
319 /* Fetch any messages and execute them. In single mode it returns 1 if
320 there are no more available messages - any other result is a
321 fatal failure code! */
322 int pkgAcqMethod::Run(bool Single)
323 {
324 while (1)
325 {
326 // Block if the message queue is empty
327 if (Messages.empty() == true)
328 {
329 if (Single == false)
330 if (WaitFd(STDIN_FILENO) == false)
331 break;
332 if (ReadMessages(STDIN_FILENO,Messages) == false)
333 break;
334 }
335
336 // Single mode exits if the message queue is empty
337 if (Single == true && Messages.empty() == true)
338 return -1;
339
340 string Message = Messages.front();
341 Messages.erase(Messages.begin());
342
343 // Fetch the message number
344 char *End;
345 int Number = strtol(Message.c_str(),&End,10);
346 if (End == Message.c_str())
347 {
348 cerr << "Malformed message!" << endl;
349 return 100;
350 }
351
352 switch (Number)
353 {
354 case 601:
355 if (Configuration(Message) == false)
356 return 100;
357 break;
358
359 case 600:
360 {
361 FetchItem *Tmp = new FetchItem;
362
363 Tmp->Uri = LookupTag(Message,"URI");
364 Tmp->DestFile = LookupTag(Message,"FileName");
365 if (RFC1123StrToTime(LookupTag(Message,"Last-Modified").c_str(),Tmp->LastModified) == false)
366 Tmp->LastModified = 0;
367 Tmp->IndexFile = StringToBool(LookupTag(Message,"Index-File"),false);
368 Tmp->FailIgnore = StringToBool(LookupTag(Message,"Fail-Ignore"),false);
369 Tmp->ExpectedHashes = HashStringList();
370 for (char const * const * t = HashString::SupportedHashes(); *t != NULL; ++t)
371 {
372 std::string tag = "Expected-";
373 tag.append(*t);
374 std::string const hash = LookupTag(Message, tag.c_str());
375 if (hash.empty() == false)
376 Tmp->ExpectedHashes.push_back(HashString(*t, hash));
377 }
378 char *End;
379 Tmp->MaximumSize = strtoll(LookupTag(Message, "Maximum-Size", "0").c_str(), &End, 10);
380 Tmp->Next = 0;
381
382 // Append it to the list
383 FetchItem **I = &Queue;
384 for (; *I != 0; I = &(*I)->Next);
385 *I = Tmp;
386 if (QueueBack == 0)
387 QueueBack = Tmp;
388
389 // Notify that this item is to be fetched.
390 if (Fetch(Tmp) == false)
391 Fail();
392
393 break;
394 }
395 }
396 }
397
398 Exit();
399 return 0;
400 }
401 /*}}}*/
402 // AcqMethod::PrintStatus - privately really send a log/status message /*{{{*/
403 // ---------------------------------------------------------------------
404 /* */
405 void pkgAcqMethod::PrintStatus(char const * const header, const char* Format,
406 va_list &args) const
407 {
408 string CurrentURI = "<UNKNOWN>";
409 if (Queue != 0)
410 CurrentURI = Queue->Uri;
411 if (UsedMirror.empty() == true)
412 fprintf(stdout, "%s\nURI: %s\nMessage: ",
413 header, CurrentURI.c_str());
414 else
415 fprintf(stdout, "%s\nURI: %s\nUsedMirror: %s\nMessage: ",
416 header, CurrentURI.c_str(), UsedMirror.c_str());
417 vfprintf(stdout,Format,args);
418 std::cout << "\n\n" << std::flush;
419 }
420 /*}}}*/
421 // AcqMethod::Log - Send a log message /*{{{*/
422 // ---------------------------------------------------------------------
423 /* */
424 void pkgAcqMethod::Log(const char *Format,...)
425 {
426 va_list args;
427 va_start(args,Format);
428 PrintStatus("101 Log", Format, args);
429 va_end(args);
430 }
431 /*}}}*/
432 // AcqMethod::Status - Send a status message /*{{{*/
433 // ---------------------------------------------------------------------
434 /* */
435 void pkgAcqMethod::Status(const char *Format,...)
436 {
437 va_list args;
438 va_start(args,Format);
439 PrintStatus("102 Status", Format, args);
440 va_end(args);
441 }
442 /*}}}*/
443 // AcqMethod::Redirect - Send a redirect message /*{{{*/
444 // ---------------------------------------------------------------------
445 /* This method sends the redirect message and dequeues the item as
446 * the worker will enqueue again later on to the right queue */
447 void pkgAcqMethod::Redirect(const string &NewURI)
448 {
449 std::cout << "103 Redirect\nURI: " << Queue->Uri << "\n"
450 << "New-URI: " << NewURI << "\n"
451 << "\n" << std::flush;
452 Dequeue();
453 }
454 /*}}}*/
455 // AcqMethod::FetchResult::FetchResult - Constructor /*{{{*/
456 // ---------------------------------------------------------------------
457 /* */
458 pkgAcqMethod::FetchResult::FetchResult() : LastModified(0),
459 IMSHit(false), Size(0), ResumePoint(0)
460 {
461 }
462 /*}}}*/
463 // AcqMethod::FetchResult::TakeHashes - Load hashes /*{{{*/
464 // ---------------------------------------------------------------------
465 /* This hides the number of hashes we are supporting from the caller.
466 It just deals with the hash class. */
467 void pkgAcqMethod::FetchResult::TakeHashes(class Hashes &Hash)
468 {
469 Hashes = Hash.GetHashStringList();
470 }
471 /*}}}*/
472 void pkgAcqMethod::Dequeue() { /*{{{*/
473 FetchItem const * const Tmp = Queue;
474 Queue = Queue->Next;
475 if (Tmp == QueueBack)
476 QueueBack = Queue;
477 delete Tmp;
478 }
479 /*}}}*/
480
481 pkgAcqMethod::~pkgAcqMethod() {}