+bool MirrorMethod::DownloadMirrorFile(string mirror_uri_str)
+{
+ // not that great to use pkgAcquire here, but we do not have
+ // any other way right now
+ string fetch = BaseUri;
+ fetch.replace(0,strlen("mirror://"),"http://");
+
+#if 0 // no need for this, the getArchitectures() will also include the main
+ // arch
+ // append main architecture
+ fetch += "?arch=" + _config->Find("Apt::Architecture");
+#endif
+
+ // append all architectures
+ std::vector<std::string> vec = APT::Configuration::getArchitectures();
+ for (std::vector<std::string>::const_iterator I = vec.begin();
+ I != vec.end(); ++I)
+ if (I == vec.begin())
+ fetch += "?arch=" + (*I);
+ else
+ fetch += "&arch=" + (*I);
+
+ // append the dist as a query string
+ if (Dist != "")
+ fetch += "&dist=" + Dist;
+
+ if(Debug)
+ clog << "MirrorMethod::DownloadMirrorFile(): '" << fetch << "'"
+ << " to " << MirrorFile << endl;
+
+ pkgAcquire Fetcher;
+ new pkgAcqFile(&Fetcher, fetch, "", 0, "", "", "", MirrorFile);
+ bool res = (Fetcher.Run() == pkgAcquire::Continue);
+ if(res) {
+ DownloadedMirrorFile = true;
+ chmod(MirrorFile.c_str(), 0644);
+ }
+ Fetcher.Shutdown();
+
+ if(Debug)
+ clog << "MirrorMethod::DownloadMirrorFile() success: " << res << endl;
+
+ return res;
+}
+
+// Randomizes the lines in the mirror file, this is used so that
+// we spread the load on the mirrors evenly
+bool MirrorMethod::RandomizeMirrorFile(string mirror_file)
+{
+ vector<string> content;
+ string line;
+
+ if (!FileExists(mirror_file))
+ return false;
+
+ // read
+ ifstream in(mirror_file.c_str());
+ while ( !in.eof() ) {
+ getline(in, line);
+ content.push_back(line);
+ }
+
+ // we want the file to be random for each different machine, but also
+ // "stable" on the same machine. this is to avoid running into out-of-sync
+ // issues (i.e. Release/Release.gpg different on each mirror)
+ struct utsname buf;
+ int seed=1, i;
+ if(uname(&buf) == 0) {
+ for(i=0,seed=1; buf.nodename[i] != 0; i++) {
+ seed = seed * 31 + buf.nodename[i];
+ }
+ }
+ srand( seed );
+ random_shuffle(content.begin(), content.end());
+
+ // write
+ ofstream out(mirror_file.c_str());
+ while ( !content.empty()) {
+ line = content.back();
+ content.pop_back();
+ out << line << "\n";
+ }
+
+ return true;
+}
+
+/* convert a the Queue->Uri back to the mirror base uri and look
+ * at all mirrors we have for this, this is needed as queue->uri
+ * may point to different mirrors (if TryNextMirror() was run)
+ */
+void MirrorMethod::CurrentQueueUriToMirror()
+{
+ // already in mirror:// style so nothing to do
+ if(Queue->Uri.find("mirror://") == 0)
+ return;
+
+ // find current mirror and select next one
+ for (vector<string>::const_iterator mirror = AllMirrors.begin();
+ mirror != AllMirrors.end(); ++mirror)
+ {
+ if (Queue->Uri.find(*mirror) == 0)
+ {
+ Queue->Uri.replace(0, mirror->length(), BaseUri);
+ return;
+ }
+ }
+ _error->Error("Internal error: Failed to convert %s back to %s",
+ Queue->Uri.c_str(), BaseUri.c_str());
+}
+
+bool MirrorMethod::TryNextMirror()
+{
+ // find current mirror and select next one
+ for (vector<string>::const_iterator mirror = AllMirrors.begin();
+ mirror != AllMirrors.end(); ++mirror)
+ {
+ if (Queue->Uri.find(*mirror) != 0)
+ continue;
+
+ vector<string>::const_iterator nextmirror = mirror + 1;
+ if (nextmirror == AllMirrors.end())
+ break;
+ Queue->Uri.replace(0, mirror->length(), *nextmirror);
+ if (Debug)
+ clog << "TryNextMirror: " << Queue->Uri << endl;
+
+ // inform parent
+ UsedMirror = *nextmirror;
+ Log("Switching mirror");
+ return true;
+ }
+
+ if (Debug)
+ clog << "TryNextMirror could not find another mirror to try" << endl;
+
+ return false;
+}
+
+bool MirrorMethod::InitMirrors()
+{
+ // if we do not have a MirrorFile, fallback
+ if(!FileExists(MirrorFile))
+ {
+ // FIXME: fallback to a default mirror here instead
+ // and provide a config option to define that default
+ return _error->Error(_("No mirror file '%s' found "), MirrorFile.c_str());
+ }
+
+ if (access(MirrorFile.c_str(), R_OK) != 0)
+ {
+ // FIXME: fallback to a default mirror here instead
+ // and provide a config option to define that default
+ return _error->Error(_("Can not read mirror file '%s'"), MirrorFile.c_str());
+ }
+
+ // FIXME: make the mirror selection more clever, do not
+ // just use the first one!
+ // BUT: we can not make this random, the mirror has to be
+ // stable accross session, because otherwise we can
+ // get into sync issues (got indexfiles from mirror A,
+ // but packages from mirror B - one might be out of date etc)
+ ifstream in(MirrorFile.c_str());
+ string s;
+ while (!in.eof())
+ {
+ getline(in, s);
+
+ // ignore lines that start with #
+ if (s.find("#") == 0)
+ continue;
+ // ignore empty lines
+ if (s.size() == 0)
+ continue;
+ // ignore non http lines
+ if (s.find("http://") != 0)
+ continue;
+
+ AllMirrors.push_back(s);
+ }
+ if (AllMirrors.empty()) {
+ return _error->Error(_("No entry found in mirror file '%s'"), MirrorFile.c_str());
+ }
+ Mirror = AllMirrors[0];
+ UsedMirror = Mirror;
+ return true;
+}
+
+string MirrorMethod::GetMirrorFileName(string mirror_uri_str)