Fixed or handling bug
[ntk/apt.git] / apt-pkg / acquire-method.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: acquire-method.cc,v 1.24 2000/01/17 07:11:49 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
57 if ((Flags & Removable) == Removable)
58 strcat(End,"Removable: true\n");
59 strcat(End,"\n");
60
61 if (write(STDOUT_FILENO,S,strlen(S)) != (signed)strlen(S))
62 exit(100);
63
64 SetNonBlock(STDIN_FILENO,true);
65
66 Queue = 0;
67 QueueBack = 0;
68 }
69 /*}}}*/
70 // AcqMethod::Fail - A fetch has failed /*{{{*/
71 // ---------------------------------------------------------------------
72 /* */
73 void pkgAcqMethod::Fail(bool Transient)
74 {
75 string Err = "Undetermined Error";
76 if (_error->empty() == false)
77 _error->PopMessage(Err);
78 _error->Discard();
79 Fail(Err,Transient);
80 }
81 /*}}}*/
82 // AcqMethod::Fail - A fetch has failed /*{{{*/
83 // ---------------------------------------------------------------------
84 /* */
85 void pkgAcqMethod::Fail(string Err,bool Transient)
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
96 char S[1024];
97 if (Queue != 0)
98 {
99 snprintf(S,sizeof(S)-50,"400 URI Failure\nURI: %s\n"
100 "Message: %s\n",Queue->Uri.c_str(),Err.c_str());
101
102 // Dequeue
103 FetchItem *Tmp = Queue;
104 Queue = Queue->Next;
105 delete Tmp;
106 if (Tmp == QueueBack)
107 QueueBack = Queue;
108 }
109 else
110 snprintf(S,sizeof(S)-50,"400 URI Failure\nURI: <UNKNOWN>\n"
111 "Message: %s\n",Err.c_str());
112
113 // Set the transient flag
114 if (Transient == true)
115 strcat(S,"Transient-Failure: true\n\n");
116 else
117 strcat(S,"\n");
118
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 /* */
126 void pkgAcqMethod::URIStart(FetchResult &Res)
127 {
128 if (Queue == 0)
129 abort();
130
131 char S[1024] = "";
132 char *End = S;
133
134 End += snprintf(S,sizeof(S),"200 URI Start\nURI: %s\n",Queue->Uri.c_str());
135 if (Res.Size != 0)
136 End += snprintf(End,sizeof(S)-4 - (End - S),"Size: %lu\n",Res.Size);
137
138 if (Res.LastModified != 0)
139 End += snprintf(End,sizeof(S)-4 - (End - S),"Last-Modified: %s\n",
140 TimeRFC1123(Res.LastModified).c_str());
141
142 if (Res.ResumePoint != 0)
143 End += snprintf(End,sizeof(S)-4 - (End - S),"Resume-Point: %lu\n",
144 Res.ResumePoint);
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 /* */
154 void pkgAcqMethod::URIDone(FetchResult &Res, FetchResult *Alt)
155 {
156 if (Queue == 0)
157 abort();
158
159 char S[1024] = "";
160 char *End = S;
161
162 End += snprintf(S,sizeof(S),"201 URI Done\nURI: %s\n",Queue->Uri.c_str());
163
164 if (Res.Filename.empty() == false)
165 End += snprintf(End,sizeof(S)-50 - (End - S),"Filename: %s\n",Res.Filename.c_str());
166
167 if (Res.Size != 0)
168 End += snprintf(End,sizeof(S)-50 - (End - S),"Size: %lu\n",Res.Size);
169
170 if (Res.LastModified != 0)
171 End += snprintf(End,sizeof(S)-50 - (End - S),"Last-Modified: %s\n",
172 TimeRFC1123(Res.LastModified).c_str());
173
174 if (Res.MD5Sum.empty() == false)
175 End += snprintf(End,sizeof(S)-50 - (End - S),"MD5-Hash: %s\n",Res.MD5Sum.c_str());
176
177 if (Res.ResumePoint != 0)
178 End += snprintf(End,sizeof(S)-50 - (End - S),"Resume-Point: %lu\n",
179 Res.ResumePoint);
180
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)
188 End += snprintf(End,sizeof(S)-50 - (End - S),"Alt-Filename: %s\n",Alt->Filename.c_str());
189
190 if (Alt->Size != 0)
191 End += snprintf(End,sizeof(S)-50 - (End - S),"Alt-Size: %lu\n",Alt->Size);
192
193 if (Alt->LastModified != 0)
194 End += snprintf(End,sizeof(S)-50 - (End - S),"Alt-Last-Modified: %s\n",
195 TimeRFC1123(Alt->LastModified).c_str());
196
197 if (Alt->MD5Sum.empty() == false)
198 End += snprintf(End,sizeof(S)-50 - (End - S),"Alt-MD5-Hash: %s\n",
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);
208
209 // Dequeue
210 FetchItem *Tmp = Queue;
211 Queue = Queue->Next;
212 delete Tmp;
213 if (Tmp == QueueBack)
214 QueueBack = Queue;
215 }
216 /*}}}*/
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 */
221 bool pkgAcqMethod::MediaFail(string Required,string Drive)
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)
237 return false;
238
239 if (ReadMessages(STDIN_FILENO,MyMessages) == false)
240 return false;
241
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 {
257 while (MyMessages.empty() == false)
258 {
259 Messages.push_back(MyMessages.front());
260 MyMessages.erase(MyMessages.begin());
261 }
262
263 return !StringToBool(LookupTag(Message,"Fail"),false);
264 }
265
266 Messages.push_back(Message);
267 }
268 }
269 /*}}}*/
270 // AcqMethod::Configuration - Handle the configuration message /*{{{*/
271 // ---------------------------------------------------------------------
272 /* This parses each configuration entry and puts it into the _config
273 Configuration class. */
274 bool 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
297 Cnf.Set(DeQuoteString(string(I,Equals-I)),
298 DeQuoteString(string(Equals+1,End-Equals-1)));
299 I = End;
300 }
301
302 return true;
303 }
304 /*}}}*/
305 // AcqMethod::Run - Run the message engine /*{{{*/
306 // ---------------------------------------------------------------------
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! */
310 int pkgAcqMethod::Run(bool Single)
311 {
312 while (1)
313 {
314 // Block if the message queue is empty
315 if (Messages.empty() == true)
316 {
317 if (Single == false)
318 if (WaitFd(STDIN_FILENO) == false)
319 break;
320 if (ReadMessages(STDIN_FILENO,Messages) == false)
321 break;
322 }
323
324 // Single mode exits if the message queue is empty
325 if (Single == true && Messages.empty() == true)
326 return -1;
327
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)
341 {
342 case 601:
343 if (Configuration(Message) == false)
344 return 100;
345 break;
346
347 case 600:
348 {
349 FetchItem *Tmp = new FetchItem;
350
351 Tmp->Uri = LookupTag(Message,"URI");
352 Tmp->DestFile = LookupTag(Message,"FileName");
353 if (StrToTime(LookupTag(Message,"Last-Modified"),Tmp->LastModified) == false)
354 Tmp->LastModified = 0;
355 Tmp->IndexFile = StringToBool(LookupTag(Message,"Index-File"),false);
356 Tmp->Next = 0;
357
358 // Append it to the list
359 FetchItem **I = &Queue;
360 for (; *I != 0; I = &(*I)->Next);
361 *I = Tmp;
362 if (QueueBack == 0)
363 QueueBack = Tmp;
364
365 // Notify that this item is to be fetched.
366 if (Fetch(Tmp) == false)
367 Fail();
368
369 break;
370 }
371 }
372 }
373
374 Exit();
375 return 0;
376 }
377 /*}}}*/
378 // AcqMethod::Log - Send a log message /*{{{*/
379 // ---------------------------------------------------------------------
380 /* */
381 void 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];
392 unsigned int Len = snprintf(S,sizeof(S)-4,"101 Log\nURI: %s\n"
393 "Message: ",CurrentURI.c_str());
394
395 vsnprintf(S+Len,sizeof(S)-4-Len,Format,args);
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 /* */
405 void 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];
416 unsigned int Len = snprintf(S,sizeof(S)-4,"102 Status\nURI: %s\n"
417 "Message: ",CurrentURI.c_str());
418
419 vsnprintf(S+Len,sizeof(S)-4-Len,Format,args);
420 strcat(S,"\n\n");
421
422 if (write(STDOUT_FILENO,S,strlen(S)) != (signed)strlen(S))
423 exit(100);
424 }
425 /*}}}*/
426
427 // AcqMethod::FetchResult::FetchResult - Constructor /*{{{*/
428 // ---------------------------------------------------------------------
429 /* */
430 pkgAcqMethod::FetchResult::FetchResult() : LastModified(0),
431 IMSHit(false), Size(0), ResumePoint(0)
432 {
433 }
434 /*}}}*/