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