summaryrefslogtreecommitdiff
path: root/methods
diff options
context:
space:
mode:
Diffstat (limited to 'methods')
-rw-r--r--methods/cdrom.cc12
-rw-r--r--methods/connect.cc18
-rw-r--r--methods/http.cc19
-rw-r--r--methods/http.h7
-rw-r--r--methods/http_main.cc15
-rw-r--r--methods/https.cc249
-rw-r--r--methods/https.h48
-rw-r--r--methods/makefile18
-rw-r--r--methods/mirror.cc282
-rw-r--r--methods/mirror.h51
10 files changed, 688 insertions, 31 deletions
diff --git a/methods/cdrom.cc b/methods/cdrom.cc
index d6b8eae75..601bc11c9 100644
--- a/methods/cdrom.cc
+++ b/methods/cdrom.cc
@@ -30,7 +30,7 @@ class CDROMMethod : public pkgAcqMethod
::Configuration Database;
string CurrentID;
string CDROM;
- bool Mounted;
+ bool MountedByApt;
virtual bool Fetch(FetchItem *Itm);
string GetID(string Name);
@@ -48,7 +48,7 @@ CDROMMethod::CDROMMethod() : pkgAcqMethod("1.0",SingleInstance | LocalOnly |
SendConfig | NeedsCleanup |
Removable),
DatabaseLoaded(false),
- Mounted(false)
+ MountedByApt(false)
{
};
/*}}}*/
@@ -57,7 +57,7 @@ CDROMMethod::CDROMMethod() : pkgAcqMethod("1.0",SingleInstance | LocalOnly |
/* */
void CDROMMethod::Exit()
{
- if (Mounted == true)
+ if (MountedByApt == true)
UnmountCdrom(CDROM);
}
/*}}}*/
@@ -139,7 +139,8 @@ bool CDROMMethod::Fetch(FetchItem *Itm)
while (CurrentID.empty() == true)
{
bool Hit = false;
- Mounted = MountCdrom(CDROM);
+ if(!IsMounted(CDROM))
+ MountedByApt = MountCdrom(CDROM);
for (unsigned int Version = 2; Version != 0; Version--)
{
if (IdentCdrom(CDROM,NewID,Version) == false)
@@ -160,7 +161,8 @@ bool CDROMMethod::Fetch(FetchItem *Itm)
break;
// I suppose this should prompt somehow?
- if (UnmountCdrom(CDROM) == false)
+ if (_config->FindB("APT::CDROM::NoMount",false) == false &&
+ UnmountCdrom(CDROM) == false)
return _error->Error(_("Unable to unmount the CD-ROM in %s, it may still be in use."),
CDROM.c_str());
if (MediaFail(Get.Host,CDROM) == false)
diff --git a/methods/connect.cc b/methods/connect.cc
index 8c2ac6d56..145001fb3 100644
--- a/methods/connect.cc
+++ b/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());
}
diff --git a/methods/http.cc b/methods/http.cc
index c6623c46f..ecfb80bd2 100644
--- a/methods/http.cc
+++ b/methods/http.cc
@@ -38,12 +38,12 @@
#include <stdio.h>
#include <errno.h>
#include <string.h>
-#include <iostream>
#include <apti18n.h>
// Internet stuff
#include <netdb.h>
+#include "config.h"
#include "connect.h"
#include "rfc2553emu.h"
#include "http.h"
@@ -57,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;
@@ -714,7 +714,7 @@ void HttpMethod::SendReq(FetchItem *Itm,CircleBuf &Out)
Req += string("Authorization: Basic ") +
Base64Encode(Uri.User + ":" + Uri.Password) + "\r\n";
- Req += "User-Agent: Debian APT-HTTP/1.3\r\n\r\n";
+ Req += "User-Agent: Ubuntu APT-HTTP/1.3 ("VERSION")\r\n\r\n";
if (Debug == true)
cerr << Req << endl;
@@ -915,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;
@@ -990,7 +993,7 @@ void HttpMethod::SigTerm(int)
depth. */
bool HttpMethod::Fetch(FetchItem *)
{
- if (Server == 0)
+ if (Server == 0)
return true;
// Queue the requests
@@ -1223,13 +1226,5 @@ int HttpMethod::Loop()
}
/*}}}*/
-int main()
-{
- setlocale(LC_ALL, "");
-
- HttpMethod Mth;
-
- return Mth.Loop();
-}
diff --git a/methods/http.h b/methods/http.h
index 541e2952c..5eac11401 100644
--- a/methods/http.h
+++ b/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
diff --git a/methods/http_main.cc b/methods/http_main.cc
new file mode 100644
index 000000000..2c46ab19d
--- /dev/null
+++ b/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();
+}
diff --git a/methods/https.cc b/methods/https.cc
new file mode 100644
index 000000000..b758e4ab3
--- /dev/null
+++ b/methods/https.cc
@@ -0,0 +1,249 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+// $Id: http.cc,v 1.59 2004/05/08 19:42:35 mdz Exp $
+/* ######################################################################
+
+ HTTPS Aquire Method - This is the HTTPS aquire method for APT.
+
+ It uses libcurl
+
+ ##################################################################### */
+ /*}}}*/
+// Include Files /*{{{*/
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/acquire-method.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/hashes.h>
+
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <utime.h>
+#include <unistd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <iostream>
+#include <apti18n.h>
+#include <sstream>
+
+#include "config.h"
+#include "https.h"
+
+ /*}}}*/
+using namespace std;
+
+size_t
+HttpsMethod::write_data(void *buffer, size_t size, size_t nmemb, void *userp)
+{
+ HttpsMethod *me = (HttpsMethod *)userp;
+
+ if(me->File->Write(buffer, size*nmemb) != true)
+ return false;
+
+ return size*nmemb;
+}
+
+int
+HttpsMethod::progress_callback(void *clientp, double dltotal, double dlnow,
+ double ultotal, double ulnow)
+{
+ HttpsMethod *me = (HttpsMethod *)clientp;
+ if(dltotal > 0 && me->Res.Size == 0) {
+ me->Res.Size = dltotal;
+ me->URIStart(me->Res);
+ }
+ return 0;
+}
+
+bool HttpsMethod::SetupProxy()
+{
+ URI ServerName = Queue->Uri;
+
+ // Determine the proxy setting
+ if (getenv("http_proxy") == 0)
+ {
+ string DefProxy = _config->Find("Acquire::http::Proxy");
+ string SpecificProxy = _config->Find("Acquire::http::Proxy::" + ServerName.Host);
+ if (SpecificProxy.empty() == false)
+ {
+ if (SpecificProxy == "DIRECT")
+ Proxy = "";
+ else
+ Proxy = SpecificProxy;
+ }
+ else
+ Proxy = DefProxy;
+ }
+
+ // Parse no_proxy, a , separated list of domains
+ if (getenv("no_proxy") != 0)
+ {
+ if (CheckDomainList(ServerName.Host,getenv("no_proxy")) == true)
+ Proxy = "";
+ }
+
+ // Determine what host and port to use based on the proxy settings
+ int Port = 0;
+ string Host;
+ if (Proxy.empty() == true || Proxy.Host.empty() == true)
+ {
+ }
+ else
+ {
+ if (Proxy.Port != 0)
+ curl_easy_setopt(curl, CURLOPT_PROXYPORT, Proxy.Port);
+ curl_easy_setopt(curl, CURLOPT_PROXY, Proxy.Host.c_str());
+ }
+}
+
+
+// HttpsMethod::Fetch - Fetch an item /*{{{*/
+// ---------------------------------------------------------------------
+/* This adds an item to the pipeline. We keep the pipeline at a fixed
+ depth. */
+bool HttpsMethod::Fetch(FetchItem *Itm)
+{
+ stringstream ss;
+ struct stat SBuf;
+ struct curl_slist *headers=NULL;
+ char curl_errorstr[CURL_ERROR_SIZE];
+
+ // TODO:
+ // - http::Timeout
+ // - http::Pipeline-Depth
+ // - error checking/reporting
+ // - more debug options? (CURLOPT_DEBUGFUNCTION?)
+
+ SetupProxy();
+
+ // callbacks
+ curl_easy_setopt(curl, CURLOPT_URL, Itm->Uri.c_str());
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, this);
+ curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, progress_callback);
+ curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, this);
+ curl_easy_setopt(curl, CURLOPT_NOPROGRESS, false);
+ curl_easy_setopt(curl, CURLOPT_FAILONERROR, true);
+
+ // FIXME: https: offer various options of verification
+ bool peer_verify = _config->FindB("Acquire::https::Verify-Peer", false);
+ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, peer_verify);
+
+ // sslcert file
+ string pem = _config->Find("Acquire::https::SslCert","");
+ if(pem != "")
+ curl_easy_setopt(curl, CURLOPT_SSLCERT, pem.c_str());
+
+ // CA-Dir
+ string certdir = _config->Find("Acquire::https::CaPath","");
+ if(certdir != "")
+ curl_easy_setopt(curl, CURLOPT_CAPATH, certdir.c_str());
+
+ // Server-verify
+ int verify = _config->FindI("Acquire::https::Verify-Host",2);
+ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, verify);
+
+ // cache-control
+ if(_config->FindB("Acquire::http::No-Cache",false) == false)
+ {
+ // cache enabled
+ if (_config->FindB("Acquire::http::No-Store",false) == true)
+ headers = curl_slist_append(headers,"Cache-Control: no-store");
+ ioprintf(ss, "Cache-Control: max-age=%u", _config->FindI("Acquire::http::Max-Age",0));
+ headers = curl_slist_append(headers, ss.str().c_str());
+ } else {
+ // cache disabled by user
+ headers = curl_slist_append(headers, "Cache-Control: no-cache");
+ headers = curl_slist_append(headers, "Pragma: no-cache");
+ }
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
+
+ // set time values
+ curl_easy_setopt(curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE);
+ curl_easy_setopt(curl, CURLOPT_TIMEVALUE, Itm->LastModified);
+
+ // speed limit
+ int dlLimit = _config->FindI("Acquire::http::Dl-Limit",0)*1024;
+ if (dlLimit > 0)
+ curl_easy_setopt(curl, CURLOPT_MAX_RECV_SPEED_LARGE, dlLimit);
+
+ // set header
+ curl_easy_setopt(curl, CURLOPT_USERAGENT,"Debian APT-CURL/1.0 ("VERSION")");
+
+ // debug
+ if(_config->FindB("Debug::Acquire::https", false))
+ curl_easy_setopt(curl, CURLOPT_VERBOSE, true);
+
+ // error handling
+ curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_errorstr);
+
+ // In this case we send an if-range query with a range header
+ if (stat(Itm->DestFile.c_str(),&SBuf) >= 0 && SBuf.st_size > 0)
+ curl_easy_setopt(curl, CURLOPT_RESUME_FROM, (long)SBuf.st_size);
+
+ // go for it - if the file exists, append on it
+ File = new FileFd(Itm->DestFile, FileFd::WriteAny);
+ File->Seek(File->Size());
+
+ // keep apt updated
+ Res.Filename = Itm->DestFile;
+
+ // get it!
+ CURLcode success = curl_easy_perform(curl);
+
+
+ // cleanup
+ if(success != 0) {
+ _error->Error(curl_errorstr);
+ Fail();
+ return true;
+ }
+
+ if (Res.Size == 0)
+ Res.Size = File->Size();
+
+ // check the downloaded result
+ struct stat Buf;
+ if (stat(File->Name().c_str(),&Buf) == 0)
+ {
+ Res.Size = Buf.st_size;
+ Res.Filename = File->Name();
+ Res.LastModified = Buf.st_mtime;
+ Res.IMSHit = false;
+ if (Itm->LastModified != 0 && Buf.st_mtime >= Itm->LastModified)
+ {
+ Res.IMSHit = true;
+ Res.LastModified = Itm->LastModified;
+ }
+ }
+
+ // take hashes
+ Hashes Hash;
+ FileFd Fd(Res.Filename, FileFd::ReadOnly);
+ Hash.AddFD(Fd.Fd(), Fd.Size());
+ Res.TakeHashes(Hash);
+
+ // keep apt updated
+ URIDone(Res);
+
+ // cleanup
+ File->Close();
+ Res.Size = 0;
+ delete File;
+ curl_slist_free_all(headers);
+
+ return true;
+};
+
+int main()
+{
+ setlocale(LC_ALL, "");
+
+ HttpsMethod Mth;
+ curl_global_init(CURL_GLOBAL_SSL) ;
+
+ return Mth.Run();
+}
+
+
diff --git a/methods/https.h b/methods/https.h
new file mode 100644
index 000000000..6620a10fc
--- /dev/null
+++ b/methods/https.h
@@ -0,0 +1,48 @@
+// -*- 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 $
+/* ######################################################################
+
+ HTTP Aquire Method - This is the HTTP aquire method for APT.
+
+ ##################################################################### */
+ /*}}}*/
+
+#ifndef APT_HTTP_H
+#define APT_HTTP_H
+
+#define MAXLEN 360
+
+#include <iostream>
+#include <curl/curl.h>
+
+using std::cout;
+using std::endl;
+
+class HttpsMethod;
+
+
+class HttpsMethod : public pkgAcqMethod
+{
+
+ virtual bool Fetch(FetchItem *);
+ static size_t write_data(void *buffer, size_t size, size_t nmemb, void *userp);
+ static int progress_callback(void *clientp, double dltotal, double dlnow,
+ double ultotal, double ulnow);
+ bool SetupProxy();
+ CURL *curl;
+ FetchResult Res;
+
+ public:
+ FileFd *File;
+
+ HttpsMethod() : pkgAcqMethod("1.2",Pipeline | SendConfig)
+ {
+ File = 0;
+ curl = curl_easy_init();
+ };
+};
+
+URI Proxy;
+
+#endif
diff --git a/methods/makefile b/methods/makefile
index 1e3b1ef85..610bd5b49 100644
--- a/methods/makefile
+++ b/methods/makefile
@@ -7,7 +7,7 @@ include ../buildlib/defaults.mak
BIN := $(BIN)/methods
# FIXME..
-LIB_APT_PKG_MAJOR = 3.11
+LIB_APT_PKG_MAJOR = 3.53
APT_DOMAIN := libapt-pkg$(LIB_APT_PKG_MAJOR)
# The file method
@@ -49,7 +49,14 @@ 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
+PROGRAM=https
+SLIBS = -lapt-pkg -lcurl
+LIB_MAKES = apt-pkg/makefile
+SOURCE = https.cc
include $(PROGRAM_H)
# The ftp method
@@ -66,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
diff --git a/methods/mirror.cc b/methods/mirror.cc
new file mode 100644
index 000000000..9fe64fd70
--- /dev/null
+++ b/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();
+}
+
+
diff --git a/methods/mirror.h b/methods/mirror.h
new file mode 100644
index 000000000..798f5a9b5
--- /dev/null
+++ b/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