]> git.saurik.com Git - apt.git/blob - apt-pkg/acquire-method.cc
Rename DropPrivs() to DropPrivileges()
[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 (!DropPrivileges()) {
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 Tmp->Next = 0;
376
377 // Append it to the list
378 FetchItem **I = &Queue;
379 for (; *I != 0; I = &(*I)->Next);
380 *I = Tmp;
381 if (QueueBack == 0)
382 QueueBack = Tmp;
383
384 // Notify that this item is to be fetched.
385 if (Fetch(Tmp) == false)
386 Fail();
387
388 break;
389 }
390 }
391 }
392
393 Exit();
394 return 0;
395 }
396 /*}}}*/
397 // AcqMethod::PrintStatus - privately really send a log/status message /*{{{*/
398 // ---------------------------------------------------------------------
399 /* */
400 void pkgAcqMethod::PrintStatus(char const * const header, const char* Format,
401 va_list &args) const
402 {
403 string CurrentURI = "<UNKNOWN>";
404 if (Queue != 0)
405 CurrentURI = Queue->Uri;
406 if (UsedMirror.empty() == true)
407 fprintf(stdout, "%s\nURI: %s\nMessage: ",
408 header, CurrentURI.c_str());
409 else
410 fprintf(stdout, "%s\nURI: %s\nUsedMirror: %s\nMessage: ",
411 header, CurrentURI.c_str(), UsedMirror.c_str());
412 vfprintf(stdout,Format,args);
413 std::cout << "\n\n" << std::flush;
414 }
415 /*}}}*/
416 // AcqMethod::Log - Send a log message /*{{{*/
417 // ---------------------------------------------------------------------
418 /* */
419 void pkgAcqMethod::Log(const char *Format,...)
420 {
421 va_list args;
422 va_start(args,Format);
423 PrintStatus("101 Log", Format, args);
424 va_end(args);
425 }
426 /*}}}*/
427 // AcqMethod::Status - Send a status message /*{{{*/
428 // ---------------------------------------------------------------------
429 /* */
430 void pkgAcqMethod::Status(const char *Format,...)
431 {
432 va_list args;
433 va_start(args,Format);
434 PrintStatus("102 Status", Format, args);
435 va_end(args);
436 }
437 /*}}}*/
438 // AcqMethod::Redirect - Send a redirect message /*{{{*/
439 // ---------------------------------------------------------------------
440 /* This method sends the redirect message and dequeues the item as
441 * the worker will enqueue again later on to the right queue */
442 void pkgAcqMethod::Redirect(const string &NewURI)
443 {
444 std::cout << "103 Redirect\nURI: " << Queue->Uri << "\n"
445 << "New-URI: " << NewURI << "\n"
446 << "\n" << std::flush;
447 Dequeue();
448 }
449 /*}}}*/
450 // AcqMethod::FetchResult::FetchResult - Constructor /*{{{*/
451 // ---------------------------------------------------------------------
452 /* */
453 pkgAcqMethod::FetchResult::FetchResult() : LastModified(0),
454 IMSHit(false), Size(0), ResumePoint(0)
455 {
456 }
457 /*}}}*/
458 // AcqMethod::FetchResult::TakeHashes - Load hashes /*{{{*/
459 // ---------------------------------------------------------------------
460 /* This hides the number of hashes we are supporting from the caller.
461 It just deals with the hash class. */
462 void pkgAcqMethod::FetchResult::TakeHashes(class Hashes &Hash)
463 {
464 Hashes = Hash.GetHashStringList();
465 }
466 /*}}}*/
467 void pkgAcqMethod::Dequeue() { /*{{{*/
468 FetchItem const * const Tmp = Queue;
469 Queue = Queue->Next;
470 if (Tmp == QueueBack)
471 QueueBack = Queue;
472 delete Tmp;
473 }
474 /*}}}*/