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