1 : /** -*- C++ -*-
2 : * @file
3 : * @author Enrico Zini (enrico) <enrico@enricozini.org>
4 : */
5 :
6 : /*
7 : * System tag database
8 : *
9 : * Copyright (C) 2003-2008 Enrico Zini <enrico@debian.org>
10 : *
11 : * This library is free software; you can redistribute it and/or
12 : * modify it under the terms of the GNU Lesser General Public
13 : * License as published by the Free Software Foundation; either
14 : * version 2.1 of the License, or (at your option) any later version.
15 : *
16 : * This library is distributed in the hope that it will be useful,
17 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 : * Lesser General Public License for more details.
20 : *
21 : * You should have received a copy of the GNU Lesser General Public
22 : * License along with this library; if not, write to the Free Software
23 : * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 : */
25 :
26 : #include <ept/debtags/debtags.h>
27 : #include <ept/debtags/maint/path.h>
28 : #include <ept/debtags/maint/serializer.h>
29 : #include <ept/debtags/maint/debtagsindexer.h>
30 :
31 : #include <tagcoll/input/stdio.h>
32 : #include <tagcoll/TextFormat.h>
33 :
34 : #include <wibble/sys/fs.h>
35 : #include <wibble/string.h>
36 :
37 : #include <iostream>
38 : #include <sstream>
39 :
40 : #include <sys/wait.h> // WIFEXITED WEXITSTATUS
41 : #include <sys/types.h> // getpwuid, stat, mkdir, getuid
42 : #include <sys/stat.h> // stat, mkdir
43 : #include <pwd.h> // getpwuid
44 : #include <unistd.h> // stat, getuid
45 :
46 :
47 : using namespace std;
48 : using namespace tagcoll;
49 : using namespace wibble;
50 :
51 : namespace ept {
52 : namespace debtags {
53 :
54 10 : Debtags::Debtags(bool editable)
55 10 : : m_coll(m_rocoll)
56 : {
57 10 : std::string tagfname;
58 10 : std::string idxfname;
59 :
60 10 : if (!DebtagsIndexer::obtainWorkingDebtags(vocabulary(), tagfname, idxfname))
61 : {
62 1 : m_timestamp = 0;
63 1 : return;
64 : } else {
65 9 : m_timestamp = Path::timestamp(idxfname);
66 :
67 9 : mastermmap.init(idxfname);
68 :
69 : // Initialize the readonly index
70 9 : m_pkgid.init(mastermmap, 0);
71 9 : m_rocoll.init(mastermmap, 1, 2);
72 : }
73 :
74 : // Initialize the patch collection layer
75 9 : rcdir = Path::debtagsUserSourceDir();
76 :
77 9 : string patchFile = str::joinpath(rcdir, "patch");
78 18 : if (Path::access(patchFile, F_OK) == 0)
79 : {
80 0 : input::Stdio in(patchFile);
81 0 : PatchList<int, int> patch;
82 0 : textformat::parsePatch(in, patchStringToInt(m_pkgid, vocabulary(), inserter(patch)));
83 0 : m_coll.setChanges(patch);
84 9 : }
85 0 : }
86 :
87 3 : tagcoll::PatchList<std::string, Tag> Debtags::changes() const
88 : {
89 3 : tagcoll::PatchList<int, int> patches = m_coll.changes();
90 3 : tagcoll::PatchList<std::string, Tag> res;
91 :
92 5 : for (tagcoll::PatchList<int, int>::const_iterator i = patches.begin();
93 : i != patches.end(); ++i)
94 : {
95 2 : std::string pkg = packageByID(i->second.item);
96 2 : if (pkg.empty())
97 0 : continue;
98 :
99 : res.addPatch(tagcoll::Patch<std::string, Tag>(pkg,
100 : vocabulary().tagsByID(i->second.added),
101 2 : vocabulary().tagsByID(i->second.removed)));
102 : }
103 :
104 3 : return res;
105 : }
106 :
107 :
108 : #if 0
109 : bool Debtags::hasTagDatabase()
110 : {
111 : if (Path::access(Path::tagdb(), R_OK) == -1)
112 : {
113 : std::cerr << "Missing tag database " << Path::tagdb() << std::endl;
114 : return false;
115 : }
116 : if (Path::access(Path::tagdbIndex(), R_OK) == -1)
117 : {
118 : std::cerr << "Missing tag database index " << Path::tagdbIndex() << std::endl;
119 : return false;
120 : }
121 : if (Path::access(Path::vocabulary(), R_OK) == -1)
122 : {
123 : std::cerr << "Missing tag vocabulary " << Path::vocabulary() << std::endl;
124 : return false;
125 : }
126 : if (Path::access(Path::vocabularyIndex(), R_OK) == -1)
127 : {
128 : std::cerr << "Missing index for tag vocabulary " << Path::vocabularyIndex() << std::endl;
129 : return false;
130 : }
131 : return true;
132 : }
133 : #endif
134 :
135 :
136 1 : void Debtags::savePatch()
137 : {
138 1 : PatchList<std::string, std::string> spatch;
139 1 : m_coll.changes().output(patchIntToString(m_pkgid, vocabulary(), tagcoll::inserter(spatch)));
140 1 : savePatch(spatch);
141 1 : }
142 :
143 1 : void Debtags::savePatch(const PatchList<std::string, std::string>& patch)
144 : {
145 1 : std::string patchFile = str::joinpath(rcdir, "patch");
146 1 : std::string backup = patchFile + "~";
147 :
148 1 : wibble::sys::fs::mkFilePath(patchFile);
149 :
150 1 : if (access(patchFile.c_str(), F_OK) == 0)
151 0 : if (rename(patchFile.c_str(), backup.c_str()) == -1)
152 0 : throw wibble::exception::System("Can't rename " + patchFile + " to " + backup);
153 :
154 : try {
155 1 : FILE* out = fopen(patchFile.c_str(), "w");
156 1 : if (out == 0)
157 0 : throw wibble::exception::System("Can't write to " + patchFile);
158 :
159 1 : textformat::outputPatch(patch, out);
160 :
161 1 : fclose(out);
162 0 : } catch (std::exception& e) {
163 0 : if (rename(backup.c_str(), patchFile.c_str()) == -1)
164 0 : std::cerr << "Warning: Cannot restore previous backup copy: " << e.what() << std::endl;
165 0 : throw;
166 1 : }
167 1 : }
168 :
169 0 : void Debtags::savePatch(const PatchList<std::string, Tag>& patch)
170 : {
171 0 : PatchList<std::string, std::string> spatch;
172 : // patch.output(patchToString<C>(m_pkgs, m_pkgidx, m_tags, tagcoll::inserter(spatch)));
173 0 : savePatch(spatch);
174 0 : }
175 :
176 0 : void Debtags::sendPatch()
177 : {
178 0 : PatchList<std::string, std::string> spatch;
179 0 : m_coll.changes().output(patchIntToString(m_pkgid, vocabulary(), tagcoll::inserter(spatch)));
180 0 : if (!spatch.empty())
181 : {
182 0 : sendPatch(spatch);
183 0 : }
184 0 : }
185 :
186 0 : void Debtags::sendPatch(const PatchList<std::string, Tag>& patch)
187 : {
188 0 : PatchList<std::string, std::string> spatch;
189 : // patch.output(patchToString<C>(m_pkgs, m_pkgidx, m_tags, tagcoll::inserter(spatch)));
190 0 : sendPatch(spatch);
191 0 : }
192 :
193 0 : void Debtags::sendPatch(const PatchList<std::string, std::string>& patch)
194 : {
195 : static const char* cmd = "/usr/sbin/sendmail -t";
196 0 : FILE* out = popen(cmd, "w");
197 0 : if (out == 0)
198 0 : throw wibble::exception::System(std::string("trying to run `") + cmd + "'");
199 :
200 0 : struct passwd* udata = getpwuid(getuid());
201 :
202 : fprintf(out,
203 : "To: enrico-debtags@debian.org\n"
204 : "Bcc: %s\n"
205 : "Subject: Tag patch\n"
206 : "Mime-Version: 1.0\n"
207 : "Content-Type: multipart/mixed; boundary=\"9amGYk9869ThD9tj\"\n"
208 : "Content-Disposition: inline\n"
209 : "X-Mailer: debtags-edit\n\n"
210 : "This mail contains a Debtags patch for the central archive\n\n"
211 : "--9amGYk9869ThD9tj\n"
212 : "Content-Type: text/plain; charset=utf-8\n"
213 : "Content-Disposition: inline\n\n"
214 0 : "-- DEBTAGS DIFF V0.1 --\n", udata->pw_name);
215 :
216 0 : textformat::outputPatch(patch, out);
217 :
218 0 : fprintf(out, "\n--9amGYk9869ThD9tj\n");
219 :
220 0 : int res = pclose(out);
221 0 : if (!WIFEXITED(res) || WEXITSTATUS(res) != 0)
222 : {
223 0 : std::stringstream str;
224 0 : str << res;
225 0 : throw wibble::exception::Consistency("checking mailer exit status", "sendmail returned nonzero (" + str.str() + "): the mail may have not been sent");
226 : }
227 0 : }
228 :
229 :
230 : template<typename OUT>
231 : void Debtags::outputSystem(const OUT& cons)
232 : {
233 : m_rocoll.output(intToPkg(m_pkgid, vocabulary(), cons));
234 : }
235 :
236 : template<typename OUT>
237 : void Debtags::outputPatched(const OUT& cons)
238 : {
239 : m_coll.output(intToPkg(m_pkgid, vocabulary(), cons));
240 : }
241 :
242 : }
243 : }
244 :
245 : #include <tagcoll/patch.tcc>
246 : #include <tagcoll/coll/patched.tcc>
247 : #include <tagcoll/TextFormat.tcc>
248 : //#include <tagcoll/stream/filters.tcc>
249 :
250 : // vim:set ts=4 sw=4:
|