source: appstream-generator/src/asgen/backends/debian/debpkg.d @ 4841

Last change on this file since 4841 was 4841, checked in by Juanma, 2 years ago

Initial release

File size: 6.6 KB
Line 
1/*
2 * Copyright (C) 2016 Matthias Klumpp <matthias@tenstral.net>
3 *
4 * Licensed under the GNU Lesser General Public License Version 3
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation, either version 3 of the license, or
9 * (at your option) any later version.
10 *
11 * This software is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this software.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20module asgen.backends.debian.debpkg;
21
22import std.stdio;
23import std.string;
24import std.path;
25import std.array : empty, appender;
26import std.file : rmdirRecurse, mkdirRecurse;
27static import std.file;
28
29import asgen.config;
30import asgen.zarchive;
31import asgen.backends.interfaces;
32import asgen.logging;
33import asgen.utils : isRemote, downloadFile;
34
35
36class DebPackage : Package
37{
38private:
39    string pkgname;
40    string pkgver;
41    string pkgarch;
42    string pkgmaintainer;
43    string[string] desc;
44
45    bool contentsRead;
46    string[] contentsL;
47
48    string tmpDir;
49    string dataArchive;
50    string controlArchive;
51
52    string debFname;
53
54public:
55    final @property override string name () const { return pkgname; }
56    final @property override string ver () const { return pkgver; }
57    final @property override string arch () const { return pkgarch; }
58
59    final @property override const(string[string]) description () const { return desc; }
60
61    override final
62    @property string filename () const {
63        if (debFname.isRemote) {
64            immutable path = buildNormalizedPath (tmpDir, debFname.baseName);
65            synchronized (this) {
66                downloadFile (debFname, path);
67            }
68            return path;
69        }
70        return debFname;
71    }
72    final @property void   filename (string fname) { debFname = fname; }
73
74    override
75    final @property string maintainer () const { return pkgmaintainer; }
76    final @property void   maintainer (string maint) { pkgmaintainer = maint; }
77
78    this (string pname, string pver, string parch)
79    {
80        pkgname = pname;
81        pkgver = pver;
82        pkgarch = parch;
83
84        contentsRead = false;
85
86        auto conf = Config.get ();
87        tmpDir = buildPath (conf.getTmpDir (), format ("%s-%s_%s", name, ver, arch));
88    }
89
90    ~this ()
91    {
92        // FIXME: We can't properly clean up because we can't GC-allocate in a destructor (leads to crashes),
93        // see if this is fixed in a future version of D, or simply don't use the GC in close ().
94        // close ();
95    }
96
97    final void setDescription (string text, string locale)
98    {
99        desc[locale] = text;
100    }
101
102    private auto openPayloadArchive ()
103    {
104        auto pa = new ArchiveDecompressor ();
105        if (!dataArchive) {
106            import std.regex;
107
108            // extract the payload to a temporary location first
109            pa.open (this.filename);
110            mkdirRecurse (tmpDir);
111
112            string[] files;
113            try {
114                files = pa.extractFilesByRegex (ctRegex!(r"data\.*"), tmpDir);
115            } catch (Exception e) { throw e; }
116
117            if (files.length == 0)
118                return null;
119            dataArchive = files[0];
120        }
121
122        pa.open (dataArchive);
123        return pa;
124    }
125
126    protected final void extractPackage (const string dest = buildPath (tmpDir, name))
127    {
128        import std.file : exists;
129        import std.regex : ctRegex;
130
131        if (!dest.exists)
132            mkdirRecurse (dest);
133
134        auto pa = openPayloadArchive ();
135        pa.extractArchive (dest);
136    }
137
138    private final auto openControlArchive ()
139    {
140        auto ca = new ArchiveDecompressor ();
141        if (!controlArchive) {
142            import std.regex;
143
144            // extract the payload to a temporary location first
145            ca.open (this.filename);
146            mkdirRecurse (tmpDir);
147
148            string[] files;
149            try {
150                files = ca.extractFilesByRegex (ctRegex!(r"control\.*"), tmpDir);
151            } catch (Exception e) { throw e; }
152
153            if (files.empty)
154                return null;
155            controlArchive = files[0];
156        }
157
158        ca.open (controlArchive);
159        return ca;
160    }
161
162    override final
163    const(ubyte)[] getFileData (string fname)
164    {
165        auto pa = openPayloadArchive ();
166        return pa.readData (fname);
167    }
168
169    @property override final
170    string[] contents ()
171    {
172        import std.utf;
173
174        if (contentsRead)
175            return contentsL;
176
177        if (pkgname.endsWith ("icon-theme")) {
178            // the md5sums file does not contain symbolic links - while that is okay-ish for regular
179            // packages, it is not acceptable for icon themes, since those rely on symlinks to provide
180            // aliases for certain icons. So, use the slow method for reading contents information here.
181
182            auto pa = openPayloadArchive ();
183            contentsL = pa.readContents ();
184            contentsRead = true;
185
186            return contentsL;
187        }
188
189        // use the md5sums file of the .deb control archive to determine
190        // the contents of this package.
191        // this is way faster than going through the payload directly, and
192        // has the same accuracy.
193        auto ca = openControlArchive ();
194        const(ubyte)[] md5sumsData;
195        try {
196            md5sumsData = ca.readData ("./md5sums");
197        } catch (Exception e) {
198            logWarning ("Could not read md5sums file for package %s: %s", this.id, e.msg);
199            return [];
200        }
201
202        auto md5sums = cast(string) md5sumsData;
203        try {
204            md5sums = md5sums.toUTF8;
205        } catch (Exception e) {
206            logError ("Could not decode md5sums file for package %s: %s", this.id, e.msg);
207            return [];
208        }
209
210        auto contentsAppender = appender!(string[]);
211        foreach (line; md5sums.splitLines ()) {
212            auto parts = line.split ("  ");
213            if (parts.length <= 0)
214                continue;
215            string c = join (parts[1..$], "  ");
216            contentsAppender.put ("/" ~ c);
217        }
218        contentsL = contentsAppender.data;
219
220        contentsRead = true;
221        return contentsL;
222    }
223
224    override final
225    void close ()
226    {
227        try {
228            if (std.file.exists (tmpDir))
229                rmdirRecurse (tmpDir);
230            dataArchive = null;
231            controlArchive = null;
232        } catch {}
233    }
234}
Note: See TracBrowser for help on using the repository browser.