* cmdline/apt-mark: fix chmod()/rename() ordering
[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 #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 #include <apt-pkg/hashes.h>
27
28 #include <iostream>
29 #include <stdarg.h>
30 #include <stdio.h>
31 #include <unistd.h>
32 #include <sys/signal.h>
33 /*}}}*/
34
35 using namespace std;
36
37 // AcqMethod::pkgAcqMethod - Constructor /*{{{*/
38 // ---------------------------------------------------------------------
39 /* This constructs the initialization text */
40 pkgAcqMethod::pkgAcqMethod(const char *Ver,unsigned long Flags)
41 {
42 char S[300] = "";
43 char *End = S;
44 strcat(End,"100 Capabilities\n");
45 sprintf(End+strlen(End),"Version: %s\n",Ver);
46
47 if ((Flags & SingleInstance) == SingleInstance)
48 strcat(End,"Single-Instance: true\n");
49
50 if ((Flags & Pipeline) == Pipeline)
51 strcat(End,"Pipeline: true\n");
52
53 if ((Flags & SendConfig) == SendConfig)
54 strcat(End,"Send-Config: true\n");
55
56 if ((Flags & LocalOnly) == LocalOnly)
57 strcat(End,"Local-Only: true\n");
58
59 if ((Flags & NeedsCleanup) == NeedsCleanup)
60 strcat(End,"Needs-Cleanup: true\n");
61
62 if ((Flags & Removable) == Removable)
63 strcat(End,"Removable: true\n");
64 strcat(End,"\n");
65
66 if (write(STDOUT_FILENO,S,strlen(S)) != (signed)strlen(S))
67 exit(100);
68
69 SetNonBlock(STDIN_FILENO,true);
70
71 Queue = 0;
72 QueueBack = 0;
73 }
74 /*}}}*/
75 // AcqMethod::Fail - A fetch has failed /*{{{*/
76 // ---------------------------------------------------------------------
77 /* */
78 void pkgAcqMethod::Fail(bool Transient)
79 {
80 string Err = "Undetermined Error";
81 if (_error->empty() == false)
82 _error->PopMessage(Err);
83 _error->Discard();
84 Fail(Err,Transient);
85 }
86 /*}}}*/
87 // AcqMethod::Fail - A fetch has failed /*{{{*/
88 // ---------------------------------------------------------------------
89 /* */
90 void pkgAcqMethod::Fail(string Err,bool Transient)
91 {
92 // Strip out junk from the error messages
93 for (string::iterator I = Err.begin(); I != Err.end(); I++)
94 {
95 if (*I == '\r')
96 *I = ' ';
97 if (*I == '\n')
98 *I = ' ';
99 }
100
101 char S[1024];
102 char *End = S;
103 if (Queue != 0)
104 {
105 End += snprintf(S,sizeof(S)-50,"400 URI Failure\nURI: %s\n"
106 "Message: %s %s\n",Queue->Uri.c_str(), Err.c_str(), IP.c_str());
107 // Dequeue
108 FetchItem *Tmp = Queue;
109 Queue = Queue->Next;
110 delete Tmp;
111 if (Tmp == QueueBack)
112 QueueBack = Queue;
113 }
114 else
115 {
116 End += snprintf(S,sizeof(S)-50,"400 URI Failure\nURI: <UNKNOWN>\n"
117 "Message: %s\n",Err.c_str());
118 }
119 if(FailReason.empty() == false)
120 End += snprintf(End,sizeof(S)-50 - (End - S),"FailReason: %s\n",FailReason.c_str());
121 if (UsedMirror.empty() == false)
122 End += snprintf(End,sizeof(S)-50 - (End - S),"UsedMirror: %s\n",UsedMirror.c_str());
123 // Set the transient flag
124 if (Transient == true)
125 strcat(S,"Transient-Failure: true\n\n");
126 else
127 strcat(S,"\n");
128
129 if (write(STDOUT_FILENO,S,strlen(S)) != (signed)strlen(S))
130 exit(100);
131 }
132 /*}}}*/
133 // AcqMethod::URIStart - Indicate a download is starting /*{{{*/
134 // ---------------------------------------------------------------------
135 /* */
136 void pkgAcqMethod::URIStart(FetchResult &Res)
137 {
138 if (Queue == 0)
139 abort();
140
141 char S[1024] = "";
142 char *End = S;
143
144 End += snprintf(S,sizeof(S),"200 URI Start\nURI: %s\n",Queue->Uri.c_str());
145 if (Res.Size != 0)
146 End += snprintf(End,sizeof(S)-4 - (End - S),"Size: %lu\n",Res.Size);
147
148 if (Res.LastModified != 0)
149 End += snprintf(End,sizeof(S)-4 - (End - S),"Last-Modified: %s\n",
150 TimeRFC1123(Res.LastModified).c_str());
151
152 if (Res.ResumePoint != 0)
153 End += snprintf(End,sizeof(S)-4 - (End - S),"Resume-Point: %lu\n",
154 Res.ResumePoint);
155
156 strcat(End,"\n");
157 if (write(STDOUT_FILENO,S,strlen(S)) != (signed)strlen(S))
158 exit(100);
159 }
160 /*}}}*/
161 // AcqMethod::URIDone - A URI is finished /*{{{*/
162 // ---------------------------------------------------------------------
163 /* */
164 void pkgAcqMethod::URIDone(FetchResult &Res, FetchResult *Alt)
165 {
166 if (Queue == 0)
167 abort();
168
169 char S[1024] = "";
170 char *End = S;
171
172 End += snprintf(S,sizeof(S),"201 URI Done\nURI: %s\n",Queue->Uri.c_str());
173
174 if (Res.Filename.empty() == false)
175 End += snprintf(End,sizeof(S)-50 - (End - S),"Filename: %s\n",Res.Filename.c_str());
176
177 if (Res.Size != 0)
178 End += snprintf(End,sizeof(S)-50 - (End - S),"Size: %lu\n",Res.Size);
179
180 if (Res.LastModified != 0)
181 End += snprintf(End,sizeof(S)-50 - (End - S),"Last-Modified: %s\n",
182 TimeRFC1123(Res.LastModified).c_str());
183
184 if (Res.MD5Sum.empty() == false)
185 End += snprintf(End,sizeof(S)-50 - (End - S),"MD5-Hash: %s\n",Res.MD5Sum.c_str());
186 if (Res.SHA1Sum.empty() == false)
187 End += snprintf(End,sizeof(S)-50 - (End - S),"SHA1-Hash: %s\n",Res.SHA1Sum.c_str());
188 if (UsedMirror.empty() == false)
189 End += snprintf(End,sizeof(S)-50 - (End - S),"UsedMirror: %s\n",UsedMirror.c_str());
190 if (Res.GPGVOutput.size() > 0)
191 End += snprintf(End,sizeof(S)-50 - (End - S),"GPGVOutput:\n");
192 for (vector<string>::iterator I = Res.GPGVOutput.begin();
193 I != Res.GPGVOutput.end(); I++)
194 End += snprintf(End,sizeof(S)-50 - (End - S), " %s\n", (*I).c_str());
195
196 if (Res.ResumePoint != 0)
197 End += snprintf(End,sizeof(S)-50 - (End - S),"Resume-Point: %lu\n",
198 Res.ResumePoint);
199
200 if (Res.IMSHit == true)
201 strcat(End,"IMS-Hit: true\n");
202 End = S + strlen(S);
203
204 if (Alt != 0)
205 {
206 if (Alt->Filename.empty() == false)
207 End += snprintf(End,sizeof(S)-50 - (End - S),"Alt-Filename: %s\n",Alt->Filename.c_str());
208
209 if (Alt->Size != 0)
210 End += snprintf(End,sizeof(S)-50 - (End - S),"Alt-Size: %lu\n",Alt->Size);
211
212 if (Alt->LastModified != 0)
213 End += snprintf(End,sizeof(S)-50 - (End - S),"Alt-Last-Modified: %s\n",
214 TimeRFC1123(Alt->LastModified).c_str());
215
216 if (Alt->MD5Sum.empty() == false)
217 End += snprintf(End,sizeof(S)-50 - (End - S),"Alt-MD5-Hash: %s\n",
218 Alt->MD5Sum.c_str());
219 if (Alt->SHA1Sum.empty() == false)
220 End += snprintf(End,sizeof(S)-50 - (End - S),"Alt-SHA1-Hash: %s\n",
221 Alt->SHA1Sum.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
450 // AcqMethod::FetchResult::FetchResult - Constructor /*{{{*/
451 // ---------------------------------------------------------------------
452 /* */
453 pkgAcqMethod::FetchResult::FetchResult() : LastModified(0),
454 IMSHit(false), Size(0), ResumePoint(0)
455 {
456 }
457 /*}}}*/
458 // AcqMethod::FetchResult::TakeHashes - Load hashes /*{{{*/
459 // ---------------------------------------------------------------------
460 /* This hides the number of hashes we are supporting from the caller.
461 It just deals with the hash class. */
462 void pkgAcqMethod::FetchResult::TakeHashes(Hashes &Hash)
463 {
464 MD5Sum = Hash.MD5.Result();
465 SHA1Sum = Hash.SHA1.Result();
466 }
467 /*}}}*/