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