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