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