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