This repository was archived by the owner on Dec 12, 2022. It is now read-only.
mirrored from https://salsa.debian.org/apt-team/apt.git
-
Notifications
You must be signed in to change notification settings - Fork 193
/
Copy pathprivate-sources.cc
326 lines (292 loc) · 10.3 KB
/
private-sources.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
#include <config.h>
#include <apt-pkg/cachefile.h>
#include <apt-pkg/cmndline.h>
#include <apt-pkg/configuration.h>
#include <apt-pkg/error.h>
#include <apt-pkg/fileutl.h>
#include <apt-pkg/hashes.h>
#include <apt-pkg/metaindex.h>
#include <apt-pkg/sourcelist.h>
#include <apt-pkg/strutl.h>
#include <apt-private/private-output.h>
#include <apt-private/private-sources.h>
#include <apt-private/private-utils.h>
#include <cstddef>
#include <iostream>
#include <regex>
#include <string>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <apti18n.h>
using namespace std::literals;
/* Interface discussion with donkult (for the future):
apt [add-{archive,release,component}|edit|change-release|disable]-sources
and be clever and work out stuff from the Release file
*/
// EditSource - EditSourcesList /*{{{*/
class APT_HIDDEN ScopedGetLock {
public:
int fd;
explicit ScopedGetLock(std::string const &filename) : fd(GetLock(filename)) {}
~ScopedGetLock() { close(fd); }
};
bool EditSources(CommandLine &CmdL)
{
std::string sourceslist;
if (CmdL.FileList[1] != NULL)
{
sourceslist = _config->FindDir("Dir::Etc::sourceparts") + CmdL.FileList[1];
} else {
sourceslist = _config->FindFile("Dir::Etc::sourcelist");
}
HashString before;
if (FileExists(sourceslist))
before.FromFile(sourceslist);
else
{
FileFd filefd;
if (filefd.Open(sourceslist, FileFd::Create | FileFd::WriteOnly, FileFd::None, 0644) == false)
return false;
}
ScopedGetLock lock(sourceslist);
if (lock.fd < 0)
return false;
bool res;
bool file_changed = false;
do {
if (EditFileInSensibleEditor(sourceslist) == false)
return false;
if (before.empty())
{
struct stat St;
if (stat(sourceslist.c_str(), &St) == 0 && St.st_size == 0)
RemoveFile("edit-sources", sourceslist);
}
else if (FileExists(sourceslist) && !before.VerifyFile(sourceslist))
{
file_changed = true;
pkgCacheFile::RemoveCaches();
}
pkgCacheFile CacheFile;
res = CacheFile.BuildCaches(nullptr);
if (res == false || _error->empty(GlobalError::WARNING) == false) {
std::string outs;
strprintf(outs, _("Failed to parse %s. Edit again? "), sourceslist.c_str());
// FIXME: should we add a "restore previous" option here?
if (YnPrompt(outs.c_str(), true) == false)
{
if (res == false && _error->PendingError() == false)
{
CacheFile.Close();
pkgCacheFile::RemoveCaches();
res = CacheFile.BuildCaches(nullptr);
}
break;
}
}
} while (res == false);
if (res == true && file_changed == true)
{
ioprintf(
std::cout, _("Your '%s' file changed, please run 'apt-get update'.\n"),
sourceslist.c_str());
}
return res;
}
/*}}}*/
template <typename T, typename V>
static auto contains(T const &container, V const &value) -> bool
{
return std::find(container.begin(), container.end(), value) != container.end();
}
static auto merge(std::vector<std::string> &a, std::vector<std::string> const &b)
{
for (auto const &e : b)
if (not contains(a, e))
a.push_back(e);
}
static bool Modernize(std::string const &filename) /*{{{*/
{
auto isMain = filename == _config->FindFile("Dir::Etc::SourceList");
auto simulate = _config->FindB("APT::Get::Simulate");
std::cerr << "Modernizing " << filename << "...\n";
pkgSourceList list;
if (not list.Read(filename))
return false;
struct Entry
{
std::set<std::string> types;
std::vector<std::string> uris;
std::vector<std::string> suites;
std::vector<std::string> components;
std::string signedBy;
std::map<std::string, std::string> options;
std::string origin;
bool merged = false;
auto Merge(Entry &other) -> bool
{
auto howManyDifferent = (types != other.types) + (uris != other.uris) + (suites != other.suites) + (components != other.components);
if (howManyDifferent > 1)
return false;
if (options != other.options)
return false;
if (origin != other.origin)
return false;
if (signedBy.empty() && not other.signedBy.empty() && (uris != other.uris || suites != other.suites))
return false;
if (not signedBy.empty() && not other.signedBy.empty() && signedBy != other.signedBy)
return false;
types.insert(other.types.begin(), other.types.end());
merge(uris, other.uris);
merge(suites, other.suites);
merge(components, other.components);
if (signedBy.empty())
signedBy = other.signedBy;
other.merged = true;
return true;
}
};
std::vector<Entry> entries;
for (auto const &meta : list)
{
Entry e;
e.uris.push_back(meta->GetURI());
e.suites.push_back(meta->GetDist());
for (auto const &t : meta->GetIndexTargets())
{
e.types.insert(t.Option(IndexTarget::TARGET_OF));
if (not contains(e.components, t.Option(IndexTarget::COMPONENT)))
e.components.push_back(t.Option(IndexTarget::COMPONENT));
}
if (meta->IsTrustedSet())
e.options["Trusted"] = "yes";
std::string err;
e.signedBy = meta->GetSignedBy();
meta->Load(&err);
if (e.signedBy.empty() && not meta->GetOrigin().empty())
{
std::string dir = _config->FindDir("Dir") + std::string{"usr/share/keyrings/"};
std::string keyring = std::regex_replace(meta->GetOrigin(), std::regex(" "), "-") + "-archive-keyring.gpg";
std::transform(keyring.begin(), keyring.end(), keyring.begin(), tolower);
if (FileExists(dir + keyring))
e.signedBy = dir + keyring;
}
if (auto k = _config->FindDir("Dir::Etc::trustedparts") += flNotDir(std::regex_replace(filename, std::regex("\\.list$"), ".gpg")); FileExists(k))
e.signedBy = k;
if (auto k = _config->FindDir("Dir::Etc::trustedparts") += flNotDir(std::regex_replace(filename, std::regex("\\.list$"), ".asc")); FileExists(k))
e.signedBy = k;
if (isMain && not meta->GetOrigin().empty())
{
constexpr auto bad = "\\|{}[]<>\"^~_=!@#$%^&*"sv;
e.origin = meta->GetOrigin();
std::transform(e.origin.begin(), e.origin.end(), e.origin.begin(), tolower);
std::transform(e.origin.begin(), e.origin.end(), e.origin.begin(), [](char c) -> char
{ return isspace(c) ? '-' : c; });
std::transform(e.origin.begin(), e.origin.end(), e.origin.begin(), [bad](char c) -> char
{ return bad.find(c) != bad.npos ? '-' : c; });
std::replace(e.origin.begin(), e.origin.end(), '/', '-');
}
entries.push_back(std::move(e));
}
for (bool merged = false; merged;)
{
merged = false;
for (auto it = entries.begin(); it != entries.end(); ++it)
{
for (auto it2 = it + 1; it2 != entries.end(); ++it2)
if (not it2->merged)
merged |= it->Merge(*it2);
}
}
std::map<std::string, std::ofstream> streams;
for (auto const &e : entries)
{
std::string outname;
if (not isMain)
outname = std::regex_replace(filename, std::regex("\\.list$"), ".sources");
else if (e.origin.empty())
outname = _config->FindDir("Dir::Etc::SourceParts") + "moved-from-main.sources";
else
outname = _config->FindDir("Dir::Etc::SourceParts") + (e.origin) + ".sources";
if (auto it = streams.find(outname); not simulate && it == streams.end())
{
std::cerr << "- Writing " << outname << "\n";
streams[outname].open(outname, std::ios::app);
}
auto &out = simulate ? std::cout : streams[outname];
if (not out)
_error->Warning("Cannot open %s for writing.", outname.c_str());
if (e.merged)
continue;
if (out.tellp() != 0)
out << "\n";
if (simulate)
out << "# Would write to: " << outname << "\n";
if (isMain)
out << "# Modernized from " << filename << "\n";
out << "Types:";
for (auto const &t : e.types)
out << " " << t;
out << "\n";
out << "URIs: " << APT::String::Join(e.uris, " ") << "\n";
out << "Suites: " << APT::String::Join(e.suites, " ") << "\n";
out << "Components: " << APT::String::Join(e.components, " ") << "\n";
out << "Signed-By: " << e.signedBy << "\n";
for (auto const &[key, value] : e.options)
out << key << ": " << value << "\n";
if (e.signedBy.empty())
_error->Warning("Could not determine Signed-By for URIs: %s, Suites: %s", APT::String::Join(e.uris, " ").c_str(), APT::String::Join(e.suites, " ").c_str());
}
if (not simulate && rename(filename.c_str(), (filename + ".bak").c_str()) != 0)
_error->WarningE("rename", "Could not rename %s", filename.c_str());
_error->DumpErrors();
std::cerr << "\n";
return true;
}
/*}}}*/
bool ModernizeSources(CommandLine &) /*{{{*/
{
auto main = _config->FindFile("Dir::Etc::SourceList");
auto parts = _config->FindDir("Dir::Etc::SourceParts");
std::vector<std::string> files;
if (FileExists(main))
files.push_back(main);
for (auto const &I : GetListOfFilesInDir(parts, std::vector<std::string>{"list", "sources"}, true))
if (APT::String::Endswith(I, ".list"))
files.push_back(I);
if (files.empty())
{
std::cout << "All sources are modern.\n";
return true;
}
// TRANSLATOR: "No" answer printed for a yes/no question if --assume-no is set
auto no = _("N");
std::cout << "The following files need modernizing:\n";
for (auto const &I : files)
std::cout << " - " << I << "\n";
std::cout << "\n"
<< "Modernizing will replace .list files with the new .sources format,\n"
<< "add Signed-By values where they can be determined automatically,\n"
<< "and save the old files into .list.bak files.\n"
<< "\n"
<< "This command supports the 'signed-by' and 'trusted' options. If you\n"
<< "have specified other options inside [] brackets, please transfer them\n"
<< "manually to the output files; see sources.list(5) for a mapping.\n"
<< "\n"
<< "For a simulation, respond " << no << " in the following prompt.\n";
std::string prompt;
strprintf(prompt, _("Rewrite %zu sources?"), files.size());
if (YnPrompt(prompt.c_str(), true) == false)
{
_config->Set("APT::Get::Simulate", true);
std::cout << "Simulating only..." << std::endl;
// Make it visible.
usleep(100 * 1000);
}
bool good = true;
for (auto const &I : files)
good = good && Modernize(I);
return good;
}
/*}}}*/