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