- add support for yum-style distroverpkg package whose version can be
[apt.git] / apt-pkg / sourcelist.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description                                                          /*{{{*/
3 // $Id: sourcelist.cc,v 1.3 2002/08/15 20:51:37 niemeyer Exp $
4 /* ######################################################################
5
6    List of Sources
7    
8    ##################################################################### */
9                                                                         /*}}}*/
10 // Include Files                                                        /*{{{*/
11 #ifdef __GNUG__
12 #pragma implementation "apt-pkg/sourcelist.h"
13 #endif
14
15 #include <apt-pkg/sourcelist.h>
16 #include <apt-pkg/error.h>
17 #include <apt-pkg/fileutl.h>
18 #include <apt-pkg/configuration.h>
19 #include <apt-pkg/strutl.h>
20
21 // CNC:2003-11-21
22 #include <apt-pkg/pkgsystem.h>
23
24 #include <apti18n.h>
25
26 #include <fstream>
27
28 // CNC:2003-03-03 - This is needed for ReadDir stuff.
29 #include <algorithm>
30 #include <stdio.h>
31 #include <dirent.h>
32 #include <sys/stat.h>
33 #include <unistd.h>
34                                                                         /*}}}*/
35
36 using namespace std;
37
38 // Global list of Items supported
39 static  pkgSourceList::Type *ItmList[10];
40 pkgSourceList::Type **pkgSourceList::Type::GlobalList = ItmList;
41 unsigned long pkgSourceList::Type::GlobalListLen = 0;
42
43 // Type::Type - Constructor                                             /*{{{*/
44 // ---------------------------------------------------------------------
45 /* Link this to the global list of items*/
46 pkgSourceList::Type::Type()
47 {
48    ItmList[GlobalListLen] = this;
49    GlobalListLen++;
50 }
51                                                                         /*}}}*/
52 // Type::GetType - Get a specific meta for a given type                 /*{{{*/
53 // ---------------------------------------------------------------------
54 /* */
55 pkgSourceList::Type *pkgSourceList::Type::GetType(const char *Type)
56 {
57    for (unsigned I = 0; I != GlobalListLen; I++)
58       if (strcmp(GlobalList[I]->Name,Type) == 0)
59          return GlobalList[I];
60    return 0;
61 }
62                                                                         /*}}}*/
63 // Type::FixupURI - Normalize the URI and check it..                    /*{{{*/
64 // ---------------------------------------------------------------------
65 /* */
66 bool pkgSourceList::Type::FixupURI(string &URI) const
67 {
68    if (URI.empty() == true)
69       return false;
70
71    if (URI.find(':') == string::npos)
72       return false;
73
74    URI = SubstVar(URI,"$(ARCH)",_config->Find("APT::Architecture"));
75    
76    // Make sure that the URI is / postfixed
77    if (URI[URI.size() - 1] != '/')
78       URI += '/';
79    
80    return true;
81 }
82                                                                         /*}}}*/
83 // Type::ParseLine - Parse a single line                                /*{{{*/
84 // ---------------------------------------------------------------------
85 /* This is a generic one that is the 'usual' format for sources.list
86    Weird types may override this. */
87 bool pkgSourceList::Type::ParseLine(vector<pkgIndexFile *> &List,
88                                     Vendor const *Vendor,
89                                     const char *Buffer,
90                                     unsigned long CurLine,
91                                     string File) const
92 {
93    string URI;
94    string Dist;
95    string Section;   
96    
97    if (ParseQuoteWord(Buffer,URI) == false)
98       return _error->Error(_("Malformed line %lu in source list %s (URI)"),CurLine,File.c_str());
99    if (ParseQuoteWord(Buffer,Dist) == false)
100       return _error->Error(_("Malformed line %lu in source list %s (dist)"),CurLine,File.c_str());
101       
102    if (FixupURI(URI) == false)
103       return _error->Error(_("Malformed line %lu in source list %s (URI parse)"),CurLine,File.c_str());
104    
105    // Check for an absolute dists specification.
106    if (Dist.empty() == false && Dist[Dist.size() - 1] == '/')
107    {
108       if (ParseQuoteWord(Buffer,Section) == true)
109          return _error->Error(_("Malformed line %lu in source list %s (Absolute dist)"),CurLine,File.c_str());
110       Dist = SubstVar(Dist,"$(ARCH)",_config->Find("APT::Architecture"));
111       return CreateItem(List,URI,Dist,Section,Vendor);
112    }
113    
114    // CNC:2004-05-18 
115    Dist = SubstVar(Dist,"$(ARCH)",_config->Find("APT::Architecture"));
116    // PM:2006-02-06
117    Dist = SubstVar(Dist,"$(VERSION)",_config->Find("APT::DistroVersion"));
118
119    // Grab the rest of the dists
120    if (ParseQuoteWord(Buffer,Section) == false)
121       return _error->Error(_("Malformed line %lu in source list %s (dist parse)"),CurLine,File.c_str());
122    
123    do
124    {
125       if (CreateItem(List,URI,Dist,Section,Vendor) == false)
126          return false;
127    }
128    while (ParseQuoteWord(Buffer,Section) == true);
129    
130    return true;
131 }
132                                                                         /*}}}*/
133
134 // SourceList::pkgSourceList - Constructors                             /*{{{*/
135 // ---------------------------------------------------------------------
136 /* */
137 pkgSourceList::pkgSourceList()
138 {
139 }
140
141 pkgSourceList::pkgSourceList(string File)
142 {
143    Read(File);
144 }
145                                                                         /*}}}*/
146 // SourceList::~pkgSourceList - Destructor                              /*{{{*/
147 // ---------------------------------------------------------------------
148 /* */
149 pkgSourceList::~pkgSourceList()
150 {
151    for (const_iterator I = SrcList.begin(); I != SrcList.end(); I++)
152       delete *I;
153    for (vector<Vendor const *>::const_iterator I = VendorList.begin(); 
154         I != VendorList.end(); I++)
155       delete *I;
156 }
157                                                                         /*}}}*/
158 // SourceList::ReadVendors - Read list of known package vendors         /*{{{*/
159 // ---------------------------------------------------------------------
160 /* This also scans a directory of vendor files similar to apt.conf.d 
161    which can contain the usual suspects of distribution provided data.
162    The APT config mechanism allows the user to override these in their
163    configuration file. */
164 bool pkgSourceList::ReadVendors()
165 {
166    Configuration Cnf;
167
168    string CnfFile = _config->FindDir("Dir::Etc::vendorparts");
169    if (FileExists(CnfFile) == true)
170       if (ReadConfigDir(Cnf,CnfFile,true) == false)
171          return false;
172    CnfFile = _config->FindFile("Dir::Etc::vendorlist");
173    if (FileExists(CnfFile) == true)
174       if (ReadConfigFile(Cnf,CnfFile,true) == false)
175          return false;
176
177    for (vector<Vendor const *>::const_iterator I = VendorList.begin(); 
178         I != VendorList.end(); I++)
179       delete *I;
180    VendorList.erase(VendorList.begin(),VendorList.end());
181    
182    // Process 'simple-key' type sections
183    const Configuration::Item *Top = Cnf.Tree("simple-key");
184    for (Top = (Top == 0?0:Top->Child); Top != 0; Top = Top->Next)
185    {
186       Configuration Block(Top);
187       Vendor *Vendor;
188       
189       Vendor = new pkgSourceList::Vendor;
190       
191       Vendor->VendorID = Top->Tag;
192       Vendor->FingerPrint = Block.Find("Fingerprint");
193       Vendor->Description = Block.Find("Name");
194
195       // CNC:2002-08-15
196       char *buffer = new char[Vendor->FingerPrint.length()+1];
197       char *p = buffer;;
198       for (string::const_iterator I = Vendor->FingerPrint.begin();
199            I != Vendor->FingerPrint.end(); I++)
200       {
201          if (*I != ' ' && *I != '\t')
202             *p++ = *I;
203       }
204       *p = 0;
205       Vendor->FingerPrint = buffer;
206       delete [] buffer;
207       
208       if (Vendor->FingerPrint.empty() == true || 
209           Vendor->Description.empty() == true)
210       {
211          _error->Error(_("Vendor block %s is invalid"), Vendor->VendorID.c_str());
212          delete Vendor;
213          continue;
214       }
215       
216       VendorList.push_back(Vendor);
217    }
218
219    /* XXX Process 'group-key' type sections
220       This is currently faked out so that the vendors file format is
221       parsed but nothing is done with it except check for validity */
222    Top = Cnf.Tree("group-key");
223    for (Top = (Top == 0?0:Top->Child); Top != 0; Top = Top->Next)
224    {
225       Configuration Block(Top);
226       Vendor *Vendor;
227       
228       Vendor = new pkgSourceList::Vendor;
229       
230       Vendor->VendorID = Top->Tag;
231       Vendor->Description = Block.Find("Name");
232
233       if (Vendor->Description.empty() == true)
234       {
235          _error->Error(_("Vendor block %s is invalid"), 
236                        Vendor->VendorID.c_str());
237          delete Vendor;
238          continue;
239       }
240       
241       VendorList.push_back(Vendor);
242    }
243    
244    return !_error->PendingError();
245 }
246                                                                         /*}}}*/
247 // SourceList::ReadMainList - Read the main source list from etc        /*{{{*/
248 // ---------------------------------------------------------------------
249 /* */
250 bool pkgSourceList::ReadMainList()
251 {
252    // CNC:2003-03-03 - Multiple sources list support.
253    bool Res = ReadVendors();
254    if (Res == false)
255       return false;
256    
257    Reset();
258    // CNC:2003-11-28 - Entries in sources.list have priority over
259    //                  entries in sources.list.d.
260    string Main = _config->FindFile("Dir::Etc::sourcelist");
261    if (FileExists(Main) == true)
262       Res &= ReadAppend(Main);   
263
264    string Parts = _config->FindDir("Dir::Etc::sourceparts");
265    if (FileExists(Parts) == true)
266       Res &= ReadSourceDir(Parts);
267    
268    return Res;
269 }
270                                                                         /*}}}*/
271 // CNC:2003-03-03 - Needed to preserve backwards compatibility.
272 // SourceList::Reset - Clear the sourcelist contents                    /*{{{*/
273 // ---------------------------------------------------------------------
274 /* */
275 void pkgSourceList::Reset()
276 {
277    for (const_iterator I = SrcList.begin(); I != SrcList.end(); I++)
278       delete *I;
279    SrcList.erase(SrcList.begin(),SrcList.end());
280    // CNC:2003-11-21
281    _system->AddSourceFiles(SrcList);
282 }
283                                                                         /*}}}*/
284 // CNC:2003-03-03 - Function moved to ReadAppend() and Reset().
285 // SourceList::Read - Parse the sourcelist file                         /*{{{*/
286 // ---------------------------------------------------------------------
287 /* */
288 bool pkgSourceList::Read(string File)
289 {
290    Reset();
291    return ReadAppend(File);
292 }
293                                                                         /*}}}*/
294 // SourceList::ReadAppend - Parse a sourcelist file                     /*{{{*/
295 // ---------------------------------------------------------------------
296 /* */
297 bool pkgSourceList::ReadAppend(string File)
298 {
299    // Open the stream for reading
300    ifstream F(File.c_str(),ios::in /*| ios::nocreate*/);
301    if (!F != 0)
302       return _error->Errno("ifstream::ifstream",_("Opening %s"),File.c_str());
303    
304 #if 0 // Now Reset() does this.
305    for (const_iterator I = SrcList.begin(); I != SrcList.end(); I++)
306       delete *I;
307    SrcList.erase(SrcList.begin(),SrcList.end());
308 #endif
309    // CNC:2003-12-10 - 300 is too short.
310    char Buffer[1024];
311
312    int CurLine = 0;
313    while (F.eof() == false)
314    {
315       F.getline(Buffer,sizeof(Buffer));
316       CurLine++;
317       _strtabexpand(Buffer,sizeof(Buffer));
318       if (F.fail() && !F.eof())
319          return _error->Error(_("Line %u too long in source list %s."),
320                               CurLine,File.c_str());
321
322       
323       char *I;
324       // CNC:2003-02-20 - Do not break if '#' is inside [].
325       for (I = Buffer; *I != 0 && *I != '#'; I++)
326          if (*I == '[')
327             for (I++; *I != 0 && *I != ']'; I++);
328       *I = 0;
329       
330       const char *C = _strstrip(Buffer);
331       
332       // Comment or blank
333       if (C[0] == '#' || C[0] == 0)
334          continue;
335             
336       // Grok it
337       string LineType;
338       if (ParseQuoteWord(C,LineType) == false)
339          return _error->Error(_("Malformed line %u in source list %s (type)"),CurLine,File.c_str());
340
341       Type *Parse = Type::GetType(LineType.c_str());
342       if (Parse == 0)
343          return _error->Error(_("Type '%s' is not known in on line %u in source list %s"),LineType.c_str(),CurLine,File.c_str());
344       
345       // Authenticated repository
346       Vendor const *Vndr = 0;
347       if (C[0] == '[')
348       {
349          string VendorID;
350          
351          if (ParseQuoteWord(C,VendorID) == false)
352              return _error->Error(_("Malformed line %u in source list %s (vendor id)"),CurLine,File.c_str());
353
354          if (VendorID.length() < 2 || VendorID.end()[-1] != ']')
355              return _error->Error(_("Malformed line %u in source list %s (vendor id)"),CurLine,File.c_str());
356          VendorID = string(VendorID,1,VendorID.size()-2);
357          
358          for (vector<Vendor const *>::const_iterator iter = VendorList.begin();
359               iter != VendorList.end(); iter++) 
360          {
361             if ((*iter)->VendorID == VendorID)
362             {
363                Vndr = *iter;
364                break;
365             }
366          }
367
368          if (Vndr == 0)
369             return _error->Error(_("Unknown vendor ID '%s' in line %u of source list %s"),
370                                  VendorID.c_str(),CurLine,File.c_str());
371       }
372       
373       if (Parse->ParseLine(SrcList,Vndr,C,CurLine,File) == false)
374          return false;
375    }
376    return true;
377 }
378                                                                         /*}}}*/
379 // SourceList::FindIndex - Get the index associated with a file         /*{{{*/
380 // ---------------------------------------------------------------------
381 /* */
382 bool pkgSourceList::FindIndex(pkgCache::PkgFileIterator File,
383                               pkgIndexFile *&Found) const
384 {
385    for (const_iterator I = SrcList.begin(); I != SrcList.end(); I++)
386    {
387       if ((*I)->FindInCache(*File.Cache()) == File)
388       {
389          Found = *I;
390          return true;
391       }
392    }
393    
394    return false;
395 }
396                                                                         /*}}}*/
397 // SourceList::GetIndexes - Load the index files into the downloader    /*{{{*/
398 // ---------------------------------------------------------------------
399 /* */
400 bool pkgSourceList::GetIndexes(pkgAcquire *Owner) const
401 {
402    for (const_iterator I = SrcList.begin(); I != SrcList.end(); I++)
403       if ((*I)->GetIndexes(Owner) == false)
404          return false;
405    return true;
406 }
407                                                                         /*}}}*/
408 // CNC:2002-07-04
409 // SourceList::GetReleases - Load release files into the downloader     /*{{{*/
410 // ---------------------------------------------------------------------
411 /* */
412 bool pkgSourceList::GetReleases(pkgAcquire *Owner) const
413 {
414    for (const_iterator I = SrcList.begin(); I != SrcList.end(); I++)
415       if ((*I)->GetReleases(Owner) == false)
416          return false;
417    return true;
418 }
419                                                                         /*}}}*/
420 // CNC:2003-03-03 - By Anton V. Denisov <avd@altlinux.org>.
421 // SourceList::ReadSourceDir - Read a directory with sources files
422 // Based on ReadConfigDir()                                             /*{{{*/
423 // ---------------------------------------------------------------------
424 /* */
425 bool pkgSourceList::ReadSourceDir(string Dir)
426 {
427    DIR *D = opendir(Dir.c_str());
428    if (D == 0)
429       return _error->Errno("opendir",_("Unable to read %s"),Dir.c_str());
430
431    vector<string> List;
432    
433    for (struct dirent *Ent = readdir(D); Ent != 0; Ent = readdir(D))
434    {
435       if (Ent->d_name[0] == '.')
436          continue;
437
438       // CNC:2003-12-02 Only accept .list files as valid sourceparts
439       if (flExtension(Ent->d_name) != "list")
440          continue;
441       
442       // Skip bad file names ala run-parts
443       const char *C = Ent->d_name;
444       for (; *C != 0; C++)
445          if (isalpha(*C) == 0 && isdigit(*C) == 0
446              && *C != '_' && *C != '-' && *C != '.')
447             break;
448       if (*C != 0)
449          continue;
450       
451       // Make sure it is a file and not something else
452       string File = flCombine(Dir,Ent->d_name);
453       struct stat St;
454       if (stat(File.c_str(),&St) != 0 || S_ISREG(St.st_mode) == 0)
455          continue;
456       
457       List.push_back(File);      
458    }   
459    closedir(D);
460    
461    sort(List.begin(),List.end());
462
463    // Read the files
464    for (vector<string>::const_iterator I = List.begin(); I != List.end(); I++)
465       if (ReadAppend(*I) == false)
466          return false;
467    return true;
468
469 }
470                                                                         /*}}}*/
471