]> git.saurik.com Git - apt.git/blob - apt-pkg/acquire-method.cc
add make -C test/interactive-helper/ to .travis.yml
[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
27 #include <iostream>
28 #include <stdio.h>
29 #include <sys/signal.h>
30 /*}}}*/
31
32 using namespace std;
33
34 // AcqMethod::pkgAcqMethod - Constructor /*{{{*/
35 // ---------------------------------------------------------------------
36 /* This constructs the initialization text */
37 pkgAcqMethod::pkgAcqMethod(const char *Ver,unsigned long Flags)
38 {
39 std::cout << "100 Capabilities\n"
40 << "Version: " << Ver << "\n";
41
42 if ((Flags & SingleInstance) == SingleInstance)
43 std::cout << "Single-Instance: true\n";
44
45 if ((Flags & Pipeline) == Pipeline)
46 std::cout << "Pipeline: true\n";
47
48 if ((Flags & SendConfig) == SendConfig)
49 std::cout << "Send-Config: true\n";
50
51 if ((Flags & LocalOnly) == LocalOnly)
52 std::cout <<"Local-Only: true\n";
53
54 if ((Flags & NeedsCleanup) == NeedsCleanup)
55 std::cout << "Needs-Cleanup: true\n";
56
57 if ((Flags & Removable) == Removable)
58 std::cout << "Removable: true\n";
59
60 std::cout << "\n" << std::flush;
61
62 SetNonBlock(STDIN_FILENO,true);
63
64 Queue = 0;
65 QueueBack = 0;
66 }
67 /*}}}*/
68 // AcqMethod::Fail - A fetch has failed /*{{{*/
69 // ---------------------------------------------------------------------
70 /* */
71 void pkgAcqMethod::Fail(bool Transient)
72 {
73 string Err = "Undetermined Error";
74 if (_error->empty() == false)
75 _error->PopMessage(Err);
76 _error->Discard();
77 Fail(Err,Transient);
78 }
79 /*}}}*/
80 // AcqMethod::Fail - A fetch has failed /*{{{*/
81 // ---------------------------------------------------------------------
82 /* */
83 void pkgAcqMethod::Fail(string Err,bool Transient)
84 {
85 // Strip out junk from the error messages
86 for (string::iterator I = Err.begin(); I != Err.end(); ++I)
87 {
88 if (*I == '\r')
89 *I = ' ';
90 if (*I == '\n')
91 *I = ' ';
92 }
93
94 if (Queue != 0)
95 {
96 std::cout << "400 URI Failure\nURI: " << Queue->Uri << "\n"
97 << "Message: " << Err << " " << IP << "\n";
98 Dequeue();
99 }
100 else
101 std::cout << "400 URI Failure\nURI: <UNKNOWN>\nMessage: " << Err << "\n";
102
103 if(FailReason.empty() == false)
104 std::cout << "FailReason: " << FailReason << "\n";
105 if (UsedMirror.empty() == false)
106 std::cout << "UsedMirror: " << UsedMirror << "\n";
107 // Set the transient flag
108 if (Transient == true)
109 std::cout << "Transient-Failure: true\n";
110
111 std::cout << "\n" << std::flush;
112 }
113 /*}}}*/
114 // AcqMethod::URIStart - Indicate a download is starting /*{{{*/
115 // ---------------------------------------------------------------------
116 /* */
117 void pkgAcqMethod::URIStart(FetchResult &Res)
118 {
119 if (Queue == 0)
120 abort();
121
122 std::cout << "200 URI Start\n"
123 << "URI: " << Queue->Uri << "\n";
124 if (Res.Size != 0)
125 std::cout << "Size: " << Res.Size << "\n";
126
127 if (Res.LastModified != 0)
128 std::cout << "Last-Modified: " << TimeRFC1123(Res.LastModified) << "\n";
129
130 if (Res.ResumePoint != 0)
131 std::cout << "Resume-Point: " << Res.ResumePoint << "\n";
132
133 if (UsedMirror.empty() == false)
134 std::cout << "UsedMirror: " << UsedMirror << "\n";
135
136 std::cout << "\n" << std::flush;
137 }
138 /*}}}*/
139 // AcqMethod::URIDone - A URI is finished /*{{{*/
140 // ---------------------------------------------------------------------
141 /* */
142 void pkgAcqMethod::URIDone(FetchResult &Res, FetchResult *Alt)
143 {
144 if (Queue == 0)
145 abort();
146
147 std::cout << "201 URI Done\n"
148 << "URI: " << Queue->Uri << "\n";
149
150 if (Res.Filename.empty() == false)
151 std::cout << "Filename: " << Res.Filename << "\n";
152
153 if (Res.Size != 0)
154 std::cout << "Size: " << Res.Size << "\n";
155
156 if (Res.LastModified != 0)
157 std::cout << "Last-Modified: " << TimeRFC1123(Res.LastModified) << "\n";
158
159 if (Res.MD5Sum.empty() == false)
160 std::cout << "MD5-Hash: " << Res.MD5Sum << "\n"
161 << "MD5Sum-Hash: " << Res.MD5Sum << "\n";
162 if (Res.SHA1Sum.empty() == false)
163 std::cout << "SHA1-Hash: " << Res.SHA1Sum << "\n";
164 if (Res.SHA256Sum.empty() == false)
165 std::cout << "SHA256-Hash: " << Res.SHA256Sum << "\n";
166 if (Res.SHA512Sum.empty() == false)
167 std::cout << "SHA512-Hash: " << Res.SHA512Sum << "\n";
168 if (UsedMirror.empty() == false)
169 std::cout << "UsedMirror: " << UsedMirror << "\n";
170 if (Res.GPGVOutput.empty() == false)
171 {
172 std::cout << "GPGVOutput:\n";
173 for (vector<string>::const_iterator I = Res.GPGVOutput.begin();
174 I != Res.GPGVOutput.end(); ++I)
175 std::cout << " " << *I << "\n";
176 }
177
178 if (Res.ResumePoint != 0)
179 std::cout << "Resume-Point: " << Res.ResumePoint << "\n";
180
181 if (Res.IMSHit == true)
182 std::cout << "IMS-Hit: true\n";
183
184 if (Alt != 0)
185 {
186 if (Alt->Filename.empty() == false)
187 std::cout << "Alt-Filename: " << Alt->Filename << "\n";
188
189 if (Alt->Size != 0)
190 std::cout << "Alt-Size: " << Alt->Size << "\n";
191
192 if (Alt->LastModified != 0)
193 std::cout << "Alt-Last-Modified: " << TimeRFC1123(Alt->LastModified) << "\n";
194
195 if (Alt->MD5Sum.empty() == false)
196 std::cout << "Alt-MD5-Hash: " << Alt->MD5Sum << "\n";
197 if (Alt->SHA1Sum.empty() == false)
198 std::cout << "Alt-SHA1-Hash: " << Alt->SHA1Sum << "\n";
199 if (Alt->SHA256Sum.empty() == false)
200 std::cout << "Alt-SHA256-Hash: " << Alt->SHA256Sum << "\n";
201 if (Alt->SHA512Sum.empty() == false)
202 std::cout << "Alt-SHA512-Hash: " << Alt->SHA512Sum << "\n";
203
204 if (Alt->IMSHit == true)
205 std::cout << "Alt-IMS-Hit: true\n";
206 }
207
208 std::cout << "\n" << std::flush;
209 Dequeue();
210 }
211 /*}}}*/
212 // AcqMethod::MediaFail - Syncronous request for new media /*{{{*/
213 // ---------------------------------------------------------------------
214 /* This sends a 403 Media Failure message to the APT and waits for it
215 to be ackd */
216 bool pkgAcqMethod::MediaFail(string Required,string Drive)
217 {
218 fprintf(stdout, "403 Media Failure\nMedia: %s\nDrive: %s\n",
219 Required.c_str(),Drive.c_str());
220 std::cout << "\n" << std::flush;
221
222 vector<string> MyMessages;
223
224 /* Here we read messages until we find a 603, each non 603 message is
225 appended to the main message list for later processing */
226 while (1)
227 {
228 if (WaitFd(STDIN_FILENO) == false)
229 return false;
230
231 if (ReadMessages(STDIN_FILENO,MyMessages) == false)
232 return false;
233
234 string Message = MyMessages.front();
235 MyMessages.erase(MyMessages.begin());
236
237 // Fetch the message number
238 char *End;
239 int Number = strtol(Message.c_str(),&End,10);
240 if (End == Message.c_str())
241 {
242 cerr << "Malformed message!" << endl;
243 exit(100);
244 }
245
246 // Change ack
247 if (Number == 603)
248 {
249 while (MyMessages.empty() == false)
250 {
251 Messages.push_back(MyMessages.front());
252 MyMessages.erase(MyMessages.begin());
253 }
254
255 return !StringToBool(LookupTag(Message,"Failed"),false);
256 }
257
258 Messages.push_back(Message);
259 }
260 }
261 /*}}}*/
262 // AcqMethod::Configuration - Handle the configuration message /*{{{*/
263 // ---------------------------------------------------------------------
264 /* This parses each configuration entry and puts it into the _config
265 Configuration class. */
266 bool pkgAcqMethod::Configuration(string Message)
267 {
268 ::Configuration &Cnf = *_config;
269
270 const char *I = Message.c_str();
271 const char *MsgEnd = I + Message.length();
272
273 unsigned int Length = strlen("Config-Item");
274 for (; I + Length < MsgEnd; I++)
275 {
276 // Not a config item
277 if (I[Length] != ':' || stringcasecmp(I,I+Length,"Config-Item") != 0)
278 continue;
279
280 I += Length + 1;
281
282 for (; I < MsgEnd && *I == ' '; I++);
283 const char *Equals = (const char*) memchr(I, '=', MsgEnd - I);
284 if (Equals == NULL)
285 return false;
286 const char *End = (const char*) memchr(Equals, '\n', MsgEnd - Equals);
287 if (End == NULL)
288 End = MsgEnd;
289
290 Cnf.Set(DeQuoteString(string(I,Equals-I)),
291 DeQuoteString(string(Equals+1,End-Equals-1)));
292 I = End;
293 }
294
295 return true;
296 }
297 /*}}}*/
298 // AcqMethod::Run - Run the message engine /*{{{*/
299 // ---------------------------------------------------------------------
300 /* Fetch any messages and execute them. In single mode it returns 1 if
301 there are no more available messages - any other result is a
302 fatal failure code! */
303 int pkgAcqMethod::Run(bool Single)
304 {
305 while (1)
306 {
307 // Block if the message queue is empty
308 if (Messages.empty() == true)
309 {
310 if (Single == false)
311 if (WaitFd(STDIN_FILENO) == false)
312 break;
313 if (ReadMessages(STDIN_FILENO,Messages) == false)
314 break;
315 }
316
317 // Single mode exits if the message queue is empty
318 if (Single == true && Messages.empty() == true)
319 return -1;
320
321 string Message = Messages.front();
322 Messages.erase(Messages.begin());
323
324 // Fetch the message number
325 char *End;
326 int Number = strtol(Message.c_str(),&End,10);
327 if (End == Message.c_str())
328 {
329 cerr << "Malformed message!" << endl;
330 return 100;
331 }
332
333 switch (Number)
334 {
335 case 601:
336 if (Configuration(Message) == false)
337 return 100;
338 break;
339
340 case 600:
341 {
342 FetchItem *Tmp = new FetchItem;
343
344 Tmp->Uri = LookupTag(Message,"URI");
345 Tmp->DestFile = LookupTag(Message,"FileName");
346 if (RFC1123StrToTime(LookupTag(Message,"Last-Modified").c_str(),Tmp->LastModified) == false)
347 Tmp->LastModified = 0;
348 Tmp->IndexFile = StringToBool(LookupTag(Message,"Index-File"),false);
349 Tmp->FailIgnore = StringToBool(LookupTag(Message,"Fail-Ignore"),false);
350 Tmp->Next = 0;
351
352 // Append it to the list
353 FetchItem **I = &Queue;
354 for (; *I != 0; I = &(*I)->Next);
355 *I = Tmp;
356 if (QueueBack == 0)
357 QueueBack = Tmp;
358
359 // Notify that this item is to be fetched.
360 if (Fetch(Tmp) == false)
361 Fail();
362
363 break;
364 }
365 }
366 }
367
368 Exit();
369 return 0;
370 }
371 /*}}}*/
372 // AcqMethod::PrintStatus - privately really send a log/status message /*{{{*/
373 // ---------------------------------------------------------------------
374 /* */
375 void pkgAcqMethod::PrintStatus(char const * const header, const char* Format,
376 va_list &args) const
377 {
378 string CurrentURI = "<UNKNOWN>";
379 if (Queue != 0)
380 CurrentURI = Queue->Uri;
381 if (UsedMirror.empty() == true)
382 fprintf(stdout, "%s\nURI: %s\nMessage: ",
383 header, CurrentURI.c_str());
384 else
385 fprintf(stdout, "%s\nURI: %s\nUsedMirror: %s\nMessage: ",
386 header, CurrentURI.c_str(), UsedMirror.c_str());
387 vfprintf(stdout,Format,args);
388 std::cout << "\n\n" << std::flush;
389 }
390 /*}}}*/
391 // AcqMethod::Log - Send a log message /*{{{*/
392 // ---------------------------------------------------------------------
393 /* */
394 void pkgAcqMethod::Log(const char *Format,...)
395 {
396 va_list args;
397 va_start(args,Format);
398 PrintStatus("101 Log", Format, args);
399 va_end(args);
400 }
401 /*}}}*/
402 // AcqMethod::Status - Send a status message /*{{{*/
403 // ---------------------------------------------------------------------
404 /* */
405 void pkgAcqMethod::Status(const char *Format,...)
406 {
407 va_list args;
408 va_start(args,Format);
409 PrintStatus("102 Status", Format, args);
410 va_end(args);
411 }
412 /*}}}*/
413 // AcqMethod::Redirect - Send a redirect message /*{{{*/
414 // ---------------------------------------------------------------------
415 /* This method sends the redirect message and dequeues the item as
416 * the worker will enqueue again later on to the right queue */
417 void pkgAcqMethod::Redirect(const string &NewURI)
418 {
419 std::cout << "103 Redirect\nURI: " << Queue->Uri << "\n"
420 << "New-URI: " << NewURI << "\n"
421 << "\n" << std::flush;
422 Dequeue();
423 }
424 /*}}}*/
425 // AcqMethod::FetchResult::FetchResult - Constructor /*{{{*/
426 // ---------------------------------------------------------------------
427 /* */
428 pkgAcqMethod::FetchResult::FetchResult() : LastModified(0),
429 IMSHit(false), Size(0), ResumePoint(0)
430 {
431 }
432 /*}}}*/
433 // AcqMethod::FetchResult::TakeHashes - Load hashes /*{{{*/
434 // ---------------------------------------------------------------------
435 /* This hides the number of hashes we are supporting from the caller.
436 It just deals with the hash class. */
437 void pkgAcqMethod::FetchResult::TakeHashes(Hashes &Hash)
438 {
439 MD5Sum = Hash.MD5.Result();
440 SHA1Sum = Hash.SHA1.Result();
441 SHA256Sum = Hash.SHA256.Result();
442 SHA512Sum = Hash.SHA512.Result();
443 }
444 /*}}}*/
445 void pkgAcqMethod::Dequeue() { /*{{{*/
446 FetchItem const * const Tmp = Queue;
447 Queue = Queue->Next;
448 if (Tmp == QueueBack)
449 QueueBack = Queue;
450 delete Tmp;
451 }
452 /*}}}*/