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