- MediaSetAccess::provideOptionalFile() added
[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 & on_media_file )
121   {
122     return provideFile( on_media_file.filename(), on_media_file.medianr() );
123   }
124
125
126   Pathname MediaSetAccess::provideFile(const Pathname & file, unsigned media_nr )
127   {
128     return provideFileInternal( file, media_nr, false, false);
129   }
130
131   Pathname MediaSetAccess::provideOptionalFile(const Pathname & file, unsigned media_nr )
132   {
133     return provideFileInternal( file, media_nr, false, true);
134   }
135
136   bool MediaSetAccess::doesFileExist(const Pathname & file, unsigned media_nr )
137   {
138     callback::SendReport<media::MediaChangeReport> report;
139     media::MediaManager media_mgr;
140
141     bool exists = false;
142
143     do
144     {
145       // get the mediaId, but don't try to attach it here
146       media::MediaAccessId media = getMediaAccessId( media_nr);
147
148       try
149       {
150         DBG << "Cheking if file " << file
151             << " from media number " << media_nr << " exists." << endl;
152         // try to attach the media
153         if ( ! media_mgr.isAttached(media) )
154           media_mgr.attachDesiredMedia(media);
155         exists = media_mgr.doesFileExist(media, file);
156         break;
157       }
158       catch ( media::MediaException & excp )
159       {
160         ZYPP_CAUGHT(excp);
161         media::MediaChangeReport::Action user;
162         do
163         {
164           DBG << "Media couldn't provide file " << file << " , releasing." << endl;
165           try
166           {
167             media_mgr.release (media);
168           }
169           catch (const Exception & excpt_r)
170           {
171               ZYPP_CAUGHT(excpt_r);
172               MIL << "Failed to release media " << media << endl;
173           }
174
175           // set up the reason
176           media::MediaChangeReport::Error reason = media::MediaChangeReport::INVALID;
177
178           if( typeid(excp) == typeid( media::MediaFileNotFoundException )  ||
179               typeid(excp) == typeid( media::MediaNotAFileException ) )
180           {
181             reason = media::MediaChangeReport::NOT_FOUND;
182           }
183           else if( typeid(excp) == typeid( media::MediaNotDesiredException)  ||
184               typeid(excp) == typeid( media::MediaNotAttachedException) )
185           {
186             reason = media::MediaChangeReport::WRONG;
187           }
188           else if( typeid(excp) == typeid( media::MediaTimeoutException))
189           {
190             reason = media::MediaChangeReport::IO_SOFT;
191           }
192
193           vector<string> devices;
194           unsigned int devindex;
195           media_mgr.getDetectedDevices(media, devices, devindex);
196
197           // release all media before requesting another (#336881)
198           media_mgr.releaseAll();
199
200           user = report->requestMedia (
201               _url,
202               media_nr,
203               _label,
204               reason,
205               excp.asUserString(),
206               devices, devindex
207             );
208
209           MIL << "doesFileExist exception caught, callback answer: " << user << endl;
210
211           if( user == media::MediaChangeReport::ABORT )
212           {
213             DBG << "Aborting" << endl;
214             ZYPP_RETHROW ( excp );
215           }
216           else if ( user == media::MediaChangeReport::IGNORE )
217           {
218             DBG << "Skipping" << endl;
219             SkipRequestException nexcp("User-requested skipping of a file");
220             nexcp.remember(excp);
221             ZYPP_THROW(nexcp);
222           }
223           else if ( user == media::MediaChangeReport::EJECT )
224           {
225             DBG << "Eject: try to release" << endl;
226             media_mgr.releaseAll();
227             // eject
228             media_mgr.release (media,
229               devindex < devices.size() ? devices[devindex] : "");
230           }
231           else if ( user == media::MediaChangeReport::RETRY  ||
232             user == media::MediaChangeReport::CHANGE_URL )
233           {
234             // retry
235             DBG << "Going to try again" << endl;
236             // invalidate current media access id
237             media_mgr.close(media);
238             _medias.erase(media_nr);
239
240             // not attaching, media set will do that for us
241             // this could generate uncaught exception (#158620)
242             break;
243           }
244           else
245           {
246             DBG << "Don't know, let's ABORT" << endl;
247             ZYPP_RETHROW ( excp );
248           }
249         } while( user == media::MediaChangeReport::EJECT );
250       }
251
252       // retry or change URL
253     } while( true );
254
255     return exists;
256   }
257
258   Pathname MediaSetAccess::provideFileInternal(const Pathname & file, unsigned media_nr, bool cached, bool checkonly )
259   {
260     callback::SendReport<media::MediaChangeReport> report;
261     media::MediaManager media_mgr;
262
263     media::MediaAccessId media;
264
265     do
266     {
267       // get the mediaId, but don't try to attach it here
268       media = getMediaAccessId( media_nr);
269
270       try
271       {
272         DBG << "Going to try to provide file " << file
273             << " from media number " << media_nr << endl;
274         // try to attach the media
275         if ( ! media_mgr.isAttached(media) )
276           media_mgr.attachDesiredMedia(media);
277         media_mgr.provideFile (media, file, false, false);
278         break;
279       }
280       catch ( media::MediaException & excp )
281       {
282         ZYPP_CAUGHT(excp);
283         media::MediaChangeReport::Action user = media::MediaChangeReport::ABORT;
284         unsigned int devindex = 0;
285         vector<string> devices;
286         media_mgr.getDetectedDevices(media, devices, devindex);
287
288         do
289         {
290           if (user != media::MediaChangeReport::EJECT) // no use in calling this again
291           {
292             DBG << "Media couldn't provide file " << file << " , releasing." << endl;
293             try
294             {
295               media_mgr.release(media);
296             }
297             catch (const Exception & excpt_r)
298             {
299                 ZYPP_CAUGHT(excpt_r);
300                 MIL << "Failed to release media " << media << endl;
301             }
302           }
303
304           // set up the reason
305           media::MediaChangeReport::Error reason = media::MediaChangeReport::INVALID;
306
307           if( typeid(excp) == typeid( media::MediaFileNotFoundException )  ||
308               typeid(excp) == typeid( media::MediaNotAFileException ) )
309           {
310             reason = media::MediaChangeReport::NOT_FOUND;
311           }
312           else if( typeid(excp) == typeid( media::MediaNotDesiredException)  ||
313               typeid(excp) == typeid( media::MediaNotAttachedException) )
314           {
315             reason = media::MediaChangeReport::WRONG;
316           }
317
318           // checkonly: only bother the user if wrong medium is in the drive
319           if (checkonly && reason != media::MediaChangeReport::WRONG)
320             return Pathname();
321           else
322           {
323             // release all media before requesting another (#336881)
324             media_mgr.releaseAll();
325
326             user = report->requestMedia (
327               _url,
328               media_nr,
329               _label,
330               reason,
331               excp.asUserString(),
332               devices,
333               devindex
334             );
335           }
336
337           MIL << "ProvideFile exception caught, callback answer: " << user << endl;
338
339           if( user == media::MediaChangeReport::ABORT )
340           {
341             DBG << "Aborting" << endl;
342             if (checkonly)
343               return Pathname();
344             ZYPP_RETHROW ( excp );
345           }
346           else if ( user == media::MediaChangeReport::IGNORE )
347           {
348             DBG << "Skipping" << endl;
349             if (checkonly)
350               return Pathname();
351             SkipRequestException nexcp("User-requested skipping of a file");
352             nexcp.remember(excp);
353             ZYPP_THROW(nexcp);
354           }
355           else if ( user == media::MediaChangeReport::EJECT )
356           {
357             DBG << "Eject: try to release" << endl;
358             media_mgr.releaseAll();
359             // eject
360             media_mgr.release (media,
361               devindex < devices.size() ? devices[devindex] : "");
362           }
363           else if ( user == media::MediaChangeReport::RETRY  ||
364             user == media::MediaChangeReport::CHANGE_URL )
365           {
366             // retry
367             DBG << "Going to try again" << endl;
368             // invalidate current media access id
369             media_mgr.close(media);
370             _medias.erase(media_nr);
371
372             // not attaching, media set will do that for us
373             // this could generate uncaught exception (#158620)
374             break;
375           }
376           else
377           {
378             DBG << "Don't know, let's ABORT" << endl;
379             if (checkonly)
380               return Pathname();
381             ZYPP_RETHROW ( excp );
382           }
383         } while( user == media::MediaChangeReport::EJECT );
384       }
385
386       // retry or change URL
387     } while( true );
388
389     return media_mgr.localPath( media, file );
390   }
391
392
393   Pathname MediaSetAccess::provideDir(const Pathname & dir,
394                                       bool recursive,
395                                       unsigned media_nr)
396   {
397     callback::SendReport<media::MediaChangeReport> report;
398     media::MediaManager media_mgr;
399     media::MediaAccessId _media;
400
401     do
402     {
403       // get the mediaId, but don't try to attach it here
404       _media = getMediaAccessId(media_nr);
405
406       try
407       {
408         DBG << "Going to try provide direcotry " << dir
409             << (recursive ? " (recursively)" : "")
410             << " from media nr. " << media_nr << endl;
411
412         // try to attach the media
413         if (!media_mgr.isAttached(_media))
414           media_mgr.attachDesiredMedia(_media);
415
416         _media = getMediaAccessId(media_nr); // in case of redirect
417
418         if (recursive)
419           media_mgr.provideDirTree(_media, dir);
420         else
421           media_mgr.provideDir(_media, dir);
422
423         break; // quit the retry loop
424       }
425       catch (media::MediaException & excp)
426       {
427         ZYPP_CAUGHT(excp);
428         media::MediaChangeReport::Action user;
429
430         do
431         {
432           DBG << "Media couldn't provide dir " << dir << ", releasing." << endl;
433           try
434           {
435             media_mgr.release (_media);
436           }
437           catch (const Exception & excpt_r)
438           {
439             ZYPP_CAUGHT(excpt_r);
440             ERR << "Failed to release media " << _media << endl;
441           }
442
443           //MIL << "Releasing all medias of all sources" << endl;
444           try
445           {
446             //! \todo do we need replacement for this at all?
447             //zypp::SourceManager::sourceManager()->releaseAllSources();
448           }
449           catch (const zypp::Exception& excpt_r)
450           {
451             ZYPP_CAUGHT(excpt_r);
452             ERR << "Failed to release all sources" << endl;
453           }
454
455           // set up the reason
456           media::MediaChangeReport::Error reason = media::MediaChangeReport::INVALID;
457
458           if (typeid(excp) == typeid(media::MediaFileNotFoundException)
459               || typeid(excp) == typeid(media::MediaNotAFileException))
460           {
461             reason = media::MediaChangeReport::NOT_FOUND;
462           }
463           else if (typeid(excp) == typeid( media::MediaNotDesiredException)
464               || typeid(excp) == typeid(media::MediaNotAttachedException))
465           {
466             reason = media::MediaChangeReport::WRONG;
467           }
468
469           vector<string> devices;
470           unsigned int devindex;
471           media_mgr.getDetectedDevices(_media, devices, devindex);
472
473           // release all media before requesting another (#336881)
474           media_mgr.releaseAll();
475
476           user = report->requestMedia(_url,
477                                       media_nr,
478                                       _label,
479                                       reason,
480                                       excp.asUserString(),
481                                       devices,
482                                       devindex);
483
484           MIL << "ProvideDir exception caught, callback answer: " << user << endl;
485
486           if (user == media::MediaChangeReport::ABORT)
487           {
488             DBG << "Aborting" << endl;
489             ZYPP_RETHROW ( excp );
490           }
491           else if (user == media::MediaChangeReport::EJECT)
492           {
493             DBG << "Eject: try to release" << endl;
494             media_mgr.releaseAll();
495             // eject
496             media_mgr.release (_media,
497               devindex < devices.size() ? devices[devindex] : "");
498           }
499           else if (user == media::MediaChangeReport::RETRY ||
500               user == media::MediaChangeReport::CHANGE_URL)
501           {
502             // retry
503             DBG << "Going to try again" << endl;
504             // invalidate current media access id
505             media_mgr.close(_media);
506             _medias.erase(media_nr);
507
508             // not attaching, media set will do that for us
509             // this could generate uncaught exception (#158620)
510
511             break;
512           }
513           else
514           {
515             DBG << "Don't know, let's ABORT" << endl;
516
517             ZYPP_RETHROW (excp);
518           }
519         }
520         while (user == media::MediaChangeReport::EJECT);
521       }
522       // retry or change URL
523     }
524     while (true);
525
526     return media_mgr.localPath(_media, dir);
527   }
528
529   media::MediaAccessId MediaSetAccess::getMediaAccessId (media::MediaNr medianr)
530   {
531     media::MediaManager media_mgr;
532
533     if (_medias.find(medianr) != _medias.end())
534     {
535       media::MediaAccessId id = _medias[medianr];
536       //if (! noattach && ! media_mgr.isAttached(id))
537       //media_mgr.attach(id);
538       return id;
539     }
540     Url url;
541     url = rewriteUrl (_url, medianr);
542     media::MediaAccessId id = media_mgr.open(url, _prefAttachPoint);
543     _medias[medianr] = id;
544
545     try
546     {
547       if (_verifiers.find(medianr) != _verifiers.end())
548       {
549         // a verifier is set for this media
550         // FIXME check the case where the verifier exists
551         // but we have no access id for the media
552         media::MediaAccessId id = _medias[medianr];
553         media::MediaManager media_mgr;
554         media_mgr.delVerifier(id);
555         media_mgr.addVerifier( id, _verifiers[medianr] );
556         // remove any saved verifier for this media
557         _verifiers.erase(medianr);
558       }
559     }
560     catch ( const Exception &e )
561     {
562       ZYPP_CAUGHT(e);
563       WAR << "Verifier not found" << endl;
564     }
565
566     return id;
567   }
568
569
570   Url MediaSetAccess::rewriteUrl (const Url & url_r, const media::MediaNr medianr)
571   {
572     std::string scheme = url_r.getScheme();
573     if (scheme == "cd" || scheme == "dvd")
574       return url_r;
575
576     DBG << "Rewriting url " << url_r << endl;
577
578     if( scheme == "iso")
579     {
580       // TODO the iso parameter will not be required in the future, this
581       // code has to be adapted together with the MediaISO change.
582       // maybe some MediaISOURL interface should be used.
583       std::string isofile = url_r.getQueryParam("iso");
584       str::regex e("^(.*)(cd|dvd)[0-9]+\\.iso$", str::regex::icase);
585
586       str::smatch what;
587       if(str::regex_match(isofile, what, e))
588       {
589         Url url( url_r);
590         isofile = what[1] + what[2] + str::numstring(medianr) + ".iso";
591         url.setQueryParam("iso", isofile);
592         DBG << "Url rewrite result: " << url << endl;
593         return url;
594       }
595     }
596     else
597     {
598       std::string pathname = url_r.getPathName();
599       str::regex e("^(.*)(cd|dvd)[0-9]+(/)?$", str::regex::icase);
600       str::smatch what;
601       if(str::regex_match(pathname, what, e))
602       {
603         Url url( url_r);
604         pathname = what[1] + what[2] + str::numstring(medianr) + what[3];
605         url.setPathName(pathname);
606         DBG << "Url rewrite result: " << url << endl;
607         return url;
608       }
609     }
610     return url_r;
611   }
612
613   void MediaSetAccess::release()
614   {
615     DBG << "Releasing all media IDs held by this MediaSetAccess" << endl;
616     media::MediaManager manager;
617     for (MediaMap::const_iterator m = _medias.begin(); m != _medias.end(); ++m)
618       manager.release(m->second, "");
619   }
620
621   std::ostream & MediaSetAccess::dumpOn( std::ostream & str ) const
622   {
623     str << "MediaSetAccess (URL='" << _url << "', attach_point_hint='" << _prefAttachPoint << "')";
624     return str;
625   }
626
627 //     media::MediaVerifierRef MediaSetAccess::verifier(unsigned media_nr)
628 //     { return media::MediaVerifierRef(new media::NoVerifier()); }
629
630 /////////////////////////////////////////////////////////////////
631 } // namespace zypp
632 ///////////////////////////////////////////////////////////////////