- initial import of revision 374 from cnc
[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
117    // Grab the rest of the dists
118    if (ParseQuoteWord(Buffer,Section) == false)
119       return _error->Error(_("Malformed line %lu in source list %s (dist parse)"),CurLine,File.c_str());
120    
121    do
122    {
123       if (CreateItem(List,URI,Dist,Section,Vendor) == false)
124          return false;
125    }
126    while (ParseQuoteWord(Buffer,Section) == true);
127    
128    return true;
129 }
130                                                                         /*}}}*/
131
132 // SourceList::pkgSourceList - Constructors                             /*{{{*/
133 // ---------------------------------------------------------------------
134 /* */
135 pkgSourceList::pkgSourceList()
136 {
137 }
138
139 pkgSourceList::pkgSourceList(string File)
140 {
141    Read(File);
142 }
143                                                                         /*}}}*/
144 // SourceList::~pkgSourceList - Destructor                              /*{{{*/
145 // ---------------------------------------------------------------------
146 /* */
147 pkgSourceList::~pkgSourceList()
148 {
149    for (const_iterator I = SrcList.begin(); I != SrcList.end(); I++)
150       delete *I;
151    for (vector<Vendor const *>::const_iterator I = VendorList.begin(); 
152         I != VendorList.end(); I++)
153       delete *I;
154 }
155                                                                         /*}}}*/
156 // SourceList::ReadVendors - Read list of known package vendors         /*{{{*/
157 // ---------------------------------------------------------------------
158 /* This also scans a directory of vendor files similar to apt.conf.d 
159    which can contain the usual suspects of distribution provided data.
160    The APT config mechanism allows the user to override these in their
161    configuration file. */
162 bool pkgSourceList::ReadVendors()
163 {
164    Configuration Cnf;
165
166    string CnfFile = _config->FindDir("Dir::Etc::vendorparts");
167    if (FileExists(CnfFile) == true)
168       if (ReadConfigDir(Cnf,CnfFile,true) == false)
169          return false;
170    CnfFile = _config->FindFile("Dir::Etc::vendorlist");
171    if (FileExists(CnfFile) == true)
172       if (ReadConfigFile(Cnf,CnfFile,true) == false)
173          return false;
174
175    for (vector<Vendor const *>::const_iterator I = VendorList.begin(); 
176         I != VendorList.end(); I++)
177       delete *I;
178    VendorList.erase(VendorList.begin(),VendorList.end());
179    
180    // Process 'simple-key' type sections
181    const Configuration::Item *Top = Cnf.Tree("simple-key");
182    for (Top = (Top == 0?0:Top->Child); Top != 0; Top = Top->Next)
183    {
184       Configuration Block(Top);
185       Vendor *Vendor;
186       
187       Vendor = new pkgSourceList::Vendor;
188       
189       Vendor->VendorID = Top->Tag;
190       Vendor->FingerPrint = Block.Find("Fingerprint");
191       Vendor->Description = Block.Find("Name");
192
193       // CNC:2002-08-15
194       char *buffer = new char[Vendor->FingerPrint.length()+1];
195       char *p = buffer;;
196       for (string::const_iterator I = Vendor->FingerPrint.begin();
197            I != Vendor->FingerPrint.end(); I++)
198       {
199          if (*I != ' ' && *I != '\t')
200             *p++ = *I;
201       }
202       *p = 0;
203       Vendor->FingerPrint = buffer;
204       delete [] buffer;
205       
206       if (Vendor->FingerPrint.empty() == true || 
207           Vendor->Description.empty() == true)
208       {
209          _error->Error(_("Vendor block %s is invalid"), Vendor->VendorID.c_str());
210          delete Vendor;
211          continue;
212       }
213       
214       VendorList.push_back(Vendor);
215    }
216
217    /* XXX Process 'group-key' type sections
218       This is currently faked out so that the vendors file format is
219       parsed but nothing is done with it except check for validity */
220    Top = Cnf.Tree("group-key");
221    for (Top = (Top == 0?0:Top->Child); Top != 0; Top = Top->Next)
222    {
223       Configuration Block(Top);
224       Vendor *Vendor;
225       
226       Vendor = new pkgSourceList::Vendor;
227       
228       Vendor->VendorID = Top->Tag;
229       Vendor->Description = Block.Find("Name");
230
231       if (Vendor->Description.empty() == true)
232       {
233          _error->Error(_("Vendor block %s is invalid"), 
234                        Vendor->VendorID.c_str());
235          delete Vendor;
236          continue;
237       }
238       
239       VendorList.push_back(Vendor);
240    }
241    
242    return !_error->PendingError();
243 }
244                                                                         /*}}}*/
245 // SourceList::ReadMainList - Read the main source list from etc        /*{{{*/
246 // ---------------------------------------------------------------------
247 /* */
248 bool pkgSourceList::ReadMainList()
249 {
250    // CNC:2003-03-03 - Multiple sources list support.
251    bool Res = ReadVendors();
252    if (Res == false)
253       return false;
254    
255    Reset();
256    // CNC:2003-11-28 - Entries in sources.list have priority over
257    //                  entries in sources.list.d.
258    string Main = _config->FindFile("Dir::Etc::sourcelist");
259    if (FileExists(Main) == true)
260       Res &= ReadAppend(Main);   
261
262    string Parts = _config->FindDir("Dir::Etc::sourceparts");
263    if (FileExists(Parts) == true)
264       Res &= ReadSourceDir(Parts);
265    
266    return Res;
267 }
268                                                                         /*}}}*/
269 // CNC:2003-03-03 - Needed to preserve backwards compatibility.
270 // SourceList::Reset - Clear the sourcelist contents                    /*{{{*/
271 // ---------------------------------------------------------------------
272 /* */
273 void pkgSourceList::Reset()
274 {
275    for (const_iterator I = SrcList.begin(); I != SrcList.end(); I++)
276       delete *I;
277    SrcList.erase(SrcList.begin(),SrcList.end());
278    // CNC:2003-11-21
279    _system->AddSourceFiles(SrcList);
280 }
281                                                                         /*}}}*/
282 // CNC:2003-03-03 - Function moved to ReadAppend() and Reset().
283 // SourceList::Read - Parse the sourcelist file                         /*{{{*/
284 // ---------------------------------------------------------------------
285 /* */
286 bool pkgSourceList::Read(string File)
287 {
288    Reset();
289    return ReadAppend(File);
290 }
291                                                                         /*}}}*/
292 // SourceList::ReadAppend - Parse a sourcelist file                     /*{{{*/
293 // ---------------------------------------------------------------------
294 /* */
295 bool pkgSourceList::ReadAppend(string File)
296 {
297    // Open the stream for reading
298    ifstream F(File.c_str(),ios::in /*| ios::nocreate*/);
299    if (!F != 0)
300       return _error->Errno("ifstream::ifstream",_("Opening %s"),File.c_str());
301    
302 #if 0 // Now Reset() does this.
303    for (const_iterator I = SrcList.begin(); I != SrcList.end(); I++)
304       delete *I;
305    SrcList.erase(SrcList.begin(),SrcList.end());
306 #endif
307    // CNC:2003-12-10 - 300 is too short.
308    char Buffer[1024];
309
310    int CurLine = 0;
311    while (F.eof() == false)
312    {
313       F.getline(Buffer,sizeof(Buffer));
314       CurLine++;
315       _strtabexpand(Buffer,sizeof(Buffer));
316       if (F.fail() && !F.eof())
317          return _error->Error(_("Line %u too long in source list %s."),
318                               CurLine,File.c_str());
319
320       
321       char *I;
322       // CNC:2003-02-20 - Do not break if '#' is inside [].
323       for (I = Buffer; *I != 0 && *I != '#'; I++)
324          if (*I == '[')
325             for (I++; *I != 0 && *I != ']'; I++);
326       *I = 0;
327       
328       const char *C = _strstrip(Buffer);
329       
330       // Comment or blank
331       if (C[0] == '#' || C[0] == 0)
332          continue;
333             
334       // Grok it
335       string LineType;
336       if (ParseQuoteWord(C,LineType) == false)
337          return _error->Error(_("Malformed line %u in source list %s (type)"),CurLine,File.c_str());
338
339       Type *Parse = Type::GetType(LineType.c_str());
340       if (Parse == 0)
341          return _error->Error(_("Type '%s' is not known in on line %u in source list %s"),LineType.c_str(),CurLine,File.c_str());
342       
343       // Authenticated repository
344       Vendor const *Vndr = 0;
345       if (C[0] == '[')
346       {
347          string VendorID;
348          
349          if (ParseQuoteWord(C,VendorID) == false)
350              return _error->Error(_("Malformed line %u in source list %s (vendor id)"),CurLine,File.c_str());
351
352          if (VendorID.length() < 2 || VendorID.end()[-1] != ']')
353              return _error->Error(_("Malformed line %u in source list %s (vendor id)"),CurLine,File.c_str());
354          VendorID = string(VendorID,1,VendorID.size()-2);
355          
356          for (vector<Vendor const *>::const_iterator iter = VendorList.begin();
357               iter != VendorList.end(); iter++) 
358          {
359             if ((*iter)->VendorID == VendorID)
360             {
361                Vndr = *iter;
362                break;
363             }
364          }
365
366          if (Vndr == 0)
367             return _error->Error(_("Unknown vendor ID '%s' in line %u of source list %s"),
368                                  VendorID.c_str(),CurLine,File.c_str());
369       }
370       
371       if (Parse->ParseLine(SrcList,Vndr,C,CurLine,File) == false)
372          return false;
373    }
374    return true;
375 }
376                                                                         /*}}}*/
377 // SourceList::FindIndex - Get the index associated with a file         /*{{{*/
378 // ---------------------------------------------------------------------
379 /* */
380 bool pkgSourceList::FindIndex(pkgCache::PkgFileIterator File,
381                               pkgIndexFile *&Found) const
382 {
383    for (const_iterator I = SrcList.begin(); I != SrcList.end(); I++)
384    {
385       if ((*I)->FindInCache(*File.Cache()) == File)
386       {
387          Found = *I;
388          return true;
389       }
390    }
391    
392    return false;
393 }
394                                                                         /*}}}*/
395 // SourceList::GetIndexes - Load the index files into the downloader    /*{{{*/
396 // ---------------------------------------------------------------------
397 /* */
398 bool pkgSourceList::GetIndexes(pkgAcquire *Owner) const
399 {
400    for (const_iterator I = SrcList.begin(); I != SrcList.end(); I++)
401       if ((*I)->GetIndexes(Owner) == false)
402          return false;
403    return true;
404 }
405                                                                         /*}}}*/
406 // CNC:2002-07-04
407 // SourceList::GetReleases - Load release files into the downloader     /*{{{*/
408 // ---------------------------------------------------------------------
409 /* */
410 bool pkgSourceList::GetReleases(pkgAcquire *Owner) const
411 {
412    for (const_iterator I = SrcList.begin(); I != SrcList.end(); I++)
413       if ((*I)->GetReleases(Owner) == false)
414          return false;
415    return true;
416 }
417                                                                         /*}}}*/
418 // CNC:2003-03-03 - By Anton V. Denisov <avd@altlinux.org>.
419 // SourceList::ReadSourceDir - Read a directory with sources files
420 // Based on ReadConfigDir()                                             /*{{{*/
421 // ---------------------------------------------------------------------
422 /* */
423 bool pkgSourceList::ReadSourceDir(string Dir)
424 {
425    DIR *D = opendir(Dir.c_str());
426    if (D == 0)
427       return _error->Errno("opendir",_("Unable to read %s"),Dir.c_str());
428
429    vector<string> List;
430    
431    for (struct dirent *Ent = readdir(D); Ent != 0; Ent = readdir(D))
432    {
433       if (Ent->d_name[0] == '.')
434          continue;
435
436       // CNC:2003-12-02 Only accept .list files as valid sourceparts
437       if (flExtension(Ent->d_name) != "list")
438          continue;
439       
440       // Skip bad file names ala run-parts
441       const char *C = Ent->d_name;
442       for (; *C != 0; C++)
443          if (isalpha(*C) == 0 && isdigit(*C) == 0
444              && *C != '_' && *C != '-' && *C != '.')
445             break;
446       if (*C != 0)
447          continue;
448       
449       // Make sure it is a file and not something else
450       string File = flCombine(Dir,Ent->d_name);
451       struct stat St;
452       if (stat(File.c_str(),&St) != 0 || S_ISREG(St.st_mode) == 0)
453          continue;
454       
455       List.push_back(File);      
456    }   
457    closedir(D);
458    
459    sort(List.begin(),List.end());
460
461    // Read the files
462    for (vector<string>::const_iterator I = List.begin(); I != List.end(); I++)
463       if (ReadAppend(*I) == false)
464          return false;
465    return true;
466
467 }
468                                                                         /*}}}*/
469