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