* mirror method:
[ntk/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];
36280399 99 char *End = S;
be4401bf
AL
100 if (Queue != 0)
101 {
36280399
MV
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());
be4401bf
AL
104 // Dequeue
105 FetchItem *Tmp = Queue;
106 Queue = Queue->Next;
107 delete Tmp;
5cb5d8dc
AL
108 if (Tmp == QueueBack)
109 QueueBack = Queue;
be4401bf
AL
110 }
111 else
36280399
MV
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());
a72ace20
AL
120 // Set the transient flag
121 if (Transient == true)
122 strcat(S,"Transient-Failure: true\n\n");
123 else
124 strcat(S,"\n");
125
93bf083d
AL
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/* */
be4401bf 133void pkgAcqMethod::URIStart(FetchResult &Res)
93bf083d 134{
be4401bf
AL
135 if (Queue == 0)
136 abort();
137
93bf083d
AL
138 char S[1024] = "";
139 char *End = S;
140
be4401bf 141 End += snprintf(S,sizeof(S),"200 URI Start\nURI: %s\n",Queue->Uri.c_str());
93bf083d 142 if (Res.Size != 0)
8b067c22 143 End += snprintf(End,sizeof(S)-4 - (End - S),"Size: %lu\n",Res.Size);
93bf083d
AL
144
145 if (Res.LastModified != 0)
8b067c22 146 End += snprintf(End,sizeof(S)-4 - (End - S),"Last-Modified: %s\n",
93bf083d
AL
147 TimeRFC1123(Res.LastModified).c_str());
148
be4401bf 149 if (Res.ResumePoint != 0)
8b067c22 150 End += snprintf(End,sizeof(S)-4 - (End - S),"Resume-Point: %lu\n",
be4401bf 151 Res.ResumePoint);
196fd136
MV
152 if (UsedMirror.empty() == false)
153 End += snprintf(End,sizeof(S)-50 - (End - S),"UsedMirror: %s\n",UsedMirror.c_str());
93bf083d
AL
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/* */
163void pkgAcqMethod::URIDone(FetchResult &Res, FetchResult *Alt)
164{
be4401bf
AL
165 if (Queue == 0)
166 abort();
167
93bf083d
AL
168 char S[1024] = "";
169 char *End = S;
170
be4401bf 171 End += snprintf(S,sizeof(S),"201 URI Done\nURI: %s\n",Queue->Uri.c_str());
93bf083d
AL
172
173 if (Res.Filename.empty() == false)
8b067c22 174 End += snprintf(End,sizeof(S)-50 - (End - S),"Filename: %s\n",Res.Filename.c_str());
93bf083d
AL
175
176 if (Res.Size != 0)
8b067c22 177 End += snprintf(End,sizeof(S)-50 - (End - S),"Size: %lu\n",Res.Size);
93bf083d
AL
178
179 if (Res.LastModified != 0)
8b067c22 180 End += snprintf(End,sizeof(S)-50 - (End - S),"Last-Modified: %s\n",
93bf083d
AL
181 TimeRFC1123(Res.LastModified).c_str());
182
183 if (Res.MD5Sum.empty() == false)
8a8feb29 184 {
8b067c22 185 End += snprintf(End,sizeof(S)-50 - (End - S),"MD5-Hash: %s\n",Res.MD5Sum.c_str());
8a8feb29
MV
186 End += snprintf(End,sizeof(S)-50 - (End - S),"MD5Sum-Hash: %s\n",Res.MD5Sum.c_str());
187 }
a7c835af
AL
188 if (Res.SHA1Sum.empty() == false)
189 End += snprintf(End,sizeof(S)-50 - (End - S),"SHA1-Hash: %s\n",Res.SHA1Sum.c_str());
5d0d7fae
MV
190 if (Res.SHA256Sum.empty() == false)
191 End += snprintf(End,sizeof(S)-50 - (End - S),"SHA256-Hash: %s\n",Res.SHA256Sum.c_str());
36280399
MV
192 if (UsedMirror.empty() == false)
193 End += snprintf(End,sizeof(S)-50 - (End - S),"UsedMirror: %s\n",UsedMirror.c_str());
b3d44315
MV
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());
93bf083d 199
b98f2859 200 if (Res.ResumePoint != 0)
8b067c22 201 End += snprintf(End,sizeof(S)-50 - (End - S),"Resume-Point: %lu\n",
b98f2859
AL
202 Res.ResumePoint);
203
93bf083d
AL
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)
8b067c22 211 End += snprintf(End,sizeof(S)-50 - (End - S),"Alt-Filename: %s\n",Alt->Filename.c_str());
93bf083d
AL
212
213 if (Alt->Size != 0)
8b067c22 214 End += snprintf(End,sizeof(S)-50 - (End - S),"Alt-Size: %lu\n",Alt->Size);
93bf083d
AL
215
216 if (Alt->LastModified != 0)
8b067c22 217 End += snprintf(End,sizeof(S)-50 - (End - S),"Alt-Last-Modified: %s\n",
93bf083d
AL
218 TimeRFC1123(Alt->LastModified).c_str());
219
220 if (Alt->MD5Sum.empty() == false)
8b067c22 221 End += snprintf(End,sizeof(S)-50 - (End - S),"Alt-MD5-Hash: %s\n",
93bf083d 222 Alt->MD5Sum.c_str());
a7c835af
AL
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());
5d0d7fae
MV
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());
93bf083d
AL
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);
be4401bf
AL
237
238 // Dequeue
239 FetchItem *Tmp = Queue;
240 Queue = Queue->Next;
241 delete Tmp;
5cb5d8dc
AL
242 if (Tmp == QueueBack)
243 QueueBack = Queue;
93bf083d
AL
244}
245 /*}}}*/
f46e7681
AL
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 */
018f1533 250bool pkgAcqMethod::MediaFail(string Required,string Drive)
f46e7681
AL
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)
76d97c26 266 return false;
f46e7681
AL
267
268 if (ReadMessages(STDIN_FILENO,MyMessages) == false)
76d97c26
AL
269 return false;
270
f46e7681
AL
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 {
76d97c26 286 while (MyMessages.empty() == false)
f46e7681
AL
287 {
288 Messages.push_back(MyMessages.front());
289 MyMessages.erase(MyMessages.begin());
290 }
542ec555 291
2a749770 292 return !StringToBool(LookupTag(Message,"Failed"),false);
f46e7681
AL
293 }
294
295 Messages.push_back(Message);
296 }
297}
298 /*}}}*/
93bf083d
AL
299// AcqMethod::Configuration - Handle the configuration message /*{{{*/
300// ---------------------------------------------------------------------
301/* This parses each configuration entry and puts it into the _config
302 Configuration class. */
303bool pkgAcqMethod::Configuration(string Message)
304{
305 ::Configuration &Cnf = *_config;
306
b4fc9b6f
AL
307 const char *I = Message.c_str();
308 const char *MsgEnd = I + Message.length();
93bf083d
AL
309
310 unsigned int Length = strlen("Config-Item");
b4fc9b6f 311 for (; I + Length < MsgEnd; I++)
93bf083d
AL
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
b4fc9b6f 319 for (; I < MsgEnd && *I == ' '; I++);
93bf083d 320 const char *Equals = I;
b4fc9b6f 321 for (; Equals < MsgEnd && *Equals != '='; Equals++);
93bf083d 322 const char *End = Equals;
b4fc9b6f 323 for (; End < MsgEnd && *End != '\n'; End++);
93bf083d
AL
324 if (End == Equals)
325 return false;
326
6d5dd02a
AL
327 Cnf.Set(DeQuoteString(string(I,Equals-I)),
328 DeQuoteString(string(Equals+1,End-Equals-1)));
93bf083d
AL
329 I = End;
330 }
331
332 return true;
333}
334 /*}}}*/
335// AcqMethod::Run - Run the message engine /*{{{*/
336// ---------------------------------------------------------------------
8384ebdf
AL
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! */
be4401bf 340int pkgAcqMethod::Run(bool Single)
93bf083d 341{
93bf083d
AL
342 while (1)
343 {
be4401bf 344 // Block if the message queue is empty
93bf083d 345 if (Messages.empty() == true)
be4401bf
AL
346 {
347 if (Single == false)
348 if (WaitFd(STDIN_FILENO) == false)
8e5fc8f5 349 break;
92e889c8 350 if (ReadMessages(STDIN_FILENO,Messages) == false)
8e5fc8f5 351 break;
92e889c8
AL
352 }
353
be4401bf
AL
354 // Single mode exits if the message queue is empty
355 if (Single == true && Messages.empty() == true)
8384ebdf 356 return -1;
be4401bf 357
93bf083d
AL
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)
8e5fc8f5 371 {
93bf083d
AL
372 case 601:
373 if (Configuration(Message) == false)
374 return 100;
375 break;
376
377 case 600:
be4401bf
AL
378 {
379 FetchItem *Tmp = new FetchItem;
380
381 Tmp->Uri = LookupTag(Message,"URI");
382 Tmp->DestFile = LookupTag(Message,"FileName");
96cc64a5 383 if (RFC1123StrToTime(LookupTag(Message,"Last-Modified").c_str(),Tmp->LastModified) == false)
b98f2859 384 Tmp->LastModified = 0;
a72ace20 385 Tmp->IndexFile = StringToBool(LookupTag(Message,"Index-File"),false);
963b16dc 386 Tmp->FailIgnore = StringToBool(LookupTag(Message,"Fail-Ignore"),false);
be4401bf
AL
387 Tmp->Next = 0;
388
389 // Append it to the list
390 FetchItem **I = &Queue;
92e889c8 391 for (; *I != 0; I = &(*I)->Next);
be4401bf 392 *I = Tmp;
5cb5d8dc
AL
393 if (QueueBack == 0)
394 QueueBack = Tmp;
395
bfd22fc0 396 // Notify that this item is to be fetched.
be4401bf 397 if (Fetch(Tmp) == false)
93bf083d 398 Fail();
bfd22fc0 399
93bf083d
AL
400 break;
401 }
402 }
403 }
404
8e5fc8f5 405 Exit();
93bf083d
AL
406 return 0;
407}
408 /*}}}*/
be4401bf
AL
409// AcqMethod::Log - Send a log message /*{{{*/
410// ---------------------------------------------------------------------
411/* */
412void 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];
196fd136
MV
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());
8b067c22 428 vsnprintf(S+Len,sizeof(S)-4-Len,Format,args);
be4401bf
AL
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/* */
438void 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];
196fd136
MV
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());
be4401bf 454
8b067c22 455 vsnprintf(S+Len,sizeof(S)-4-Len,Format,args);
be4401bf
AL
456 strcat(S,"\n\n");
457
458 if (write(STDOUT_FILENO,S,strlen(S)) != (signed)strlen(S))
459 exit(100);
460}
461 /*}}}*/
ebb461fd
MV
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. */
466void 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 /*}}}*/
93bf083d
AL
493// AcqMethod::FetchResult::FetchResult - Constructor /*{{{*/
494// ---------------------------------------------------------------------
495/* */
93274b8d
AL
496pkgAcqMethod::FetchResult::FetchResult() : LastModified(0),
497 IMSHit(false), Size(0), ResumePoint(0)
93bf083d
AL
498{
499}
500 /*}}}*/
a7c835af
AL
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. */
505void pkgAcqMethod::FetchResult::TakeHashes(Hashes &Hash)
506{
507 MD5Sum = Hash.MD5.Result();
508 SHA1Sum = Hash.SHA1.Result();
5d0d7fae 509 SHA256Sum = Hash.SHA256.Result();
a7c835af
AL
510}
511 /*}}}*/