Big refactoring of MediaSetAccess
[platform/upstream/libzypp.git] / zypp / MediaSetAccess.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9
10 #include <iostream>
11 #include <fstream>
12
13 #include "zypp/base/LogTools.h"
14 #include "zypp/base/Regex.h"
15 #include "zypp/base/UserRequestException.h"
16 #include "zypp/ZYppCallbacks.h"
17 #include "zypp/MediaSetAccess.h"
18 #include "zypp/PathInfo.h"
19 //#include "zypp/source/MediaSetAccessReportReceivers.h"
20
21 using namespace std;
22
23 ///////////////////////////////////////////////////////////////////
24 namespace zypp
25 { /////////////////////////////////////////////////////////////////
26
27 IMPL_PTR_TYPE(MediaSetAccess);
28
29 ///////////////////////////////////////////////////////////////////
30
31   MediaSetAccess::MediaSetAccess(const Url &url,
32                                  const Pathname & prefered_attach_point)
33       : _url(url)
34       , _prefAttachPoint(prefered_attach_point)
35   {}
36
37   MediaSetAccess::MediaSetAccess(const std::string & label_r,
38                                  const Url &url,
39                                  const Pathname & prefered_attach_point)
40       : _url(url)
41       , _prefAttachPoint(prefered_attach_point)
42       , _label( label_r )
43   {}
44
45   MediaSetAccess::~MediaSetAccess()
46   {
47     try
48     {
49       release();
50     }
51     catch(...) {} // don't let exception escape a dtor.
52   }
53
54
55   void MediaSetAccess::setVerifier( unsigned media_nr, media::MediaVerifierRef verifier )
56   {
57     if (_medias.find(media_nr) != _medias.end())
58     {
59       // the media already exists, set theverifier
60       media::MediaAccessId id = _medias[media_nr];
61       media::MediaManager media_mgr;
62       media_mgr.addVerifier( id, verifier );
63       // remove any saved verifier for this media
64       _verifiers.erase(media_nr);
65       //if (! noattach && ! media_mgr.isAttached(id))
66       //media_mgr.attach(id);
67     }
68     else
69     {
70       // save the verifier in the map, and set it when
71       // the media number is first attached
72       _verifiers[media_nr] = verifier;
73     }
74   }
75
76 //       callback::SendReport<source::DownloadFileReport> report;
77 //       DownloadProgressFileReceiver download_report( report );
78 //       SourceFactory source_factory;
79 //       Url file_url( url().asString() + file_r.asString() );
80 //       report->start( source_factory.createFrom(this), file_url );
81 //       callback::TempConnect<media::DownloadProgressReport> tmp_download( download_report );
82 //       Pathname file = provideJustFile( file_r, media_nr, cached, checkonly );
83 //       report->finish( file_url, source::DownloadFileReport::NO_ERROR, "" );
84 //       return file;
85
86   void MediaSetAccess::releaseFile( const OnMediaLocation & on_media_file )
87   {
88     releaseFile( on_media_file.filename(), on_media_file.medianr() );
89   }
90
91   void MediaSetAccess::releaseFile( const Pathname & file, unsigned media_nr)
92   {
93     media::MediaManager media_mgr;
94     media::MediaAccessId media;
95
96     media = getMediaAccessId( media_nr);
97     DBG << "Going to release file " << file
98         << " from media number " << media_nr << endl;
99
100     if ( ! media_mgr.isAttached(media) )
101       return; //disattached media is free
102
103     media_mgr.releaseFile (media, file);
104   }
105
106   void MediaSetAccess::dirInfo( filesystem::DirContent &retlist, const Pathname &dirname,
107                                 bool dots, unsigned media_nr )
108   {
109     media::MediaManager media_mgr;
110     media::MediaAccessId media;
111     media = getMediaAccessId(media_nr);
112
113     // try to attach the media
114     if ( ! media_mgr.isAttached(media) )
115         media_mgr.attachDesiredMedia(media);
116
117     media_mgr.dirInfo(media, retlist, dirname, dots);
118   }
119
120   Pathname MediaSetAccess::provideFile( const OnMediaLocation & resource, ProvideFileOptions options )
121   {
122     return provide( ProvideFile, resource, options );
123   }
124
125   Pathname MediaSetAccess::provideFile(const Pathname & file, unsigned media_nr, ProvideFileOptions options )
126   {
127     OnMediaLocation resource;
128     resource.setLocation(file, media_nr);
129     return provide( ProvideFile, resource, options );
130   }
131
132   Pathname MediaSetAccess::provideOptionalFile(const Pathname & file, unsigned media_nr )
133   {
134     OnMediaLocation resource;
135     resource.setLocation(file, media_nr);
136     Pathname ret;
137     try {
138         ret = provide(ProvideFile, resource, NON_INTERACTIVE);
139     }
140     catch ( const Exception &e )
141     {
142         ZYPP_CAUGHT(e);
143     }
144     return ret;
145   }
146
147
148   bool MediaSetAccess::doesFileExist(const Pathname & file, unsigned media_nr )
149   {
150     callback::SendReport<media::MediaChangeReport> report;
151     media::MediaManager media_mgr;
152
153     bool exists = false;
154     do
155     {
156       // get the mediaId, but don't try to attach it here
157       media::MediaAccessId media = getMediaAccessId( media_nr);
158
159       try
160       {
161         DBG << "Cheking if file " << file
162             << " from media number " << media_nr << " exists." << endl;
163         // try to attach the media
164         if ( ! media_mgr.isAttached(media) )
165           media_mgr.attachDesiredMedia(media);
166         exists = media_mgr.doesFileExist(media, file);
167         break;
168       }
169       catch ( media::MediaException & excp )
170       {
171         ZYPP_CAUGHT(excp);
172         media::MediaChangeReport::Action user;
173         do
174         {
175           DBG << "Media couldn't provide file " << file << " , releasing." << endl;
176           try
177           {
178             media_mgr.release (media);
179           }
180           catch (const Exception & excpt_r)
181           {
182               ZYPP_CAUGHT(excpt_r);
183               MIL << "Failed to release media " << media << endl;
184           }
185
186           // set up the reason
187           media::MediaChangeReport::Error reason = media::MediaChangeReport::INVALID;
188
189           if( typeid(excp) == typeid( media::MediaFileNotFoundException )  ||
190               typeid(excp) == typeid( media::MediaNotAFileException ) )
191           {
192             reason = media::MediaChangeReport::NOT_FOUND;
193           }
194           else if( typeid(excp) == typeid( media::MediaNotDesiredException)  ||
195               typeid(excp) == typeid( media::MediaNotAttachedException) )
196           {
197             reason = media::MediaChangeReport::WRONG;
198           }
199           else if( typeid(excp) == typeid( media::MediaTimeoutException) ||
200                    typeid(excp) == typeid( media::MediaTemporaryProblemException))
201           {
202             reason = media::MediaChangeReport::IO_SOFT;
203           }
204
205           vector<string> devices;
206           unsigned int devindex;
207           media_mgr.getDetectedDevices(media, devices, devindex);
208
209           // release all media before requesting another (#336881)
210           media_mgr.releaseAll();
211
212           user = report->requestMedia (
213               _url,
214               media_nr,
215               _label,
216               reason,
217               excp.asUserString(),
218               devices, devindex
219             );
220
221           MIL << "doesFileExist exception caught, callback answer: " << user << endl;
222
223           if( user == media::MediaChangeReport::ABORT )
224           {
225             DBG << "Aborting" << endl;
226             ZYPP_RETHROW ( excp );
227           }
228           else if ( user == media::MediaChangeReport::IGNORE )
229           {
230             DBG << "Skipping" << endl;
231             SkipRequestException nexcp("User-requested skipping of a file");
232             nexcp.remember(excp);
233             ZYPP_THROW(nexcp);
234           }
235           else if ( user == media::MediaChangeReport::EJECT )
236           {
237             DBG << "Eject: try to release" << endl;
238             media_mgr.releaseAll();
239             // eject
240             media_mgr.release (media,
241               devindex < devices.size() ? devices[devindex] : "");
242           }
243           else if ( user == media::MediaChangeReport::RETRY  ||
244             user == media::MediaChangeReport::CHANGE_URL )
245           {
246             // retry
247             DBG << "Going to try again" << endl;
248             // invalidate current media access id
249             media_mgr.close(media);
250             _medias.erase(media_nr);
251
252             // not attaching, media set will do that for us
253             // this could generate uncaught exception (#158620)
254             break;
255           }
256           else
257           {
258             DBG << "Don't know, let's ABORT" << endl;
259             ZYPP_RETHROW ( excp );
260           }
261         } while( user == media::MediaChangeReport::EJECT );
262       }
263
264       // retry or change URL
265     } while( true );
266
267     return exists;
268   }
269
270   Pathname MediaSetAccess::provide( ProvideAction action,
271                                     const OnMediaLocation &resource,
272                                     ProvideFileOptions options )
273   {
274     Pathname file(resource.filename());
275     unsigned media_nr(resource.medianr());
276       
277     callback::SendReport<media::MediaChangeReport> report;
278     media::MediaManager media_mgr;
279
280     media::MediaAccessId media;
281
282     do
283     {
284       // get the mediaId, but don't try to attach it here
285       media = getMediaAccessId( media_nr);
286
287       try
288       {
289         DBG << "Going to try to provide file " << file
290             << " from media number " << media_nr << endl;
291         // try to attach the media
292         if ( ! media_mgr.isAttached(media) )
293           media_mgr.attachDesiredMedia(media);
294
295         switch (action)
296         {
297         case ProvideFile:
298             media_mgr.provideFile (media, file); break;
299         case ProvideDir:
300             media_mgr.provideDir (media, file); break;
301         case ProvideDirRecursive:
302             media_mgr.provideDirTree(media, file); break;
303         default:
304             ZYPP_THROW(Exception("Unknown action"));
305         }
306         break;
307       }
308       catch ( media::MediaException & excp )
309       {
310         ZYPP_CAUGHT(excp);
311         media::MediaChangeReport::Action user = media::MediaChangeReport::ABORT;
312         unsigned int devindex = 0;
313         vector<string> devices;
314         media_mgr.getDetectedDevices(media, devices, devindex);
315
316         do
317         {
318           if (user != media::MediaChangeReport::EJECT) // no use in calling this again
319           {
320             DBG << "Media couldn't provide file " << file << " , releasing." << endl;
321             try
322             {
323               media_mgr.release(media);
324             }
325             catch (const Exception & excpt_r)
326             {
327                 ZYPP_CAUGHT(excpt_r);
328                 MIL << "Failed to release media " << media << endl;
329             }
330           }
331
332           // set up the reason
333           media::MediaChangeReport::Error reason = media::MediaChangeReport::INVALID;
334
335           if( typeid(excp) == typeid( media::MediaFileNotFoundException )  ||
336               typeid(excp) == typeid( media::MediaNotAFileException ) )
337           {
338             reason = media::MediaChangeReport::NOT_FOUND;
339           }
340           else if( typeid(excp) == typeid( media::MediaNotDesiredException)  ||
341               typeid(excp) == typeid( media::MediaNotAttachedException) )
342           {
343             reason = media::MediaChangeReport::WRONG;
344           }
345           else if( typeid(excp) == typeid( media::MediaTimeoutException) ||
346                    typeid(excp) == typeid( media::MediaTemporaryProblemException))
347           {
348             reason = media::MediaChangeReport::IO_SOFT;
349           }
350
351           // non interactive only bother the user if wrong medium is in the drive
352           // otherwise propagate the error
353           if ( ( options & NON_INTERACTIVE ) && reason != media::MediaChangeReport::WRONG)
354           {
355               ZYPP_RETHROW(excp);
356           }
357           else
358           {
359             // release all media before requesting another (#336881)
360             media_mgr.releaseAll();
361
362             user = report->requestMedia (
363               _url,
364               media_nr,
365               _label,
366               reason,
367               excp.asUserString(),
368               devices,
369               devindex
370             );
371           }
372
373           MIL << "ProvideFile exception caught, callback answer: " << user << endl;
374
375           if( user == media::MediaChangeReport::ABORT )
376           {
377             DBG << "Aborting" << endl;
378             ZYPP_RETHROW ( excp );
379           }
380           else if ( user == media::MediaChangeReport::IGNORE )
381           {
382             DBG << "Skipping" << endl;
383             SkipRequestException nexcp("User-requested skipping of a file");
384             nexcp.remember(excp);
385             ZYPP_THROW(nexcp);
386           }
387           else if ( user == media::MediaChangeReport::EJECT )
388           {
389             DBG << "Eject: try to release" << endl;
390             media_mgr.releaseAll();
391             // eject
392             media_mgr.release (media,
393               devindex < devices.size() ? devices[devindex] : "");
394           }
395           else if ( user == media::MediaChangeReport::RETRY  ||
396             user == media::MediaChangeReport::CHANGE_URL )
397           {
398             // retry
399             DBG << "Going to try again" << endl;
400             // invalidate current media access id
401             media_mgr.close(media);
402             _medias.erase(media_nr);
403
404             // not attaching, media set will do that for us
405             // this could generate uncaught exception (#158620)
406             break;
407           }
408           else
409           {
410             DBG << "Don't know, let's ABORT" << endl;
411             ZYPP_RETHROW ( excp );
412           }
413         } while( user == media::MediaChangeReport::EJECT );
414       }
415
416       // retry or change URL
417     } while( true );
418
419     return media_mgr.localPath( media, file );
420   }
421
422
423   Pathname MediaSetAccess::provideDir(const Pathname & dir,
424                                       bool recursive,
425                                       unsigned media_nr,
426                                       ProvideFileOptions options )
427   {
428     OnMediaLocation resource;
429     resource.setLocation(dir, media_nr);
430
431     return provide( recursive? ProvideDir : ProvideDirRecursive, resource, options );
432   }
433
434   media::MediaAccessId MediaSetAccess::getMediaAccessId (media::MediaNr medianr)
435   {
436     media::MediaManager media_mgr;
437
438     if (_medias.find(medianr) != _medias.end())
439     {
440       media::MediaAccessId id = _medias[medianr];
441       //if (! noattach && ! media_mgr.isAttached(id))
442       //media_mgr.attach(id);
443       return id;
444     }
445     Url url;
446     url = rewriteUrl (_url, medianr);
447     media::MediaAccessId id = media_mgr.open(url, _prefAttachPoint);
448     _medias[medianr] = id;
449
450     try
451     {
452       if (_verifiers.find(medianr) != _verifiers.end())
453       {
454         // a verifier is set for this media
455         // FIXME check the case where the verifier exists
456         // but we have no access id for the media
457         media::MediaAccessId id = _medias[medianr];
458         media::MediaManager media_mgr;
459         media_mgr.delVerifier(id);
460         media_mgr.addVerifier( id, _verifiers[medianr] );
461         // remove any saved verifier for this media
462         _verifiers.erase(medianr);
463       }
464     }
465     catch ( const Exception &e )
466     {
467       ZYPP_CAUGHT(e);
468       WAR << "Verifier not found" << endl;
469     }
470
471     return id;
472   }
473
474
475   Url MediaSetAccess::rewriteUrl (const Url & url_r, const media::MediaNr medianr)
476   {
477     std::string scheme = url_r.getScheme();
478     if (scheme == "cd" || scheme == "dvd")
479       return url_r;
480
481     DBG << "Rewriting url " << url_r << endl;
482
483     if( scheme == "iso")
484     {
485       // TODO the iso parameter will not be required in the future, this
486       // code has to be adapted together with the MediaISO change.
487       // maybe some MediaISOURL interface should be used.
488       std::string isofile = url_r.getQueryParam("iso");
489       str::regex e("^(.*)(cd|dvd)[0-9]+\\.iso$", str::regex::icase);
490
491       str::smatch what;
492       if(str::regex_match(isofile, what, e))
493       {
494         Url url( url_r);
495         isofile = what[1] + what[2] + str::numstring(medianr) + ".iso";
496         url.setQueryParam("iso", isofile);
497         DBG << "Url rewrite result: " << url << endl;
498         return url;
499       }
500     }
501     else
502     {
503       std::string pathname = url_r.getPathName();
504       str::regex e("^(.*)(cd|dvd)[0-9]+(/)?$", str::regex::icase);
505       str::smatch what;
506       if(str::regex_match(pathname, what, e))
507       {
508         Url url( url_r);
509         pathname = what[1] + what[2] + str::numstring(medianr) + what[3];
510         url.setPathName(pathname);
511         DBG << "Url rewrite result: " << url << endl;
512         return url;
513       }
514     }
515     return url_r;
516   }
517
518   void MediaSetAccess::release()
519   {
520     DBG << "Releasing all media IDs held by this MediaSetAccess" << endl;
521     media::MediaManager manager;
522     for (MediaMap::const_iterator m = _medias.begin(); m != _medias.end(); ++m)
523       manager.release(m->second, "");
524   }
525
526   std::ostream & MediaSetAccess::dumpOn( std::ostream & str ) const
527   {
528     str << "MediaSetAccess (URL='" << _url << "', attach_point_hint='" << _prefAttachPoint << "')";
529     return str;
530   }
531
532 //     media::MediaVerifierRef MediaSetAccess::verifier(unsigned media_nr)
533 //     { return media::MediaVerifierRef(new media::NoVerifier()); }
534
535 /////////////////////////////////////////////////////////////////
536 } // namespace zypp
537 ///////////////////////////////////////////////////////////////////