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