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