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