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