Minor Acquire cleanup
[ntk/apt.git] / apt-pkg / acquire-item.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: acquire-item.cc,v 1.14 1998/11/29 01:24:14 jgg Exp $
4 /* ######################################################################
5
6 Acquire Item - Item to acquire
7
8 Each item can download to exactly one file at a time. This means you
9 cannot create an item that fetches two uri's to two files at the same
10 time. The pkgAcqIndex class creates a second class upon instantiation
11 to fetch the other index files because of this.
12
13 ##################################################################### */
14 /*}}}*/
15 // Include Files /*{{{*/
16 #ifdef __GNUG__
17 #pragma implementation "apt-pkg/acquire-item.h"
18 #endif
19 #include <apt-pkg/acquire-item.h>
20 #include <apt-pkg/configuration.h>
21 #include <apt-pkg/error.h>
22 #include <strutl.h>
23
24 #include <sys/stat.h>
25 #include <unistd.h>
26 #include <errno.h>
27 #include <string.h>
28 #include <stdio.h>
29 /*}}}*/
30
31 // Acquire::Item::Item - Constructor /*{{{*/
32 // ---------------------------------------------------------------------
33 /* */
34 pkgAcquire::Item::Item(pkgAcquire *Owner) : Owner(Owner), FileSize(0),
35 Mode(0), ID(0), Complete(false), Local(false),
36 QueueCounter(0)
37 {
38 Owner->Add(this);
39 Status = StatIdle;
40 }
41 /*}}}*/
42 // Acquire::Item::~Item - Destructor /*{{{*/
43 // ---------------------------------------------------------------------
44 /* */
45 pkgAcquire::Item::~Item()
46 {
47 Owner->Remove(this);
48 }
49 /*}}}*/
50 // Acquire::Item::Failed - Item failed to download /*{{{*/
51 // ---------------------------------------------------------------------
52 /* We return to an idle state if there are still other queues that could
53 fetch this object */
54 void pkgAcquire::Item::Failed(string Message)
55 {
56 Status = StatIdle;
57 if (QueueCounter <= 1)
58 {
59 /* This indicates that the file is not available right now but might
60 be sometime later. If we do a retry cycle then this should be
61 retried */
62 if (StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
63 {
64 Status = StatIdle;
65 Owner->Dequeue(this);
66 return;
67 }
68
69 ErrorText = LookupTag(Message,"Message");
70 Status = StatError;
71 Owner->Dequeue(this);
72 }
73 }
74 /*}}}*/
75 // Acquire::Item::Start - Item has begun to download /*{{{*/
76 // ---------------------------------------------------------------------
77 /* */
78 void pkgAcquire::Item::Start(string Message,unsigned long Size)
79 {
80 Status = StatFetching;
81 if (FileSize == 0 && Complete == false)
82 FileSize = Size;
83 }
84 /*}}}*/
85 // Acquire::Item::Done - Item downloaded OK /*{{{*/
86 // ---------------------------------------------------------------------
87 /* */
88 void pkgAcquire::Item::Done(string Message,unsigned long Size,string)
89 {
90 // We just downloaded something..
91 string FileName = LookupTag(Message,"Filename");
92 if (Complete == false && FileName == DestFile)
93 {
94 if (Owner->Log != 0)
95 Owner->Log->Fetched(Size,atoi(LookupTag(Message,"Resume-Point","0").c_str()));
96 }
97
98 Status = StatDone;
99 ErrorText = string();
100 Owner->Dequeue(this);
101 }
102 /*}}}*/
103 // Acquire::Item::Rename - Rename a file /*{{{*/
104 // ---------------------------------------------------------------------
105 /* This helper function is used by alot of item methods as thier final
106 step */
107 void pkgAcquire::Item::Rename(string From,string To)
108 {
109 if (rename(From.c_str(),To.c_str()) != 0)
110 {
111 char S[300];
112 sprintf(S,"rename failed, %s (%s -> %s).",strerror(errno),
113 From.c_str(),To.c_str());
114 Status = StatError;
115 ErrorText = S;
116 }
117 }
118 /*}}}*/
119
120 // AcqIndex::AcqIndex - Constructor /*{{{*/
121 // ---------------------------------------------------------------------
122 /* The package file is added to the queue and a second class is
123 instantiated to fetch the revision file */
124 pkgAcqIndex::pkgAcqIndex(pkgAcquire *Owner,const pkgSourceList::Item *Location) :
125 Item(Owner), Location(Location)
126 {
127 Decompression = false;
128 Erase = false;
129
130 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
131 DestFile += URItoFileName(Location->PackagesURI());
132
133 // Create the item
134 Desc.URI = Location->PackagesURI() + ".gz";
135 Desc.Description = Location->PackagesInfo();
136 Desc.Owner = this;
137
138 // Set the short description to the archive component
139 if (Location->Dist[Location->Dist.size() - 1] == '/')
140 Desc.ShortDesc = Location->Dist;
141 else
142 Desc.ShortDesc = Location->Dist + '/' + Location->Section;
143
144 QueueURI(Desc);
145
146 // Create the Release fetch class
147 new pkgAcqIndexRel(Owner,Location);
148 }
149 /*}}}*/
150 // AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
151 // ---------------------------------------------------------------------
152 /* The only header we use is the last-modified header. */
153 string pkgAcqIndex::Custom600Headers()
154 {
155 string Final = _config->FindDir("Dir::State::lists");
156 Final += URItoFileName(Location->PackagesURI());
157
158 struct stat Buf;
159 if (stat(Final.c_str(),&Buf) != 0)
160 return "\nIndex-File: true";
161
162 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
163 }
164 /*}}}*/
165 // AcqIndex::Done - Finished a fetch /*{{{*/
166 // ---------------------------------------------------------------------
167 /* This goes through a number of states.. On the initial fetch the
168 method could possibly return an alternate filename which points
169 to the uncompressed version of the file. If this is so the file
170 is copied into the partial directory. In all other cases the file
171 is decompressed with a gzip uri. */
172 void pkgAcqIndex::Done(string Message,unsigned long Size,string MD5)
173 {
174 Item::Done(Message,Size,MD5);
175
176 if (Decompression == true)
177 {
178 // Done, move it into position
179 string FinalFile = _config->FindDir("Dir::State::lists");
180 FinalFile += URItoFileName(Location->PackagesURI());
181 Rename(DestFile,FinalFile);
182
183 /* We restore the original name to DestFile so that the clean operation
184 will work OK */
185 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
186 DestFile += URItoFileName(Location->PackagesURI());
187
188 // Remove the compressed version.
189 if (Erase == true)
190 unlink(DestFile.c_str());
191 return;
192 }
193
194 Erase = false;
195 Complete = true;
196
197 // Handle the unzipd case
198 string FileName = LookupTag(Message,"Alt-Filename");
199 if (FileName.empty() == false)
200 {
201 // The files timestamp matches
202 if (StringToBool(LookupTag(Message,"Alt-IMS-Hit"),false) == true)
203 return;
204
205 Decompression = true;
206 Local = true;
207 DestFile += ".decomp";
208 Desc.URI = "copy:" + FileName;
209 QueueURI(Desc);
210 Mode = "copy";
211 return;
212 }
213
214 FileName = LookupTag(Message,"Filename");
215 if (FileName.empty() == true)
216 {
217 Status = StatError;
218 ErrorText = "Method gave a blank filename";
219 }
220
221 // The files timestamp matches
222 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
223 return;
224
225 if (FileName == DestFile)
226 Erase = true;
227 else
228 Local = true;
229
230 Decompression = true;
231 DestFile += ".decomp";
232 Desc.URI = "gzip:" + FileName,Location->PackagesInfo();
233 QueueURI(Desc);
234 Mode = "gzip";
235 }
236 /*}}}*/
237 // AcqIndex::Describe - Describe the Item /*{{{*/
238 // ---------------------------------------------------------------------
239 /* */
240 string pkgAcqIndex::Describe()
241 {
242 return Location->PackagesURI();
243 }
244 /*}}}*/
245
246 // AcqIndexRel::pkgAcqIndexRel - Constructor /*{{{*/
247 // ---------------------------------------------------------------------
248 /* The Release file is added to the queue */
249 pkgAcqIndexRel::pkgAcqIndexRel(pkgAcquire *Owner,
250 const pkgSourceList::Item *Location) :
251 Item(Owner), Location(Location)
252 {
253 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
254 DestFile += URItoFileName(Location->ReleaseURI());
255
256 // Create the item
257 Desc.URI = Location->ReleaseURI();
258 Desc.Description = Location->ReleaseInfo();
259 Desc.Owner = this;
260
261 // Set the short description to the archive component
262 if (Location->Dist[Location->Dist.size() - 1] == '/')
263 Desc.ShortDesc = Location->Dist;
264 else
265 Desc.ShortDesc = Location->Dist + '/' + Location->Section;
266
267 QueueURI(Desc);
268 }
269 /*}}}*/
270 // AcqIndexRel::Custom600Headers - Insert custom request headers /*{{{*/
271 // ---------------------------------------------------------------------
272 /* The only header we use is the last-modified header. */
273 string pkgAcqIndexRel::Custom600Headers()
274 {
275 string Final = _config->FindDir("Dir::State::lists");
276 Final += URItoFileName(Location->ReleaseURI());
277
278 struct stat Buf;
279 if (stat(Final.c_str(),&Buf) != 0)
280 return "\nIndex-File: true";
281
282 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
283 }
284 /*}}}*/
285 // AcqIndexRel::Done - Item downloaded OK /*{{{*/
286 // ---------------------------------------------------------------------
287 /* The release file was not placed into the download directory then
288 a copy URI is generated and it is copied there otherwise the file
289 in the partial directory is moved into .. and the URI is finished. */
290 void pkgAcqIndexRel::Done(string Message,unsigned long Size,string MD5)
291 {
292 Item::Done(Message,Size,MD5);
293
294 string FileName = LookupTag(Message,"Filename");
295 if (FileName.empty() == true)
296 {
297 Status = StatError;
298 ErrorText = "Method gave a blank filename";
299 return;
300 }
301
302 Complete = true;
303
304 // The files timestamp matches
305 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
306 return;
307
308 // We have to copy it into place
309 if (FileName != DestFile)
310 {
311 Local = true;
312 Desc.URI = "copy:" + FileName;
313 QueueURI(Desc);
314 return;
315 }
316
317 // Done, move it into position
318 string FinalFile = _config->FindDir("Dir::State::lists");
319 FinalFile += URItoFileName(Location->ReleaseURI());
320 Rename(DestFile,FinalFile);
321 }
322 /*}}}*/
323 // AcqIndexRel::Describe - Describe the Item /*{{{*/
324 // ---------------------------------------------------------------------
325 /* */
326 string pkgAcqIndexRel::Describe()
327 {
328 return Location->ReleaseURI();
329 }
330 /*}}}*/
331
332 // AcqArchive::AcqArchive - Constructor /*{{{*/
333 // ---------------------------------------------------------------------
334 /* */
335 pkgAcqArchive::pkgAcqArchive(pkgAcquire *Owner,pkgSourceList *Sources,
336 pkgRecords *Recs,pkgCache::VerIterator const &Version,
337 string &StoreFilename) :
338 Item(Owner), Version(Version), Sources(Sources), Recs(Recs),
339 StoreFilename(StoreFilename)
340 {
341 // Select a source
342 pkgCache::VerFileIterator Vf = Version.FileList();
343 for (; Vf.end() == false; Vf++)
344 {
345 // Ignore not source sources
346 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
347 continue;
348
349 // Try to cross match against the source list
350 string PkgFile = flNotDir(Vf.File().FileName());
351 pkgSourceList::const_iterator Location;
352 for (Location = Sources->begin(); Location != Sources->end(); Location++)
353 if (PkgFile == URItoFileName(Location->PackagesURI()))
354 break;
355
356 if (Location == Sources->end())
357 continue;
358
359 // Grab the text package record
360 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
361 if (_error->PendingError() == true)
362 return;
363
364 PkgFile = Parse.FileName();
365 MD5 = Parse.MD5Hash();
366 if (PkgFile.empty() == true)
367 {
368 _error->Error("Unable to locate a file name for package %s, "
369 "perhaps the package files are corrupted.",
370 Version.ParentPkg().Name());
371 return;
372 }
373
374 // See if we already have the file.
375 FileSize = Version->Size;
376 string FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(PkgFile);
377 struct stat Buf;
378 if (stat(FinalFile.c_str(),&Buf) == 0)
379 {
380 // Make sure the size matches
381 if ((unsigned)Buf.st_size == Version->Size)
382 {
383 Complete = true;
384 Local = true;
385 Status = StatDone;
386 StoreFilename = DestFile = FinalFile;
387 return;
388 }
389
390 /* Hmm, we have a file and its size does not match, this shouldnt
391 happen.. */
392 unlink(FinalFile.c_str());
393 }
394
395 DestFile = _config->FindDir("Dir::Cache::Archives") + "partial/" + flNotDir(PkgFile);
396
397 // Create the item
398 Desc.URI = Location->ArchiveURI(PkgFile);
399 Desc.Description = Location->ArchiveInfo(Version);
400 Desc.Owner = this;
401 Desc.ShortDesc = Version.ParentPkg().Name();
402 QueueURI(Desc);
403
404 return;
405 }
406
407 _error->Error("I wasn't able to locate file for the %s package. "
408 "This probably means you need to rerun update.",
409 Version.ParentPkg().Name());
410 }
411 /*}}}*/
412 // AcqArchive::Done - Finished fetching /*{{{*/
413 // ---------------------------------------------------------------------
414 /* */
415 void pkgAcqArchive::Done(string Message,unsigned long Size,string Md5Hash)
416 {
417 Item::Done(Message,Size,MD5);
418
419 // Check the size
420 if (Size != Version->Size)
421 {
422 _error->Error("Size mismatch for package %s",Version.ParentPkg().Name());
423 return;
424 }
425
426 // Check the md5
427 if (Md5Hash.empty() == false && MD5.empty() == false)
428 {
429 if (Md5Hash != MD5)
430 {
431 _error->Error("MD5Sum mismatch for package %s",Version.ParentPkg().Name());
432 return;
433 }
434 }
435
436 // Grab the output filename
437 string FileName = LookupTag(Message,"Filename");
438 if (FileName.empty() == true)
439 {
440 Status = StatError;
441 ErrorText = "Method gave a blank filename";
442 return;
443 }
444
445 Complete = true;
446
447 // Reference filename
448 if (FileName != DestFile)
449 {
450 StoreFilename = DestFile = FileName;
451 Local = true;
452 return;
453 }
454
455 // Done, move it into position
456 string FinalFile = _config->FindDir("Dir::Cache::Archives");
457 FinalFile += flNotDir(DestFile);
458 Rename(DestFile,FinalFile);
459
460 StoreFilename = DestFile = FinalFile;
461 Complete = true;
462 }
463 /*}}}*/
464 // AcqArchive::Describe - Describe the Item /*{{{*/
465 // ---------------------------------------------------------------------
466 /* */
467 string pkgAcqArchive::Describe()
468 {
469 return Desc.URI;
470 }
471 /*}}}*/
472