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