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