]> git.saurik.com Git - apt.git/blobdiff - apt-pkg/acquire-item.cc
* debug support (Debug::pkgAcquire::RRed) for rred method added
[apt.git] / apt-pkg / acquire-item.cc
index f40c6a50d862cce446e3fae11429780442a9c3a1..b8886a750aafa84ee5dc178a84bb55f0c9a5c2cb 100644 (file)
@@ -24,6 +24,8 @@
 #include <apt-pkg/strutl.h>
 #include <apt-pkg/fileutl.h>
 #include <apt-pkg/md5.h>
+#include <apt-pkg/sha1.h>
+#include <apt-pkg/tagfile.h>
 
 #include <apti18n.h>
     
@@ -31,6 +33,7 @@
 #include <unistd.h>
 #include <errno.h>
 #include <string>
+#include <sstream>
 #include <stdio.h>
                                                                        /*}}}*/
 
@@ -131,14 +134,336 @@ void pkgAcquire::Item::Rename(string From,string To)
 }
                                                                        /*}}}*/
 
+// AcqIndexDiffs::AcqIndexDiffs - Constructor                  
+// ---------------------------------------------------------------------
+/* The package diff is added to the queue. one object is constructed
+ * for each diff and the index
+ */
+pkgAcqIndexDiffs::pkgAcqIndexDiffs(pkgAcquire *Owner,
+                                  string URI,string URIDesc,string ShortDesc,
+                                  string ExpectedMD5, vector<DiffInfo> diffs)
+   : Item(Owner), RealURI(URI), ExpectedMD5(ExpectedMD5), 
+     available_patches(diffs)
+{
+   
+   DestFile = _config->FindDir("Dir::State::lists") + "partial/";
+   DestFile += URItoFileName(URI);
+
+   Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
+
+   Desc.Description = URIDesc;
+   Desc.Owner = this;
+   Desc.ShortDesc = ShortDesc;
+
+   CurrentPackagesFile = _config->FindDir("Dir::State::lists");
+   CurrentPackagesFile += URItoFileName(RealURI);
+
+   if(Debug) {
+      std::clog << "pkgAcqIndexDiffs::pkgAcqIndexDiffs(): " 
+               << CurrentPackagesFile << std::endl;
+   }
+
+   if(!FileExists(CurrentPackagesFile) || 
+      !_config->FindB("Acquire::Diffs",true)) {
+      // we don't have a pkg file or we don't want to queue
+      if(Debug)
+        std::clog << "No index file or canceld by user" << std::endl;
+      Failed("", NULL);
+      return;
+   }
+
+   if(available_patches.size() == 0) {
+      State = StateFetchIndex;
+      QueueDiffIndex(URI);
+   } else {
+      State = StateFetchDiff;
+      QueueNextDiff();
+   }
+}
+
+void pkgAcqIndexDiffs::QueueDiffIndex(string URI)
+{
+   Desc.URI = URI + ".diff/Index";
+   Desc.Description = Description + "IndexDiff";
+   DestFile = _config->FindDir("Dir::State::lists") + "partial/";
+   DestFile += URItoFileName(URI) + string(".IndexDiff");
+
+   if(Debug)
+      std::clog << "QueueDiffIndex: " << Desc.URI << std::endl;
+
+   QueueURI(Desc);
+}
+
+// AcqIndex::Custom600Headers - Insert custom request headers          /*{{{*/
+// ---------------------------------------------------------------------
+/* The only header we use is the last-modified header. */
+string pkgAcqIndexDiffs::Custom600Headers()
+{
+   // we only care for the IndexDiff file
+   if(State != StateFetchIndex)
+      return string("");
+
+   string Final = _config->FindDir("Dir::State::lists");
+   Final += URItoFileName(RealURI) + string(".IndexDiff");
+   
+   if(Debug)
+      std::clog << "Custom600Header-IMS: " << Final << std::endl;
+
+   struct stat Buf;
+   if (stat(Final.c_str(),&Buf) != 0)
+      return "\nIndex-File: true";
+   
+   return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
+}
+
+void pkgAcqIndexDiffs::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
+{
+   std::clog << "pkgAcqIndexDiffs failed: " << Desc.URI << std::endl
+            << "Falling back to normal index file aquire" << std::endl;
+   new pkgAcqIndex(Owner, RealURI, Desc.Description,Desc.ShortDesc, 
+                  ExpectedMD5);
+   Finish();
+}
+
+
+// helper that cleans the item out of the fetcher queue
+void pkgAcqIndexDiffs::Finish(bool allDone)
+{
+   // we restore the original name, this is required, otherwise
+   // the file will be cleaned
+   if(allDone) {
+      // this is for the "real" finish
+      DestFile = _config->FindDir("Dir::State::lists");
+      DestFile += URItoFileName(RealURI);
+      Complete = true;
+      Dequeue();
+      if(Debug)
+        std::clog << "\n\nallDone: " << DestFile << "\n" << std::endl;
+      return;
+
+   }
+
+   if(Debug)
+      std::clog << "Finishing: " << Desc.URI << std::endl;
+   Complete = false;
+   Status = StatDone;
+   Dequeue();
+   return;
+}
+
+
+
+bool pkgAcqIndexDiffs::QueueNextDiff()
+{
+
+   // calc sha1 of the just patched file
+   string FinalFile = _config->FindDir("Dir::State::lists");
+   FinalFile += URItoFileName(RealURI);
+
+   FileFd fd(FinalFile, FileFd::ReadOnly);
+   SHA1Summation SHA1;
+   SHA1.AddFD(fd.Fd(), fd.Size());
+   string local_sha1 = string(SHA1.Result());
+   if(Debug)
+      std::clog << "QueueNextDiff: " 
+               << FinalFile << " (" << local_sha1 << ")"<<std::endl;
+
+   // remove all patches until the next matching patch is found
+   // this requires the Index file to be ordered
+   for(vector<DiffInfo>::iterator I=available_patches.begin();
+       available_patches.size() > 0 && I != available_patches.end() 
+         && (*I).sha1 != local_sha1; 
+       I++) {
+      available_patches.erase(I);
+   }
+
+   // error checking and falling back if no patch was found
+   if(available_patches.size() == 0) { 
+      Failed("", NULL);
+      return false;
+   }
+
+   // queue the right diff
+   Desc.URI = string(RealURI) + string(".diff/") + available_patches[0].file + string(".gz");
+   Desc.Description = available_patches[0].file + string(".pdiff");
+
+   DestFile = _config->FindDir("Dir::State::lists") + "partial/";
+   DestFile += URItoFileName(RealURI + string(".diff/") + available_patches[0].file);
+
+   if(Debug)
+      std::clog << "pkgAcqIndexDiffs::QueueNextDiff(): " << Desc.URI << std::endl;
+   
+   QueueURI(Desc);
+
+   return true;
+}
+
+bool pkgAcqIndexDiffs::ParseIndexDiff(string IndexDiffFile)
+{
+   if(Debug)
+      std::clog << "pkgAcqIndexDiffs::ParseIndexDiff() " << IndexDiffFile 
+               << std::endl;
+   
+   FileFd Fd(IndexDiffFile,FileFd::ReadOnly);
+   pkgTagFile TF(&Fd);
+   if (_error->PendingError() == true)
+      return false;
+   pkgTagSection Tags;
+   if(TF.Step(Tags) == true)
+   {
+      string local_sha1;
+      string tmp = Tags.FindS("SHA1-Current");
+      std::stringstream ss(tmp);
+      ss >> ServerSha1;
+
+      FileFd fd(CurrentPackagesFile, FileFd::ReadOnly);
+      SHA1Summation SHA1;
+      SHA1.AddFD(fd.Fd(), fd.Size());
+      local_sha1 = string(SHA1.Result());
+
+      if(local_sha1 == ServerSha1) {
+        if(Debug)
+           std::clog << "Package file is up-to-date" << std::endl;
+        Finish(true);
+        return true;
+      }
+      if(Debug)
+        std::clog << "SHA1-Current: " << ServerSha1 << std::endl;
+
+      // check the historie and see what patches we need
+      string history = Tags.FindS("SHA1-History");     
+      std::stringstream hist(history);
+      DiffInfo d;
+      string size;
+      bool found = false;
+      while(hist >> d.sha1 >> size >> d.file) {
+        d.size = atoi(size.c_str());
+        // read until the first match is found
+        if(d.sha1 == local_sha1) 
+           found=true;
+        // from that point on, we probably need all diffs
+        if(found) {
+           if(Debug)
+              std::clog << "Need to get diff: " << d.file << std::endl;
+           available_patches.push_back(d);
+        }
+      }
+
+      // no information how to get the patches, bail out
+      if(!found) {
+        if(Debug)
+           std::clog << "Can't find a patch in the index file" << std::endl;
+        // Failed will queue a big package file
+        Failed("", NULL);
+      } else {
+        // queue the diffs
+        new pkgAcqIndexDiffs(Owner, RealURI, Description, Desc.ShortDesc,
+                             ExpectedMD5, available_patches);
+        Finish();
+        return true;
+      }
+   }
+
+   return false;
+}
+
+
+void pkgAcqIndexDiffs::Done(string Message,unsigned long Size,string Md5Hash,
+                           pkgAcquire::MethodConfig *Cnf)
+{
+   if(Debug)
+      std::clog << "pkgAcqIndexDiffs::Done(): " << Desc.URI << std::endl;
+
+   Item::Done(Message,Size,Md5Hash,Cnf);
+
+   string FinalFile;
+   FinalFile = _config->FindDir("Dir::State::lists")+URItoFileName(RealURI);
+
+   // sucess in downloading the index
+   if(State == StateFetchIndex) 
+   {
+      // rename the index
+      FinalFile += string(".IndexDiff");
+      if(Debug)
+        std::clog << "Renaming: " << DestFile << " -> " << FinalFile 
+                  << std::endl;
+      Rename(DestFile,FinalFile);
+      chmod(FinalFile.c_str(),0644);
+      DestFile = FinalFile;
+
+      if(!ParseIndexDiff(DestFile))
+        return Failed("", NULL);
+      else 
+        return Finish();
+   }
+
+   // sucess in downloading a diff, enter ApplyDiff state
+   if(State == StateFetchDiff) 
+   {
+
+      if(Debug)
+        std::clog << "Sending to gzip method: " << FinalFile << std::endl;
+
+      string FileName = LookupTag(Message,"Filename");
+      State = StateUnzipDiff;
+      Desc.URI = "gzip:" + FileName;
+      DestFile += ".decomp";
+      QueueURI(Desc);
+      Mode = "gzip";
+      return;
+   } 
+
+   // sucess in downloading a diff, enter ApplyDiff state
+   if(State == StateUnzipDiff) 
+   {
+
+      // rred excepts the patch as $FinalFile.ed
+      Rename(DestFile,FinalFile+".ed");
+
+      if(Debug)
+        std::clog << "Sending to rred method: " << FinalFile << std::endl;
+
+      State = StateApplyDiff;
+      Desc.URI = "rred:" + FinalFile;
+      QueueURI(Desc);
+      Mode = "rred";
+      return;
+   } 
+
+
+   // success in download/apply a diff, queue next (if needed)
+   if(State == StateApplyDiff)
+   {
+      // remove the just applied patch
+      available_patches.erase(available_patches.begin());
+
+      // move into place
+      if(Debug) 
+      {
+        std::clog << "Moving patched file in place: " << std::endl
+                  << DestFile << " -> " << FinalFile << std::endl;
+      }
+      Rename(DestFile,FinalFile);
+
+      // see if there is more to download
+      if(available_patches.size() > 0) {
+        new pkgAcqIndexDiffs(Owner, RealURI, Description, Desc.ShortDesc,
+                             ExpectedMD5, available_patches);
+        return Finish();
+      } else 
+        return Finish(true);
+   }
+}
+
+
 // AcqIndex::AcqIndex - Constructor                                    /*{{{*/
 // ---------------------------------------------------------------------
 /* The package file is added to the queue and a second class is 
    instantiated to fetch the revision file */   
 pkgAcqIndex::pkgAcqIndex(pkgAcquire *Owner,
                         string URI,string URIDesc,string ShortDesc,
-                        string ExpectedMD5, string comprExt) :
-   Item(Owner), RealURI(URI), ExpectedMD5(ExpectedMD5)
+                        string ExpectedMD5, string comprExt)
+   Item(Owner), RealURI(URI), ExpectedMD5(ExpectedMD5)
 {
    Decompression = false;
    Erase = false;
@@ -321,6 +646,10 @@ pkgAcqMetaSig::pkgAcqMetaSig(pkgAcquire *Owner,
    DestFile = _config->FindDir("Dir::State::lists") + "partial/";
    DestFile += URItoFileName(URI);
 
+   // remove any partial downloaded sig-file. it may confuse proxies
+   // and is too small to warrant a partial download anyway
+   unlink(DestFile.c_str());
+
    // Create the item
    Desc.Description = URIDesc;
    Desc.Owner = this;
@@ -336,6 +665,12 @@ pkgAcqMetaSig::pkgAcqMetaSig(pkgAcquire *Owner,
       // File was already in place.  It needs to be re-verified
       // because Release might have changed, so Move it into partial
       Rename(Final,DestFile);
+      // unlink the file and do not try to use I-M-S and Last-Modified
+      // if the users proxy is broken
+      if(_config->FindB("Acquire::BrokenProxy", false) == true) {
+        std::cerr << "forcing re-get of the signature file as requested" << std::endl;
+        unlink(DestFile.c_str());
+      }
    }
 
    QueueURI(Desc);
@@ -346,13 +681,10 @@ pkgAcqMetaSig::pkgAcqMetaSig(pkgAcquire *Owner,
 /* The only header we use is the last-modified header. */
 string pkgAcqMetaSig::Custom600Headers()
 {
-   string Final = _config->FindDir("Dir::State::lists");
-   Final += URItoFileName(RealURI);
-   
    struct stat Buf;
-   if (stat(Final.c_str(),&Buf) != 0)
+   if (stat(DestFile.c_str(),&Buf) != 0)
       return "\nIndex-File: true";
-   
+
    return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
 }
 
@@ -592,8 +924,8 @@ void pkgAcqMetaIndex::QueueIndexes(bool verify)
       }
       
       // Queue Packages file
-      new pkgAcqIndex(Owner, (*Target)->URI, (*Target)->Description,
-                      (*Target)->ShortDesc, ExpectedIndexMD5);
+      new pkgAcqIndexDiffs(Owner, (*Target)->URI, (*Target)->Description,
+                          (*Target)->ShortDesc, ExpectedIndexMD5);
    }
 }