Fixed md5 handling
[ntk/apt.git] / apt-pkg / acquire-item.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: acquire-item.cc,v 1.18 1999/01/24 21:16:04 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 ErrorText = LookupTag(Message,"Message");
58 if (QueueCounter <= 1)
59 {
60 /* This indicates that the file is not available right now but might
61 be sometime later. If we do a retry cycle then this should be
62 retried */
63 if (StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
64 {
65 Status = StatIdle;
66 Owner->Dequeue(this);
67 return;
68 }
69
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), Vf(Version.FileList())
340 {
341 // Select a source
342 if (QueueNext() == false && _error->PendingError() == false)
343 _error->Error("I wasn't able to locate file for the %s package. "
344 "This might mean you need to manually fix this package.",
345 Version.ParentPkg().Name());
346 }
347 /*}}}*/
348 // AcqArchive::QueueNext - Queue the next file source /*{{{*/
349 // ---------------------------------------------------------------------
350 /* This queues the next available file version for download. */
351 bool pkgAcqArchive::QueueNext()
352 {
353 for (; Vf.end() == false; Vf++)
354 {
355 // Ignore not source sources
356 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
357 continue;
358
359 // Try to cross match against the source list
360 string PkgFile = flNotDir(Vf.File().FileName());
361 pkgSourceList::const_iterator Location;
362 for (Location = Sources->begin(); Location != Sources->end(); Location++)
363 if (PkgFile == URItoFileName(Location->PackagesURI()))
364 break;
365
366 if (Location == Sources->end())
367 continue;
368
369 // Grab the text package record
370 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
371 if (_error->PendingError() == true)
372 return false;
373
374 PkgFile = Parse.FileName();
375 MD5 = Parse.MD5Hash();
376 if (PkgFile.empty() == true)
377 return _error->Error("The package index files are corrupted. No Filename: "
378 "field for package %s."
379 ,Version.ParentPkg().Name());
380
381 // See if we already have the file.
382 FileSize = Version->Size;
383 string FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(PkgFile);
384 struct stat Buf;
385 if (stat(FinalFile.c_str(),&Buf) == 0)
386 {
387 // Make sure the size matches
388 if ((unsigned)Buf.st_size == Version->Size)
389 {
390 Complete = true;
391 Local = true;
392 Status = StatDone;
393 StoreFilename = DestFile = FinalFile;
394 return true;
395 }
396
397 /* Hmm, we have a file and its size does not match, this shouldnt
398 happen.. */
399 unlink(FinalFile.c_str());
400 }
401
402 DestFile = _config->FindDir("Dir::Cache::Archives") + "partial/" + flNotDir(PkgFile);
403
404 // Create the item
405 Desc.URI = Location->ArchiveURI(PkgFile);
406 Desc.Description = Location->ArchiveInfo(Version);
407 Desc.Owner = this;
408 Desc.ShortDesc = Version.ParentPkg().Name();
409 QueueURI(Desc);
410
411 Vf++;
412 return true;
413 }
414 return false;
415 }
416 /*}}}*/
417 // AcqArchive::Done - Finished fetching /*{{{*/
418 // ---------------------------------------------------------------------
419 /* */
420 void pkgAcqArchive::Done(string Message,unsigned long Size,string Md5Hash)
421 {
422 Item::Done(Message,Size,Md5Hash);
423
424 // Check the size
425 if (Size != Version->Size)
426 {
427 _error->Error("Size mismatch for package %s",Version.ParentPkg().Name());
428 return;
429 }
430
431 // Check the md5
432 if (Md5Hash.empty() == false && MD5.empty() == false)
433 {
434 if (Md5Hash != MD5)
435 {
436 _error->Error("MD5Sum mismatch for package %s",Version.ParentPkg().Name());
437 return;
438 }
439 }
440
441 // Grab the output filename
442 string FileName = LookupTag(Message,"Filename");
443 if (FileName.empty() == true)
444 {
445 Status = StatError;
446 ErrorText = "Method gave a blank filename";
447 return;
448 }
449
450 Complete = true;
451
452 // Reference filename
453 if (FileName != DestFile)
454 {
455 StoreFilename = DestFile = FileName;
456 Local = true;
457 return;
458 }
459
460 // Done, move it into position
461 string FinalFile = _config->FindDir("Dir::Cache::Archives");
462 FinalFile += flNotDir(DestFile);
463 Rename(DestFile,FinalFile);
464
465 StoreFilename = DestFile = FinalFile;
466 Complete = true;
467 }
468 /*}}}*/
469 // AcqArchive::Describe - Describe the Item /*{{{*/
470 // ---------------------------------------------------------------------
471 /* */
472 string pkgAcqArchive::Describe()
473 {
474 return Desc.URI;
475 }
476 /*}}}*/
477 // AcqArchive::Failed - Failure handler /*{{{*/
478 // ---------------------------------------------------------------------
479 /* Here we try other sources */
480 void pkgAcqArchive::Failed(string Message)
481 {
482 ErrorText = LookupTag(Message,"Message");
483 if (QueueNext() == false)
484 Item::Failed(Message);
485 }
486 /*}}}*/