add the various foldmarkers in apt-pkg & cmdline (no code change)
[ntk/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 {
180 End += snprintf(End,sizeof(S)-50 - (End - S),"MD5-Hash: %s\n",Res.MD5Sum.c_str());
181 End += snprintf(End,sizeof(S)-50 - (End - S),"MD5Sum-Hash: %s\n",Res.MD5Sum.c_str());
182 }
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 (Res.SHA256Sum.empty() == false)
186 End += snprintf(End,sizeof(S)-50 - (End - S),"SHA256-Hash: %s\n",Res.SHA256Sum.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 if (Alt->SHA256Sum.empty() == false)
220 End += snprintf(End,sizeof(S)-50 - (End - S),"Alt-SHA256-Hash: %s\n",
221 Alt->SHA256Sum.c_str());
222
223 if (Alt->IMSHit == true)
224 strcat(End,"Alt-IMS-Hit: true\n");
225 }
226
227 strcat(End,"\n");
228 if (write(STDOUT_FILENO,S,strlen(S)) != (signed)strlen(S))
229 exit(100);
230
231 // Dequeue
232 FetchItem *Tmp = Queue;
233 Queue = Queue->Next;
234 delete Tmp;
235 if (Tmp == QueueBack)
236 QueueBack = Queue;
237 }
238 /*}}}*/
239 // AcqMethod::MediaFail - Syncronous request for new media /*{{{*/
240 // ---------------------------------------------------------------------
241 /* This sends a 403 Media Failure message to the APT and waits for it
242 to be ackd */
243 bool pkgAcqMethod::MediaFail(string Required,string Drive)
244 {
245 char S[1024];
246 snprintf(S,sizeof(S),"403 Media Failure\nMedia: %s\nDrive: %s\n\n",
247 Required.c_str(),Drive.c_str());
248
249 if (write(STDOUT_FILENO,S,strlen(S)) != (signed)strlen(S))
250 exit(100);
251
252 vector<string> MyMessages;
253
254 /* Here we read messages until we find a 603, each non 603 message is
255 appended to the main message list for later processing */
256 while (1)
257 {
258 if (WaitFd(STDIN_FILENO) == false)
259 return false;
260
261 if (ReadMessages(STDIN_FILENO,MyMessages) == false)
262 return false;
263
264 string Message = MyMessages.front();
265 MyMessages.erase(MyMessages.begin());
266
267 // Fetch the message number
268 char *End;
269 int Number = strtol(Message.c_str(),&End,10);
270 if (End == Message.c_str())
271 {
272 cerr << "Malformed message!" << endl;
273 exit(100);
274 }
275
276 // Change ack
277 if (Number == 603)
278 {
279 while (MyMessages.empty() == false)
280 {
281 Messages.push_back(MyMessages.front());
282 MyMessages.erase(MyMessages.begin());
283 }
284
285 return !StringToBool(LookupTag(Message,"Failed"),false);
286 }
287
288 Messages.push_back(Message);
289 }
290 }
291 /*}}}*/
292 // AcqMethod::Configuration - Handle the configuration message /*{{{*/
293 // ---------------------------------------------------------------------
294 /* This parses each configuration entry and puts it into the _config
295 Configuration class. */
296 bool pkgAcqMethod::Configuration(string Message)
297 {
298 ::Configuration &Cnf = *_config;
299
300 const char *I = Message.c_str();
301 const char *MsgEnd = I + Message.length();
302
303 unsigned int Length = strlen("Config-Item");
304 for (; I + Length < MsgEnd; I++)
305 {
306 // Not a config item
307 if (I[Length] != ':' || stringcasecmp(I,I+Length,"Config-Item") != 0)
308 continue;
309
310 I += Length + 1;
311
312 for (; I < MsgEnd && *I == ' '; I++);
313 const char *Equals = I;
314 for (; Equals < MsgEnd && *Equals != '='; Equals++);
315 const char *End = Equals;
316 for (; End < MsgEnd && *End != '\n'; End++);
317 if (End == Equals)
318 return false;
319
320 Cnf.Set(DeQuoteString(string(I,Equals-I)),
321 DeQuoteString(string(Equals+1,End-Equals-1)));
322 I = End;
323 }
324
325 return true;
326 }
327 /*}}}*/
328 // AcqMethod::Run - Run the message engine /*{{{*/
329 // ---------------------------------------------------------------------
330 /* Fetch any messages and execute them. In single mode it returns 1 if
331 there are no more available messages - any other result is a
332 fatal failure code! */
333 int pkgAcqMethod::Run(bool Single)
334 {
335 while (1)
336 {
337 // Block if the message queue is empty
338 if (Messages.empty() == true)
339 {
340 if (Single == false)
341 if (WaitFd(STDIN_FILENO) == false)
342 break;
343 if (ReadMessages(STDIN_FILENO,Messages) == false)
344 break;
345 }
346
347 // Single mode exits if the message queue is empty
348 if (Single == true && Messages.empty() == true)
349 return -1;
350
351 string Message = Messages.front();
352 Messages.erase(Messages.begin());
353
354 // Fetch the message number
355 char *End;
356 int Number = strtol(Message.c_str(),&End,10);
357 if (End == Message.c_str())
358 {
359 cerr << "Malformed message!" << endl;
360 return 100;
361 }
362
363 switch (Number)
364 {
365 case 601:
366 if (Configuration(Message) == false)
367 return 100;
368 break;
369
370 case 600:
371 {
372 FetchItem *Tmp = new FetchItem;
373
374 Tmp->Uri = LookupTag(Message,"URI");
375 Tmp->DestFile = LookupTag(Message,"FileName");
376 if (StrToTime(LookupTag(Message,"Last-Modified"),Tmp->LastModified) == false)
377 Tmp->LastModified = 0;
378 Tmp->IndexFile = StringToBool(LookupTag(Message,"Index-File"),false);
379 Tmp->Next = 0;
380
381 // Append it to the list
382 FetchItem **I = &Queue;
383 for (; *I != 0; I = &(*I)->Next);
384 *I = Tmp;
385 if (QueueBack == 0)
386 QueueBack = Tmp;
387
388 // Notify that this item is to be fetched.
389 if (Fetch(Tmp) == false)
390 Fail();
391
392 break;
393 }
394 }
395 }
396
397 Exit();
398 return 0;
399 }
400 /*}}}*/
401 // AcqMethod::Log - Send a log message /*{{{*/
402 // ---------------------------------------------------------------------
403 /* */
404 void pkgAcqMethod::Log(const char *Format,...)
405 {
406 string CurrentURI = "<UNKNOWN>";
407 if (Queue != 0)
408 CurrentURI = Queue->Uri;
409
410 va_list args;
411 va_start(args,Format);
412
413 // sprintf the description
414 char S[1024];
415 unsigned int Len = snprintf(S,sizeof(S)-4,"101 Log\nURI: %s\n"
416 "Message: ",CurrentURI.c_str());
417
418 vsnprintf(S+Len,sizeof(S)-4-Len,Format,args);
419 strcat(S,"\n\n");
420
421 if (write(STDOUT_FILENO,S,strlen(S)) != (signed)strlen(S))
422 exit(100);
423 }
424 /*}}}*/
425 // AcqMethod::Status - Send a status message /*{{{*/
426 // ---------------------------------------------------------------------
427 /* */
428 void pkgAcqMethod::Status(const char *Format,...)
429 {
430 string CurrentURI = "<UNKNOWN>";
431 if (Queue != 0)
432 CurrentURI = Queue->Uri;
433
434 va_list args;
435 va_start(args,Format);
436
437 // sprintf the description
438 char S[1024];
439 unsigned int Len = snprintf(S,sizeof(S)-4,"102 Status\nURI: %s\n"
440 "Message: ",CurrentURI.c_str());
441
442 vsnprintf(S+Len,sizeof(S)-4-Len,Format,args);
443 strcat(S,"\n\n");
444
445 if (write(STDOUT_FILENO,S,strlen(S)) != (signed)strlen(S))
446 exit(100);
447 }
448 /*}}}*/
449 // AcqMethod::Redirect - Send a redirect message /*{{{*/
450 // ---------------------------------------------------------------------
451 /* This method sends the redirect message and also manipulates the queue
452 to keep the pipeline synchronized. */
453 void pkgAcqMethod::Redirect(const string &NewURI)
454 {
455 string CurrentURI = "<UNKNOWN>";
456 if (Queue != 0)
457 CurrentURI = Queue->Uri;
458
459 char S[1024];
460 snprintf(S, sizeof(S)-50, "103 Redirect\nURI: %s\nNew-URI: %s\n\n",
461 CurrentURI.c_str(), NewURI.c_str());
462
463 if (write(STDOUT_FILENO,S,strlen(S)) != (ssize_t)strlen(S))
464 exit(100);
465
466 // Change the URI for the request.
467 Queue->Uri = NewURI;
468
469 /* To keep the pipeline synchronized, move the current request to
470 the end of the queue, past the end of the current pipeline. */
471 FetchItem *I;
472 for (I = Queue; I->Next != 0; I = I->Next) ;
473 I->Next = Queue;
474 Queue = Queue->Next;
475 I->Next->Next = 0;
476 if (QueueBack == 0)
477 QueueBack = I->Next;
478 }
479 /*}}}*/
480 // AcqMethod::FetchResult::FetchResult - Constructor /*{{{*/
481 // ---------------------------------------------------------------------
482 /* */
483 pkgAcqMethod::FetchResult::FetchResult() : LastModified(0),
484 IMSHit(false), Size(0), ResumePoint(0)
485 {
486 }
487 /*}}}*/
488 // AcqMethod::FetchResult::TakeHashes - Load hashes /*{{{*/
489 // ---------------------------------------------------------------------
490 /* This hides the number of hashes we are supporting from the caller.
491 It just deals with the hash class. */
492 void pkgAcqMethod::FetchResult::TakeHashes(Hashes &Hash)
493 {
494 MD5Sum = Hash.MD5.Result();
495 SHA1Sum = Hash.SHA1.Result();
496 SHA256Sum = Hash.SHA256.Result();
497 }
498 /*}}}*/