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