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