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