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