Browse Source

* merged the apt--mirror branch

Michael Vogt 19 years ago
parent
commit
a577a938d4

+ 54 - 2
apt-pkg/acquire-item.cc

@@ -63,6 +63,7 @@ void pkgAcquire::Item::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
 {
    Status = StatIdle;
    ErrorText = LookupTag(Message,"Message");
+   UsedMirror =  LookupTag(Message,"UsedMirror");
    if (QueueCounter <= 1)
    {
       /* This indicates that the file is not available right now but might
@@ -79,6 +80,13 @@ void pkgAcquire::Item::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
       Status = StatError;
       Dequeue();
    }   
+   
+   // report mirror failure back to LP if we actually use a mirror
+   string FailReason = LookupTag(Message, "FailReason");
+   if(FailReason.size() != 0)
+      ReportMirrorFailure(FailReason);
+   else
+      ReportMirrorFailure(ErrorText);
 }
 									/*}}}*/
 // Acquire::Item::Start - Item has begun to download			/*{{{*/
@@ -100,6 +108,7 @@ void pkgAcquire::Item::Done(string Message,unsigned long Size,string,
 {
    // We just downloaded something..
    string FileName = LookupTag(Message,"Filename");
+   UsedMirror =  LookupTag(Message,"UsedMirror");
    if (Complete == false && FileName == DestFile)
    {
       if (Owner->Log != 0)
@@ -108,7 +117,6 @@ void pkgAcquire::Item::Done(string Message,unsigned long Size,string,
 
    if (FileSize == 0)
       FileSize= Size;
-   
    Status = StatDone;
    ErrorText = string();
    Owner->Dequeue(this);
@@ -131,6 +139,49 @@ void pkgAcquire::Item::Rename(string From,string To)
 }
 									/*}}}*/
 
+void pkgAcquire::Item::ReportMirrorFailure(string FailCode)
+{
+   // we only act if a mirror was used at all
+   if(UsedMirror.empty())
+      return;
+#if 0
+   std::cerr << "\nReportMirrorFailure: " 
+	     << UsedMirror
+	     << " Uri: " << DescURI()
+	     << " FailCode: " 
+	     << FailCode << std::endl;
+#endif
+   const char *Args[40];
+   unsigned int i = 0;
+   string report = _config->Find("Methods::Mirror::ProblemReporting", 
+				 "/usr/lib/apt/apt-report-mirror-failure");
+   if(!FileExists(report))
+      return;
+   Args[i++] = report.c_str();
+   Args[i++] = UsedMirror.c_str();
+   Args[i++] = DescURI().c_str();
+   Args[i++] = FailCode.c_str();
+   Args[i++] = NULL;
+   pid_t pid = ExecFork();
+   if(pid < 0) 
+   {
+      _error->Error("ReportMirrorFailure Fork failed");
+      return;
+   }
+   else if(pid == 0) 
+   {
+      execvp(Args[0], (char**)Args);
+      std::cerr << "Could not exec " << Args[0] << std::endl;
+      _exit(100);
+   }
+   if(!ExecWait(pid, "report-mirror-failure")) 
+   {
+      _error->Warning("Couldn't report problem to '%s'",
+		      _config->Find("Methods::Mirror::ProblemReporting").c_str());
+   }
+}
+
+
 // AcqIndex::AcqIndex - Constructor					/*{{{*/
 // ---------------------------------------------------------------------
 /* The package file is added to the queue and a second class is 
@@ -176,7 +227,6 @@ string pkgAcqIndex::Custom600Headers()
    struct stat Buf;
    if (stat(Final.c_str(),&Buf) != 0)
       return "\nIndex-File: true";
-   
    return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
 }
 									/*}}}*/
@@ -235,6 +285,7 @@ void pkgAcqIndex::Done(string Message,unsigned long Size,string MD5,
          Status = StatAuthError;
          ErrorText = _("MD5Sum mismatch");
          Rename(DestFile,DestFile + ".FAILED");
+	 ReportMirrorFailure("HashChecksumFailure");
          return;
       }
       // Done, move it into position
@@ -757,6 +808,7 @@ void pkgAcqMetaIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
       }
 
       // gpgv method failed 
+      ReportMirrorFailure("GPGFailure");
       _error->Warning("GPG error: %s: %s",
                       Desc.Description.c_str(),
                       LookupTag(Message,"Message").c_str());

+ 4 - 0
apt-pkg/acquire-item.h

@@ -57,6 +57,7 @@ class pkgAcquire::Item
    unsigned long ID;
    bool Complete;
    bool Local;
+   string UsedMirror;
 
    // Number of queues we are inserted into
    unsigned int QueueCounter;
@@ -79,6 +80,9 @@ class pkgAcquire::Item
    pkgAcquire *GetOwner() {return Owner;};
    virtual bool IsTrusted() {return false;};
    
+   // report mirror problems
+   void ReportMirrorFailure(string FailCode);
+
    Item(pkgAcquire *Owner);
    virtual ~Item();
 };

+ 13 - 8
apt-pkg/acquire-method.cc

@@ -99,12 +99,11 @@ void pkgAcqMethod::Fail(string Err,bool Transient)
    }
    
    char S[1024];
+   char *End = S;
    if (Queue != 0)
    {
-      snprintf(S,sizeof(S)-50,"400 URI Failure\nURI: %s\n"
-	       "Message: %s %s\n",Queue->Uri.c_str(),Err.c_str(),
-	       FailExtra.c_str());
-
+      End += snprintf(S,sizeof(S)-50,"400 URI Failure\nURI: %s\n"
+		      "Message: %s %s\n",Queue->Uri.c_str(), Err.c_str(), IP.c_str());
       // Dequeue
       FetchItem *Tmp = Queue;
       Queue = Queue->Next;
@@ -113,10 +112,14 @@ void pkgAcqMethod::Fail(string Err,bool Transient)
 	 QueueBack = Queue;
    }
    else
-      snprintf(S,sizeof(S)-50,"400 URI Failure\nURI: <UNKNOWN>\n"
-	       "Message: %s %s\n",Err.c_str(),
-	       FailExtra.c_str());
-      
+   {
+      End += snprintf(S,sizeof(S)-50,"400 URI Failure\nURI: <UNKNOWN>\n"
+		      "Message: %s\n",Err.c_str());
+   }
+   if(FailReason.empty() == false)
+      End += snprintf(End,sizeof(S)-50 - (End - S),"FailReason: %s\n",FailReason.c_str());
+   if (UsedMirror.empty() == false)
+      End += snprintf(End,sizeof(S)-50 - (End - S),"UsedMirror: %s\n",UsedMirror.c_str());
    // Set the transient flag 
    if (Transient == true)
       strcat(S,"Transient-Failure: true\n\n");
@@ -182,6 +185,8 @@ void pkgAcqMethod::URIDone(FetchResult &Res, FetchResult *Alt)
       End += snprintf(End,sizeof(S)-50 - (End - S),"MD5-Hash: %s\n",Res.MD5Sum.c_str());
    if (Res.SHA1Sum.empty() == false)
       End += snprintf(End,sizeof(S)-50 - (End - S),"SHA1-Hash: %s\n",Res.SHA1Sum.c_str());
+   if (UsedMirror.empty() == false)
+      End += snprintf(End,sizeof(S)-50 - (End - S),"UsedMirror: %s\n",UsedMirror.c_str());
    if (Res.GPGVOutput.size() > 0)
       End += snprintf(End,sizeof(S)-50 - (End - S),"GPGVOutput:\n");     
    for (vector<string>::iterator I = Res.GPGVOutput.begin();

+ 9 - 6
apt-pkg/acquire-method.h

@@ -54,7 +54,9 @@ class pkgAcqMethod
    vector<string> Messages;
    FetchItem *Queue;
    FetchItem *QueueBack;
-   string FailExtra;
+   string FailReason;
+   string UsedMirror;
+   string IP;
    
    // Handlers for messages
    virtual bool Configuration(string Message);
@@ -63,14 +65,14 @@ class pkgAcqMethod
    // Outgoing messages
    void Fail(bool Transient = false);
    inline void Fail(const char *Why, bool Transient = false) {Fail(string(Why),Transient);};
-   void Fail(string Why, bool Transient = false);
-   void URIStart(FetchResult &Res);
-   void URIDone(FetchResult &Res,FetchResult *Alt = 0);
+   virtual void Fail(string Why, bool Transient = false);
+   virtual void URIStart(FetchResult &Res);
+   virtual void URIDone(FetchResult &Res,FetchResult *Alt = 0);
+
    bool MediaFail(string Required,string Drive);
    virtual void Exit() {};
 
    public:
-
    enum CnfFlags {SingleInstance = (1<<0),
                   Pipeline = (1<<1), SendConfig = (1<<2),
                   LocalOnly = (1<<3), NeedsCleanup = (1<<4), 
@@ -80,7 +82,8 @@ class pkgAcqMethod
    void Status(const char *Format,...);
    
    int Run(bool Single = false);
-   inline void SetFailExtraMsg(string Msg) {FailExtra = Msg;};
+   inline void SetFailReason(string Msg) {FailReason = Msg;};
+   inline void SetIP(string aIP) {IP = aIP;};
    
    pkgAcqMethod(const char *Ver,unsigned long Flags = 0);
    virtual ~pkgAcqMethod() {};

+ 1 - 0
apt-pkg/init.cc

@@ -56,6 +56,7 @@ bool pkgInitConfig(Configuration &Cnf)
        
    Cnf.Set("Dir::State::lists","lists/");
    Cnf.Set("Dir::State::cdroms","cdroms.list");
+   Cnf.Set("Dir::State::mirrors","mirrors/");
    
    // Cache
    Cnf.Set("Dir::Cache","var/cache/apt/");

+ 24 - 0
cmdline/apt-report-mirror-failure

@@ -0,0 +1,24 @@
+#!/usr/bin/python
+
+import sys
+import urllib
+import apt_pkg
+
+apt_pkg.init()
+url = apt_pkg.Config.Find("Acquire::Mirror::ReportFailures", None)
+                          #"http://people.ubuntu.com:9000/mirror-failure")
+                          #"http://localhost:9000/mirror-failure")
+if not url:
+    sys.exit(0)
+
+print "Reporting mirror failure to '%s'" % url
+
+data = {}
+data['mirror'] = sys.argv[1]
+data['failurl'] = sys.argv[2]
+data['error'] = sys.argv[3]
+f = urllib.urlopen(url, urllib.urlencode(data))
+f.read()
+f.close()
+
+

+ 6 - 0
cmdline/makefile

@@ -58,3 +58,9 @@ SOURCE=apt-mark
 TO=$(BIN)
 TARGET=program
 include $(COPY_H)
+
+# The apt-key program
+SOURCE=apt-report-mirror-failure
+TO=$(BIN)
+TARGET=program
+include $(COPY_H)

+ 1 - 1
configure.in

@@ -18,7 +18,7 @@ AC_CONFIG_AUX_DIR(buildlib)
 AC_CONFIG_HEADER(include/config.h:buildlib/config.h.in include/apti18n.h:buildlib/apti18n.h.in)
 
 dnl -- SET THIS TO THE RELEASE VERSION --
-AC_DEFINE_UNQUOTED(VERSION,"0.6.46.4ubuntu6")
+AC_DEFINE_UNQUOTED(VERSION,"0.6.46.4ubuntu7")
 PACKAGE="apt"
 AC_DEFINE_UNQUOTED(PACKAGE,"$PACKAGE")
 AC_SUBST(PACKAGE)

+ 1 - 0
debian/apt.dirs

@@ -6,5 +6,6 @@ etc/apt/apt.conf.d
 etc/apt/sources.list.d
 var/cache/apt/archives/partial
 var/lib/apt/lists/partial
+var/lib/apt/mirrors/partial
 var/lib/apt/periodic
 usr/share/bug/apt

+ 12 - 0
debian/changelog

@@ -1,3 +1,15 @@
+apt (0.6.46.4ubuntu7) feisty; urgency=low
+
+  * Merged the apt--mirror branch. This means that a new 'mirror' 
+    method is available that will allow dynamic mirror updates.
+    The sources.list entry looks something like this:
+    "deb mirror://mirrors.lp.net/get_mirror feisty main restricted"
+
+    It also supports error reporting to a configurable url for mirror
+    problems/failures.
+
+ -- Michael Vogt <michael.vogt@ubuntu.com>  Tue,  6 Feb 2007 11:38:06 +0100
+
 apt (0.6.46.4ubuntu6) feisty; urgency=low
 
   * methods/http.cc:

+ 4 - 0
debian/rules

@@ -219,6 +219,10 @@ apt: build debian/shlibs.local
 	rm -f build/po/*.pot
 	rm -f po/*.pot
 
+	# move the mirror failure script in place
+	mv debian/$@/usr/bin/apt-report-mirror-failure \
+	   debian/$@/usr/lib/apt/apt-report-mirror-failure \
+
 	dh_installexamples -p$@ $(BLD)/docs/examples/*
 	dh_installman -p$@ 
 	dh_installcron -p$@

+ 13 - 1
doc/examples/configure-index

@@ -180,6 +180,17 @@ Acquire
   {
    Options {"--ignore-time-conflict";}	// not very usefull on a normal system
   };
+
+  mirror
+  {
+   RefreshInterval "360"; // refresh interval in minutes
+   MaxAge "90";           // max age for a mirror file in days before 
+                          // it gets deleted
+   // mirror failure reporting script
+   ProblemReporting "/usr/lib/apt/apt-report-mirror-failure"; 
+   // mirror failure reporting url
+   ReportFailures "http://example.com/mirror-failure"; 
+  };
 };
 
 // Directory layout
@@ -193,6 +204,7 @@ Dir "/"
      userstatus "status.user";
      status "/var/lib/dpkg/status";
      cdroms "cdroms.list";
+     mirrors "mirrors/";
   };
   
   // Location of the cache dir
@@ -283,9 +295,9 @@ Debug
   Acquire::Http "false";   // Show http command traffic
   Acquire::Https "false";   // Show https debug
   Acquire::gpgv "false";   // Show the gpgv traffic
+  Acquire::Mirror "false"; // Show debugging of the mirror method
   aptcdrom "false";        // Show found package files
   IdentCdrom "false";
-  
 }
 
 /* Whatever you do, do not use this configuration file!! Take out ONLY

+ 9 - 9
methods/connect.cc

@@ -18,6 +18,7 @@
 #include <stdio.h>
 #include <errno.h>
 #include <unistd.h>
+#include <sstream>
 
 // Internet stuff
 #include <netinet/in.h>
@@ -67,12 +68,10 @@ static bool DoConnect(struct addrinfo *Addr,string Host,
       wrong this will get tacked onto the end of the error message */
    if (LastHostAddr->ai_next != 0)
    {
-      char Name2[NI_MAXHOST + NI_MAXSERV + 10];
-      snprintf(Name2,sizeof(Name2),_("[IP: %s %s]"),Name,Service);
-      Owner->SetFailExtraMsg(string(Name2));
-   }   
-   else
-      Owner->SetFailExtraMsg("");
+      std::stringstream ss;
+      ioprintf(ss, _("[IP: %s %s]"),Name,Service);
+      Owner->SetIP(ss.str());
+   }
       
    // Get a socket
    if ((Fd = socket(Addr->ai_family,Addr->ai_socktype,
@@ -89,7 +88,7 @@ static bool DoConnect(struct addrinfo *Addr,string Host,
    /* This implements a timeout for connect by opening the connection
       nonblocking */
    if (WaitFd(Fd,true,TimeOut) == false) {
-      Owner->SetFailExtraMsg("\nFailReason: Timeout");
+      Owner->SetFailReason("Timeout");
       return _error->Error(_("Could not connect to %s:%s (%s), "
 			   "connection timed out"),Host.c_str(),Service,Name);
    }
@@ -104,7 +103,7 @@ static bool DoConnect(struct addrinfo *Addr,string Host,
    {
       errno = Err;
       if(errno == ECONNREFUSED)
-         Owner->SetFailExtraMsg("\nFailReason: ConnectionRefused");
+         Owner->SetFailReason("ConnectionRefused");
       return _error->Errno("connect",_("Could not connect to %s:%s (%s)."),Host.c_str(),
 			   Service,Name);
    }
@@ -164,12 +163,13 @@ bool Connect(string Host,int Port,const char *Service,int DefPort,int &Fd,
 		  DefPort = 0;
 		  continue;
 	       }
+	       Owner->SetFailReason("ResolveFailure");
 	       return _error->Error(_("Could not resolve '%s'"),Host.c_str());
 	    }
 	    
 	    if (Res == EAI_AGAIN)
 	    {
-	       Owner->SetFailExtraMsg("\nFailReason: TmpResolveFailure");
+	       Owner->SetFailReason("TmpResolveFailure");
 	       return _error->Error(_("Temporary failure resolving '%s'"),
 				    Host.c_str());
 	    }

+ 5 - 11
methods/http.cc

@@ -38,7 +38,6 @@
 #include <stdio.h>
 #include <errno.h>
 #include <string.h>
-#include <iostream>
 #include <apti18n.h>
 
 // Internet stuff
@@ -58,7 +57,7 @@ time_t HttpMethod::FailTime = 0;
 unsigned long PipelineDepth = 10;
 unsigned long TimeOut = 120;
 bool Debug = false;
-
+URI Proxy;
 
 unsigned long CircleBuf::BwReadLimit=0;
 unsigned long CircleBuf::BwTickReadData=0;
@@ -916,6 +915,9 @@ int HttpMethod::DealWithHeaders(FetchResult &Res,ServerState *Srv)
       failure */
    if (Srv->Result < 200 || Srv->Result >= 300)
    {
+      char err[255];
+      snprintf(err,sizeof(err)-1,"HttpError%i",Srv->Result);
+      SetFailReason(err);
       _error->Error("%u %s",Srv->Result,Srv->Code);
       if (Srv->HaveContent == true)
 	 return 4;
@@ -991,7 +993,7 @@ void HttpMethod::SigTerm(int)
    depth. */
 bool HttpMethod::Fetch(FetchItem *)
 {
-   if (Server == 0)
+   if (Server == 0) 
       return true;
 
    // Queue the requests
@@ -1224,13 +1226,5 @@ int HttpMethod::Loop()
 }
 									/*}}}*/
 
-int main()
-{
-   setlocale(LC_ALL, "");
-
-   HttpMethod Mth;
-   
-   return Mth.Loop();
-}
 
 

+ 4 - 3
methods/http.h

@@ -13,7 +13,7 @@
 
 #define MAXLEN 360
 
-#include <iostream>
+
 
 using std::cout;
 using std::endl;
@@ -134,7 +134,6 @@ class HttpMethod : public pkgAcqMethod
    bool ServerDie(ServerState *Srv);
    int DealWithHeaders(FetchResult &Res,ServerState *Srv);
 
-   virtual bool Fetch(FetchItem *);
    virtual bool Configuration(string Message);
    
    // In the event of a fatal signal this file will be closed and timestamped.
@@ -142,6 +141,9 @@ class HttpMethod : public pkgAcqMethod
    static int FailFd;
    static time_t FailTime;
    static void SigTerm(int);
+
+   protected:
+   virtual bool Fetch(FetchItem *);
    
    public:
    friend class ServerState;
@@ -158,6 +160,5 @@ class HttpMethod : public pkgAcqMethod
    };
 };
 
-URI Proxy;
 
 #endif

+ 15 - 0
methods/http_main.cc

@@ -0,0 +1,15 @@
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/acquire-method.h>
+
+#include "connect.h"
+#include "rfc2553emu.h"
+#include "http.h"
+
+
+int main()
+{
+   setlocale(LC_ALL, "");
+
+   HttpMethod Mth;
+   return Mth.Loop();
+}

+ 8 - 1
methods/makefile

@@ -49,7 +49,7 @@ include $(PROGRAM_H)
 PROGRAM=http
 SLIBS = -lapt-pkg $(SOCKETLIBS)
 LIB_MAKES = apt-pkg/makefile
-SOURCE = http.cc rfc2553emu.cc connect.cc
+SOURCE = http.cc http_main.cc rfc2553emu.cc connect.cc
 include $(PROGRAM_H)
 
 # The https method
@@ -73,6 +73,13 @@ LIB_MAKES = apt-pkg/makefile
 SOURCE = rsh.cc
 include $(PROGRAM_H)
 
+# The mirror method
+PROGRAM=mirror
+SLIBS = -lapt-pkg $(SOCKETLIBS)
+LIB_MAKES = apt-pkg/makefile
+SOURCE = mirror.cc http.cc rfc2553emu.cc connect.cc
+include $(PROGRAM_H)
+
 # SSH and vzip2 method symlink
 binary: $(BIN)/ssh $(BIN)/bzip2
 veryclean: clean-$(BIN)/ssh clean-$(BIN)/bzip2

+ 282 - 0
methods/mirror.cc

@@ -0,0 +1,282 @@
+// -*- mode: cpp; mode: fold -*-
+// Description								/*{{{*/
+// $Id: mirror.cc,v 1.59 2004/05/08 19:42:35 mdz Exp $
+/* ######################################################################
+
+   Mirror Aquire Method - This is the Mirror aquire method for APT.
+   
+   ##################################################################### */
+									/*}}}*/
+// Include Files							/*{{{*/
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/acquire-method.h>
+#include <apt-pkg/acquire-item.h>
+#include <apt-pkg/acquire.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/hashes.h>
+#include <apt-pkg/sourcelist.h>
+
+#include <fstream>
+#include <iostream>
+#include <stdarg.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+
+using namespace std;
+
+#include "mirror.h"
+#include "http.h"
+#include "apti18n.h"
+									/*}}}*/
+
+/* Done:
+ * - works with http (only!)
+ * - always picks the first mirror from the list
+ * - call out to problem reporting script
+ * - supports "deb mirror://host/path/to/mirror-list/// dist component"
+ * - uses pkgAcqMethod::FailReason() to have a string representation
+ *   of the failure that is also send to LP
+ * 
+ * TODO: 
+ * - deal with runing as non-root because we can't write to the lists 
+     dir then -> use the cached mirror file
+ * - better method to download than having a pkgAcquire interface here
+ *   and better error handling there!
+ * - support more than http
+ * - testing :)
+ */
+
+MirrorMethod::MirrorMethod()
+   : HttpMethod(), HasMirrorFile(false)
+{
+};
+
+// HttpMethod::Configuration - Handle a configuration message		/*{{{*/
+// ---------------------------------------------------------------------
+/* We stash the desired pipeline depth */
+bool MirrorMethod::Configuration(string Message)
+{
+   if (pkgAcqMethod::Configuration(Message) == false)
+      return false;
+   Debug = _config->FindB("Debug::Acquire::mirror",false);
+   
+   return true;
+}
+									/*}}}*/
+
+// clean the mirrors dir based on ttl information
+bool MirrorMethod::Clean(string Dir)
+{
+   vector<metaIndex *>::const_iterator I;
+
+   if(Debug)
+      clog << "MirrorMethod::Clean(): " << Dir << endl;
+
+   // read sources.list
+   pkgSourceList list;
+   list.ReadMainList();
+
+   DIR *D = opendir(Dir.c_str());   
+   if (D == 0)
+      return _error->Errno("opendir",_("Unable to read %s"),Dir.c_str());
+   
+   string StartDir = SafeGetCWD();
+   if (chdir(Dir.c_str()) != 0)
+   {
+      closedir(D);
+      return _error->Errno("chdir",_("Unable to change to %s"),Dir.c_str());
+   }
+   
+   for (struct dirent *Dir = readdir(D); Dir != 0; Dir = readdir(D))
+   {
+      // Skip some files..
+      if (strcmp(Dir->d_name,"lock") == 0 ||
+	  strcmp(Dir->d_name,"partial") == 0 ||
+	  strcmp(Dir->d_name,".") == 0 ||
+	  strcmp(Dir->d_name,"..") == 0)
+	 continue;
+
+      // see if we have that uri
+      for(I=list.begin(); I != list.end(); I++)
+      {
+	 string uri = (*I)->GetURI();
+	 if(uri.substr(0,strlen("mirror://")) != string("mirror://"))
+	    continue;
+	 string BaseUri = uri.substr(0,uri.size()-1);
+	 if (URItoFileName(BaseUri) == Dir->d_name)
+	    break;
+      }
+      // nothing found, nuke it
+      if (I == list.end())
+	 unlink(Dir->d_name);
+   };
+   
+   chdir(StartDir.c_str());
+   closedir(D);
+   return true;   
+}
+
+
+bool MirrorMethod::GetMirrorFile(string mirror_uri_str)
+{
+   /* 
+    - a mirror_uri_str looks like this:
+    mirror://people.ubuntu.com/~mvo/apt/mirror/mirrors/dists/feisty/Release.gpg
+   
+    - the matching source.list entry
+    deb mirror://people.ubuntu.com/~mvo/apt/mirror/mirrors feisty main
+   
+    - we actually want to go after:
+    http://people.ubuntu.com/~mvo/apt/mirror/mirrors
+
+    And we need to save the BaseUri for later:
+    - mirror://people.ubuntu.com/~mvo/apt/mirror/mirrors
+
+   FIXME: what if we have two similar prefixes?
+     mirror://people.ubuntu.com/~mvo/mirror
+     mirror://people.ubuntu.com/~mvo/mirror2
+   then mirror_uri_str looks like:
+     mirror://people.ubuntu.com/~mvo/apt/mirror/dists/feisty/Release.gpg
+     mirror://people.ubuntu.com/~mvo/apt/mirror2/dists/feisty/Release.gpg
+   we search sources.list and find:
+     mirror://people.ubuntu.com/~mvo/apt/mirror
+   in both cases! So we need to apply some domain knowledge here :( and
+   check for /dists/ or /Release.gpg as suffixes
+   */
+   if(Debug)
+      std::cerr << "GetMirrorFile: " << mirror_uri_str << std::endl;
+
+   // read sources.list and find match
+   vector<metaIndex *>::const_iterator I;
+   pkgSourceList list;
+   list.ReadMainList();
+   for(I=list.begin(); I != list.end(); I++)
+   {
+      string uristr = (*I)->GetURI();
+      if(Debug)
+	 std::cerr << "Checking: " << uristr << std::endl;
+      if(uristr.substr(0,strlen("mirror://")) != string("mirror://"))
+	 continue;
+      // find matching uri in sources.list
+      if(mirror_uri_str.substr(0,uristr.size()) == uristr)
+      {
+	 if(Debug)
+	    std::cerr << "found BaseURI: " << uristr << std::endl;
+	 BaseUri = uristr.substr(0,uristr.size()-1);
+      }
+   }
+   string fetch = BaseUri;
+   fetch.replace(0,strlen("mirror://"),"http://");
+
+   // get new file
+   MirrorFile = _config->FindDir("Dir::State::mirrors") + URItoFileName(BaseUri);
+
+   if(Debug) 
+   {
+      cerr << "base-uri: " << BaseUri << endl;
+      cerr << "mirror-file: " << MirrorFile << endl;
+   }
+
+   // check the file, if it is not older than RefreshInterval just use it
+   // otherwise try to get a new one
+   if(FileExists(MirrorFile)) 
+   {
+      struct stat buf;
+      time_t t,now,refresh;
+      if(stat(MirrorFile.c_str(), &buf) != 0)
+	 return false;
+      t = std::max(buf.st_mtime, buf.st_ctime);
+      now = time(NULL);
+      refresh = 60*_config->FindI("Acquire::Mirror::RefreshInterval",360);
+      if(t + refresh > now)
+      {
+	 if(Debug)
+	    clog << "Mirror file is in RefreshInterval" << endl;
+	 HasMirrorFile = true;
+	 return true;
+      }
+      if(Debug)
+	 clog << "Mirror file " << MirrorFile << " older than " << refresh << "min, re-download it" << endl;
+   }
+
+   // not that great to use pkgAcquire here, but we do not have 
+   // any other way right now
+   pkgAcquire Fetcher;
+   new pkgAcqFile(&Fetcher, fetch, "", 0, "", "", "", MirrorFile);
+   bool res = (Fetcher.Run() == pkgAcquire::Continue);
+   if(res)
+      HasMirrorFile = true;
+   Fetcher.Shutdown();
+   return res;
+}
+
+bool MirrorMethod::SelectMirror()
+{
+   // FIXME: make the mirror selection more clever, do not 
+   //        just use the first one!
+   ifstream in(MirrorFile.c_str());
+   getline(in, Mirror);
+   if(Debug)
+      cerr << "Using mirror: " << Mirror << endl;
+
+   UsedMirror = Mirror;
+   return true;
+}
+
+// MirrorMethod::Fetch - Fetch an item					/*{{{*/
+// ---------------------------------------------------------------------
+/* This adds an item to the pipeline. We keep the pipeline at a fixed
+   depth. */
+bool MirrorMethod::Fetch(FetchItem *Itm)
+{
+   // select mirror only once per session
+   if(!HasMirrorFile)
+   {
+      Clean(_config->FindDir("Dir::State::mirrors"));
+      GetMirrorFile(Itm->Uri);
+      SelectMirror();
+   }
+
+   for (FetchItem *I = Queue; I != 0; I = I->Next)
+   {
+      if(I->Uri.find("mirror://") != string::npos)
+	 I->Uri.replace(0,BaseUri.size(),Mirror);
+   }
+
+   // now run the real fetcher
+   return HttpMethod::Fetch(Itm);
+};
+
+void MirrorMethod::Fail(string Err,bool Transient)
+{
+   if(Queue->Uri.find("http://") != string::npos)
+      Queue->Uri.replace(0,Mirror.size(), BaseUri);
+   pkgAcqMethod::Fail(Err, Transient);
+}
+
+void MirrorMethod::URIStart(FetchResult &Res)
+{
+   if(Queue->Uri.find("http://") != string::npos)
+      Queue->Uri.replace(0,Mirror.size(), BaseUri);
+   pkgAcqMethod::URIStart(Res);
+}
+
+void MirrorMethod::URIDone(FetchResult &Res,FetchResult *Alt)
+{
+   if(Queue->Uri.find("http://") != string::npos)
+      Queue->Uri.replace(0,Mirror.size(), BaseUri);
+   pkgAcqMethod::URIDone(Res, Alt);
+}
+
+
+int main()
+{
+   setlocale(LC_ALL, "");
+
+   MirrorMethod Mth;
+
+   return Mth.Loop();
+}
+
+

+ 51 - 0
methods/mirror.h

@@ -0,0 +1,51 @@
+// -*- mode: cpp; mode: fold -*-
+// Description								/*{{{*/// $Id: http.h,v 1.12 2002/04/18 05:09:38 jgg Exp $
+// $Id: http.h,v 1.12 2002/04/18 05:09:38 jgg Exp $
+/* ######################################################################
+
+   MIRROR Aquire Method - This is the MIRROR aquire method for APT.
+
+   ##################################################################### */
+									/*}}}*/
+
+#ifndef APT_MIRROR_H
+#define APT_MIRROR_H
+
+
+#include <iostream>
+
+using std::cout;
+using std::cerr;
+using std::endl;
+
+#include "http.h"
+
+class MirrorMethod : public HttpMethod
+{
+   FetchResult Res;
+   // we simply transform between BaseUri and Mirror
+   string BaseUri;    // the original mirror://... url
+   string Mirror;     // the selected mirror uri (http://...)
+   string MirrorFile; // 
+   bool HasMirrorFile;
+
+   bool Debug;
+
+ protected:
+   bool GetMirrorFile(string uri);
+   bool SelectMirror();
+   bool Clean(string dir);
+   
+   // we need to overwrite those to transform the url back
+   virtual void Fail(string Why, bool Transient = false);
+   virtual void URIStart(FetchResult &Res);
+   virtual void URIDone(FetchResult &Res,FetchResult *Alt = 0);
+   virtual bool Configuration(string Message);
+
+ public:
+   MirrorMethod();
+   virtual bool Fetch(FetchItem *Itm);
+};
+
+
+#endif

+ 23 - 0
mirror-failure.py

@@ -0,0 +1,23 @@
+# File: cgihttpserver-example-1.py
+
+import CGIHTTPServer
+import BaseHTTPServer
+
+class Handler(CGIHTTPServer.CGIHTTPRequestHandler):
+    #cgi_directories = ["/cgi"]
+    def do_POST(self):
+	print "do_POST"
+        #print self.command
+        #print self.path
+        #print self.headers
+        print self.client_address
+        data = self.rfile.read(int(self.headers["content-length"]))
+        print data
+        self.wfile.write("200 Ok\n");
+
+PORT = 8000
+
+httpd = BaseHTTPServer.HTTPServer(("", PORT), Handler)
+print "serving at port", PORT
+httpd.serve_forever()
+

File diff suppressed because it is too large
+ 266 - 254
po/apt-all.pot