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