58d214068ec3d1d86cfb21c2e470a2d4715ad158
[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->Next = 0;
355
356 // Append it to the list
357 FetchItem **I = &Queue;
358 for (; *I != 0; I = &(*I)->Next);
359 *I = Tmp;
360 if (QueueBack == 0)
361 QueueBack = Tmp;
362
363 // Notify that this item is to be fetched.
364 if (Fetch(Tmp) == false)
365 Fail();
366
367 break;
368 }
369 }
370 }
371
372 Exit();
373 return 0;
374 }
375 /*}}}*/
376 // AcqMethod::PrintStatus - privately really send a log/status message /*{{{*/
377 // ---------------------------------------------------------------------
378 /* */
379 void pkgAcqMethod::PrintStatus(char const * const header, const char* Format,
380 va_list &args) const
381 {
382 string CurrentURI = "<UNKNOWN>";
383 if (Queue != 0)
384 CurrentURI = Queue->Uri;
385 if (UsedMirror.empty() == true)
386 fprintf(stdout, "%s\nURI: %s\nMessage: ",
387 header, CurrentURI.c_str());
388 else
389 fprintf(stdout, "%s\nURI: %s\nUsedMirror: %s\nMessage: ",
390 header, CurrentURI.c_str(), UsedMirror.c_str());
391 vfprintf(stdout,Format,args);
392 std::cout << "\n\n" << std::flush;
393 }
394 /*}}}*/
395 // AcqMethod::Log - Send a log message /*{{{*/
396 // ---------------------------------------------------------------------
397 /* */
398 void pkgAcqMethod::Log(const char *Format,...)
399 {
400 va_list args;
401 va_start(args,Format);
402 PrintStatus("101 Log", Format, args);
403 va_end(args);
404 }
405 /*}}}*/
406 // AcqMethod::Status - Send a status message /*{{{*/
407 // ---------------------------------------------------------------------
408 /* */
409 void pkgAcqMethod::Status(const char *Format,...)
410 {
411 va_list args;
412 va_start(args,Format);
413 PrintStatus("102 Status", Format, args);
414 va_end(args);
415 }
416 /*}}}*/
417 // AcqMethod::Redirect - Send a redirect message /*{{{*/
418 // ---------------------------------------------------------------------
419 /* This method sends the redirect message and dequeues the item as
420 * the worker will enqueue again later on to the right queue */
421 void pkgAcqMethod::Redirect(const string &NewURI)
422 {
423 std::cout << "103 Redirect\nURI: " << Queue->Uri << "\n"
424 << "New-URI: " << NewURI << "\n"
425 << "\n" << std::flush;
426 Dequeue();
427 }
428 /*}}}*/
429 // AcqMethod::FetchResult::FetchResult - Constructor /*{{{*/
430 // ---------------------------------------------------------------------
431 /* */
432 pkgAcqMethod::FetchResult::FetchResult() : LastModified(0),
433 IMSHit(false), Size(0), ResumePoint(0)
434 {
435 }
436 /*}}}*/
437 // AcqMethod::FetchResult::TakeHashes - Load hashes /*{{{*/
438 // ---------------------------------------------------------------------
439 /* This hides the number of hashes we are supporting from the caller.
440 It just deals with the hash class. */
441 void pkgAcqMethod::FetchResult::TakeHashes(class Hashes &Hash)
442 {
443 Hashes = Hash.GetHashStringList();
444 }
445 /*}}}*/
446 void pkgAcqMethod::Dequeue() { /*{{{*/
447 FetchItem const * const Tmp = Queue;
448 Queue = Queue->Next;
449 if (Tmp == QueueBack)
450 QueueBack = Queue;
451 delete Tmp;
452 }
453 /*}}}*/