1package deb
2
3import (
4	"os"
5	"path/filepath"
6	"sort"
7	"strings"
8
9	"github.com/aptly-dev/aptly/aptly"
10	"github.com/aptly-dev/aptly/pgp"
11	"github.com/aptly-dev/aptly/utils"
12)
13
14// CollectPackageFiles walks filesystem collecting all candidates for package files
15func CollectPackageFiles(locations []string, reporter aptly.ResultReporter) (packageFiles, otherFiles, failedFiles []string) {
16	for _, location := range locations {
17		info, err2 := os.Stat(location)
18		if err2 != nil {
19			reporter.Warning("Unable to process %s: %s", location, err2)
20			failedFiles = append(failedFiles, location)
21			continue
22		}
23		if info.IsDir() {
24			err2 = filepath.Walk(location, func(path string, info os.FileInfo, err3 error) error {
25				if err3 != nil {
26					return err3
27				}
28				if info.IsDir() {
29					return nil
30				}
31
32				if strings.HasSuffix(info.Name(), ".deb") || strings.HasSuffix(info.Name(), ".udeb") ||
33					strings.HasSuffix(info.Name(), ".dsc") || strings.HasSuffix(info.Name(), ".ddeb") {
34					packageFiles = append(packageFiles, path)
35				} else if strings.HasSuffix(info.Name(), ".buildinfo") {
36					otherFiles = append(otherFiles, path)
37				}
38
39				return nil
40			})
41
42			if err2 != nil {
43				reporter.Warning("Unable to process %s: %s", location, err2)
44				failedFiles = append(failedFiles, location)
45				continue
46			}
47		} else {
48			if strings.HasSuffix(info.Name(), ".deb") || strings.HasSuffix(info.Name(), ".udeb") ||
49				strings.HasSuffix(info.Name(), ".dsc") || strings.HasSuffix(info.Name(), ".ddeb") {
50				packageFiles = append(packageFiles, location)
51			} else if strings.HasSuffix(info.Name(), ".buildinfo") {
52				otherFiles = append(otherFiles, location)
53			} else {
54				reporter.Warning("Unknown file extension: %s", location)
55				failedFiles = append(failedFiles, location)
56				continue
57			}
58		}
59	}
60
61	sort.Strings(packageFiles)
62
63	return
64}
65
66// ImportPackageFiles imports files into local repository
67func ImportPackageFiles(list *PackageList, packageFiles []string, forceReplace bool, verifier pgp.Verifier,
68	pool aptly.PackagePool, collection *PackageCollection, reporter aptly.ResultReporter, restriction PackageQuery,
69	checksumStorage aptly.ChecksumStorage) (processedFiles []string, failedFiles []string, err error) {
70	if forceReplace {
71		list.PrepareIndex()
72	}
73
74	for _, file := range packageFiles {
75		var (
76			stanza Stanza
77			p      *Package
78		)
79
80		candidateProcessedFiles := []string{}
81		isSourcePackage := strings.HasSuffix(file, ".dsc")
82		isUdebPackage := strings.HasSuffix(file, ".udeb")
83
84		if isSourcePackage {
85			stanza, err = GetControlFileFromDsc(file, verifier)
86
87			if err == nil {
88				stanza["Package"] = stanza["Source"]
89				delete(stanza, "Source")
90
91				p, err = NewSourcePackageFromControlFile(stanza)
92			}
93		} else {
94			stanza, err = GetControlFileFromDeb(file)
95			if isUdebPackage {
96				p = NewUdebPackageFromControlFile(stanza)
97			} else {
98				p = NewPackageFromControlFile(stanza)
99			}
100		}
101		if err != nil {
102			reporter.Warning("Unable to read file %s: %s", file, err)
103			failedFiles = append(failedFiles, file)
104			continue
105		}
106
107		if p.Name == "" {
108			reporter.Warning("Empty package name on %s", file)
109			failedFiles = append(failedFiles, file)
110			continue
111		}
112
113		if p.Version == "" {
114			reporter.Warning("Empty version on %s", file)
115			failedFiles = append(failedFiles, file)
116			continue
117		}
118
119		if p.Architecture == "" {
120			reporter.Warning("Empty architecture on %s", file)
121			failedFiles = append(failedFiles, file)
122			continue
123		}
124
125		var files PackageFiles
126
127		if isSourcePackage {
128			files = p.Files()
129		}
130
131		var checksums utils.ChecksumInfo
132		checksums, err = utils.ChecksumsForFile(file)
133		if err != nil {
134			return nil, nil, err
135		}
136
137		mainPackageFile := PackageFile{
138			Filename:  filepath.Base(file),
139			Checksums: checksums,
140		}
141
142		mainPackageFile.PoolPath, err = pool.Import(file, mainPackageFile.Filename, &mainPackageFile.Checksums, false, checksumStorage)
143		if err != nil {
144			reporter.Warning("Unable to import file %s into pool: %s", file, err)
145			failedFiles = append(failedFiles, file)
146			continue
147		}
148
149		candidateProcessedFiles = append(candidateProcessedFiles, file)
150
151		// go over all the other files
152		for i := range files {
153			sourceFile := filepath.Join(filepath.Dir(file), filepath.Base(files[i].Filename))
154
155			_, err = os.Stat(sourceFile)
156			if err == nil {
157				files[i].PoolPath, err = pool.Import(sourceFile, files[i].Filename, &files[i].Checksums, false, checksumStorage)
158				if err == nil {
159					candidateProcessedFiles = append(candidateProcessedFiles, sourceFile)
160				}
161			} else if os.IsNotExist(err) {
162				// if file is not present, try to find it in the pool
163				var (
164					err2  error
165					found bool
166				)
167
168				files[i].PoolPath, found, err2 = pool.Verify("", files[i].Filename, &files[i].Checksums, checksumStorage)
169				if err2 != nil {
170					err = err2
171				} else if found {
172					// clear error, file is already in the package pool
173					err = nil
174				}
175			}
176
177			if err != nil {
178				reporter.Warning("Unable to import file %s into pool: %s", sourceFile, err)
179				failedFiles = append(failedFiles, file)
180				break
181			}
182		}
183		if err != nil {
184			// some files haven't been imported
185			continue
186		}
187
188		p.UpdateFiles(append(files, mainPackageFile))
189
190		if restriction != nil && !restriction.Matches(p) {
191			reporter.Warning("%s has been ignored as it doesn't match restriction", p)
192			failedFiles = append(failedFiles, file)
193			continue
194		}
195
196		err = collection.Update(p)
197		if err != nil {
198			reporter.Warning("Unable to save package %s: %s", p, err)
199			failedFiles = append(failedFiles, file)
200			continue
201		}
202
203		if forceReplace {
204			conflictingPackages := list.Search(Dependency{Pkg: p.Name, Version: p.Version, Relation: VersionEqual, Architecture: p.Architecture}, true)
205			for _, cp := range conflictingPackages {
206				reporter.Removed("%s removed due to conflict with package being added", cp)
207				list.Remove(cp)
208			}
209		}
210
211		err = list.Add(p)
212		if err != nil {
213			reporter.Warning("Unable to add package to repo %s: %s", p, err)
214			failedFiles = append(failedFiles, file)
215			continue
216		}
217
218		reporter.Added("%s added", p)
219		processedFiles = append(processedFiles, candidateProcessedFiles...)
220	}
221
222	err = nil
223	return
224}
225