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