- initial import of revision 374 from cnc
[apt.git] / methods / cdrom.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description                                                          /*{{{*/
3 // $Id: cdrom.cc,v 1.20 2003/02/10 07:34:41 doogie Exp $
4 /* ######################################################################
5
6    CDROM URI method for APT
7    
8    ##################################################################### */
9                                                                         /*}}}*/
10 // Include Files                                                        /*{{{*/
11 #include <apt-pkg/acquire-method.h>
12 #include <apt-pkg/cdromutl.h>
13 #include <apt-pkg/error.h>
14 #include <apt-pkg/configuration.h>
15 #include <apt-pkg/fileutl.h>
16
17 #include <sys/stat.h>
18 #include <unistd.h>
19
20 #include <iostream>
21
22 // CNC:2003-02-20 - Moved header to fix compilation error when
23 //                  --disable-nls is used.
24 #include <apti18n.h>
25                                                                         /*}}}*/
26 // CNC:2002-10-18
27 #include <utime.h>  
28
29 using namespace std;
30
31 class CDROMMethod : public pkgAcqMethod
32 {
33    bool DatabaseLoaded;
34    ::Configuration Database;
35    string CurrentID;
36    string CDROM;
37    bool Mounted;
38    
39    virtual bool Fetch(FetchItem *Itm);
40    string GetID(string Name);
41    virtual void Exit();
42    virtual string PreferredURI();
43
44    // CNC:2002-10-18
45    bool Copy(string Src, string Dest);
46    
47    public:
48    
49    CDROMMethod();
50 };
51
52 // CDROMMethod::CDROMethod - Constructor                                /*{{{*/
53 // ---------------------------------------------------------------------
54 /* */
55 CDROMMethod::CDROMMethod() : pkgAcqMethod("1.0",SingleInstance | LocalOnly |
56                                           SendConfig | NeedsCleanup |
57                                           Removable | HasPreferredURI), 
58                                           DatabaseLoaded(false), 
59                                           Mounted(false)
60 {
61 };
62                                                                         /*}}}*/
63 // CNC:2004-04-27
64 // CDROMMethod::PreferredURI() -                                        /*{{{*/
65 // ---------------------------------------------------------------------
66 /* */
67 string CDROMMethod::PreferredURI()
68 {
69    CDROM = _config->FindDir("Acquire::cdrom::mount","/cdrom/");
70    if (CDROM[0] == '.')
71       CDROM= SafeGetCWD() + '/' + CDROM;
72    string ID;
73    MountCdrom(CDROM);
74    if (IdentCdrom(CDROM,ID,2) == true) {
75       if (DatabaseLoaded == false)
76       {
77          string DFile = _config->FindFile("Dir::State::cdroms");
78          if (FileExists(DFile) == true)
79          {
80             if (ReadConfigFile(Database,DFile) == false) {
81                _error->Error(_("Unable to read the cdrom database %s"),
82                              DFile.c_str());
83                return "";
84             }
85          }
86          DatabaseLoaded = true;
87       }
88       string Name = Database.Find("CD::"+ID);
89       if (Name.empty() == false)
90          return "cdrom:[" + Name + "]";
91    }
92    return "";
93 }
94                                                                         /*}}}*/
95 // CDROMMethod::Exit - Unmount the disc if necessary                    /*{{{*/
96 // ---------------------------------------------------------------------
97 /* */
98 void CDROMMethod::Exit()
99 {
100    if (Mounted == true)
101       UnmountCdrom(CDROM);
102 }
103                                                                         /*}}}*/
104 // CDROMMethod::GetID - Search the database for a matching string       /*{{{*/
105 // ---------------------------------------------------------------------
106 /* */
107 string CDROMMethod::GetID(string Name)
108 {
109    // Search for an ID
110    const Configuration::Item *Top = Database.Tree("CD");
111    if (Top != 0)
112       Top = Top->Child;
113    
114    for (; Top != 0;)
115    {      
116       if (Top->Value == Name)
117          return Top->Tag;
118       
119       Top = Top->Next;
120    }
121    return string();
122 }
123                                                                         /*}}}*/
124 // CNC:2002-10-18
125 bool CDROMMethod::Copy(string Src, string Dest)
126 {
127    // See if the file exists
128    FileFd From(Src,FileFd::ReadOnly);
129    FileFd To(Dest,FileFd::WriteEmpty);
130    To.EraseOnFailure();
131    if (_error->PendingError() == true)
132    {
133       To.OpFail();
134       return false;
135    }
136    
137    // Copy the file
138    if (CopyFile(From,To) == false)
139    {
140       To.OpFail();
141       return false;
142    }
143
144    From.Close();
145    To.Close();
146  
147    struct stat Buf;
148    if (stat(Src.c_str(),&Buf) != 0)
149        return _error->Error("File not found");      
150    
151    // Transfer the modification times
152    struct utimbuf TimeBuf;
153    TimeBuf.actime = Buf.st_atime;
154    TimeBuf.modtime = Buf.st_mtime;
155    if (utime(Dest.c_str(),&TimeBuf) != 0)
156    {
157       To.OpFail();
158       return _error->Errno("utime","Failed to set modification time");
159    }
160    return true;
161 }
162
163 // CDROMMethod::Fetch - Fetch a file                                    /*{{{*/
164 // ---------------------------------------------------------------------
165 /* */
166 bool CDROMMethod::Fetch(FetchItem *Itm)
167 {
168    URI Get = Itm->Uri;
169    string File = Get.Path;
170    FetchResult Res;
171
172    bool Debug = _config->FindB("Debug::Acquire::cdrom",false);
173
174    /* All IMS queries are returned as a hit, CDROMs are readonly so 
175       time stamps never change */
176    if (Itm->LastModified != 0)
177    {
178       Res.LastModified = Itm->LastModified;
179       Res.IMSHit = true;
180       Res.Filename = File;
181       URIDone(Res);
182       return true;
183    }
184
185    // Load the database
186    if (DatabaseLoaded == false)
187    {
188       // Read the database
189       string DFile = _config->FindFile("Dir::State::cdroms");
190       if (FileExists(DFile) == true)
191       {
192          if (ReadConfigFile(Database,DFile) == false)
193             return _error->Error(_("Unable to read the cdrom database %s"),
194                           DFile.c_str());
195       }
196       DatabaseLoaded = true;
197    }
198        
199    // All non IMS queries for package files fail.
200    if (Itm->IndexFile == true || GetID(Get.Host).empty() == true)
201    {
202       Fail(_("Please use apt-cdrom to make this CD recognized by APT."
203            " apt-get update cannot be used to add new CDs"));
204       return true;
205    }
206
207    // We already have a CD inserted, but it is the wrong one
208    if (CurrentID.empty() == false && Database.Find("CD::" + CurrentID) != Get.Host)
209    {
210       Fail(_("Wrong CD"),true);
211       return true;
212    }
213    
214    CDROM = _config->FindDir("Acquire::cdrom::mount","/cdrom/");
215    if (CDROM[0] == '.')
216       CDROM= SafeGetCWD() + '/' + CDROM;
217    string NewID;
218    while (CurrentID.empty() == true)
219    {
220       bool Hit = false;
221       Mounted = MountCdrom(CDROM);
222       for (unsigned int Version = 2; Version != 0; Version--)
223       {
224          if (IdentCdrom(CDROM,NewID,Version) == false)
225             return false;
226          
227          if (Debug == true)
228             clog << "ID " << Version << " " << NewID << endl;
229       
230          // A hit
231          if (Database.Find("CD::" + NewID) == Get.Host)
232          {
233             Hit = true;
234             break;
235          }       
236       }
237
238       if (Hit == true)
239          break;
240          
241       // I suppose this should prompt somehow?
242       if (UnmountCdrom(CDROM) == false)
243          return _error->Error(_("Unable to unmount the CD-ROM in %s, it may still be in use."),
244                               CDROM.c_str());
245       if (MediaFail(Get.Host,CDROM) == false)
246       {
247          CurrentID = "FAIL";
248          Fail(_("Wrong CD"),true);
249          return true;
250       }
251    }
252    
253    // CNC:2002-10-18
254    // Found a CD
255    if (_config->FindB("Acquire::CDROM::Copy-All", false) == true ||
256        _config->FindB("Acquire::CDROM::Copy", false) == true) {
257       Res.Filename = Queue->DestFile;
258       URIStart(Res);
259       Copy(CDROM+File, Queue->DestFile);
260    } else {
261       Res.Filename = CDROM + File;
262    }
263    
264    struct stat Buf;
265    if (stat(Res.Filename.c_str(),&Buf) != 0)
266       return _error->Error(_("File not found"));
267    
268    if (NewID.empty() == false)
269       CurrentID = NewID;
270    Res.LastModified = Buf.st_mtime;
271    Res.Size = Buf.st_size;
272    URIDone(Res);
273    return true;
274 }
275                                                                         /*}}}*/
276
277 int main()
278 {
279    CDROMMethod Mth;
280    return Mth.Run();
281 }