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