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