MediaCIFS.cc

Go to the documentation of this file.
00001 /*---------------------------------------------------------------------\
00002 |                          ____ _   __ __ ___                          |
00003 |                         |__  / \ / / . \ . \                         |
00004 |                           / / \ V /|  _/  _/                         |
00005 |                          / /__ | | | | | |                           |
00006 |                         /_____||_| |_| |_|                           |
00007 |                                                                      |
00008 \---------------------------------------------------------------------*/
00013 #include <iostream>
00014 #include <fstream>
00015 
00016 #include "zypp/base/Logger.h"
00017 #include "zypp/base/Gettext.h"
00018 #include "zypp/TmpPath.h"
00019 #include "zypp/KVMap.h"
00020 #include "zypp/media/Mount.h"
00021 #include "zypp/media/MediaUserAuth.h"
00022 #include "zypp/media/CredentialManager.h"
00023 #include "zypp/ZYppCallbacks.h"
00024 
00025 #warning FIXME: get rid of this dependency on Target
00026 #include "zypp/ZYppFactory.h" // for target->root()
00027 #include "zypp/Target.h" // for zypp->target->root()
00028 
00029 #include "zypp/media/MediaCIFS.h"
00030 
00031 #include <sys/types.h>
00032 #include <sys/mount.h>
00033 #include <errno.h>
00034 #include <dirent.h>
00035 
00036 using namespace std;
00037 
00038 namespace zypp {
00039   namespace media {
00040 
00041     /******************************************************************
00042     **
00043     **
00044     **  FUNCTION NAME : getShare
00045     **  FUNCTION TYPE : inline Pathname
00046     **
00047     ** Get the 1st path component (CIFS share name).
00048     */
00049     inline string getShare( Pathname spath_r )
00050     {
00051       if ( spath_r.empty() )
00052         return string();
00053 
00054       string share( spath_r.absolutename().asString() );
00055       string::size_type sep = share.find( "/", 1 );
00056       if ( sep == string::npos )
00057         share = share.erase( 0, 1 ); // nothing but the share name in spath_r
00058       else
00059         share = share.substr( 1, sep-1 );
00060 
00061       // deescape %2f in sharename
00062       while ( (sep = share.find( "%2f" )) != string::npos ) {
00063         share.replace( sep, 3, "/" );
00064       }
00065 
00066       return share;
00067     }
00068 
00069     /******************************************************************
00070     **
00071     **
00072     **  FUNCTION NAME : stripShare
00073     **  FUNCTION TYPE : inline Pathname
00074     **
00075     ** Strip off the 1st path component (CIFS share name).
00076     */
00077     inline Pathname stripShare( Pathname spath_r )
00078     {
00079       if ( spath_r.empty() )
00080         return Pathname();
00081 
00082       string striped( spath_r.absolutename().asString() );
00083       string::size_type sep = striped.find( "/", 1 );
00084       if ( sep == string::npos )
00085         return "/"; // nothing but the share name in spath_r
00086 
00087       return striped.substr( sep );
00088     }
00089 
00091     //
00092     //  CLASS NAME : MediaCIFS
00093     //
00095 
00097     //
00098     //
00099     //  METHOD NAME : MediaCIFS::MediaCIFS
00100     //  METHOD TYPE : Constructor
00101     //
00102     //  DESCRIPTION :
00103     //
00104     MediaCIFS::MediaCIFS( const Url &      url_r,
00105                         const Pathname & attach_point_hint_r )
00106         : MediaHandler( url_r, attach_point_hint_r,
00107                     stripShare( url_r.getPathName() ), // urlpath WITHOUT share name at attachpoint
00108                     false )       // does_download
00109     {
00110         MIL << "MediaCIFS::MediaCIFS(" << url_r << ", " << attach_point_hint_r << ")" << endl;
00111     }
00112 
00114     //
00115     //
00116     //  METHOD NAME : MediaCIFS::attachTo
00117     //  METHOD TYPE : PMError
00132     void MediaCIFS::attachTo(bool next)
00133     {
00134       if(_url.getHost().empty())
00135         ZYPP_THROW(MediaBadUrlEmptyHostException(_url));
00136       if(next)
00137         ZYPP_THROW(MediaNotSupportedException(_url));
00138 
00139       string path = "//";
00140       path += _url.getHost() + "/" + getShare( _url.getPathName() );
00141 
00142       MediaSourceRef media( new MediaSource( "cifs", path));
00143       AttachedMedia  ret( findAttachedMedia( media));
00144 
00145       if( ret.mediaSource &&
00146           ret.attachPoint &&
00147           !ret.attachPoint->empty())
00148       {
00149         DBG << "Using a shared media "
00150             << ret.mediaSource->name
00151             << " attached on "
00152             << ret.attachPoint->path
00153             << endl;
00154 
00155         removeAttachPoint();
00156         setAttachPoint(ret.attachPoint);
00157         setMediaSource(ret.mediaSource);
00158         return;
00159       }
00160 
00161       std::string mountpoint = attachPoint().asString();
00162       if( !isUseableAttachPoint(attachPoint()))
00163       {
00164         mountpoint = createAttachPoint().asString();
00165         if( mountpoint.empty())
00166           ZYPP_THROW( MediaBadAttachPointException(url()));
00167         setAttachPoint( mountpoint, true);
00168       }
00169 
00170       Mount mount;
00171       CredentialManager cm;
00172 
00173       Mount::Options options( _url.getQueryParam("mountoptions") );
00174       string username = _url.getUsername();
00175       string password = _url.getPassword();
00176 
00177       if ( ! options.has( "rw" ) ) {
00178         options["ro"];
00179       }
00180 
00181       // look for a workgroup
00182       string workgroup = _url.getQueryParam("workgroup");
00183       if ( workgroup.empty() )
00184         workgroup = _url.getQueryParam("domain");
00185       if ( !workgroup.empty() )
00186         options["domain"] = workgroup;
00187 
00188       // extract 'username', do not overwrite any _url.username
00189 
00190       Mount::Options::iterator toEnv;
00191       toEnv = options.find("username");
00192       if ( toEnv != options.end() ) {
00193         if ( username.empty() )
00194           username = toEnv->second;
00195         options.erase( toEnv );
00196       }
00197 
00198       toEnv = options.find("user"); // actually cifs specific
00199       if ( toEnv != options.end() ) {
00200         if ( username.empty() )
00201           username = toEnv->second;
00202         options.erase( toEnv );
00203       }
00204 
00205       // extract 'password', do not overwrite any _url.password
00206 
00207       toEnv = options.find("password");
00208       if ( toEnv != options.end() ) {
00209         if ( password.empty() )
00210           password = toEnv->second;
00211         options.erase( toEnv );
00212       }
00213 
00214       toEnv = options.find("pass"); // actually cifs specific
00215       if ( toEnv != options.end() ) {
00216         if ( password.empty() )
00217           password = toEnv->second;
00218         options.erase( toEnv );
00219       }
00220 
00221       if ( username.empty() || password.empty() )
00222       {
00223         AuthData_Ptr c = cm.getCred(_url);
00224         if (c)
00225         {
00226           username = c->username();
00227           password = c->password();
00228         }
00229       }
00230 
00231       bool firstTry = true;
00232       bool authRequired = false;
00233       AuthData authdata;
00234       do // repeat this while the mount returns "Permission denied" error
00235       {
00236         // get credentials from authenicate()
00237         if ( !firstTry )
00238         {
00239           username = authdata.username();
00240           password = authdata.password();
00241         }
00242 
00243         // pass 'username' and 'password' via environment
00244         Mount::Environment environment;
00245         if ( !username.empty() )
00246           environment["USER"] = username;
00247         if ( !password.empty() )
00248           environment["PASSWD"] = password;
00249 
00251         // In case we need a tmpfile, credentials will remove
00252         // it in it's destructor after the mout call below.
00253         filesystem::TmpPath credentials;
00254         if ( !username.empty() || !password.empty() )
00255         {
00256           filesystem::TmpFile tmp;
00257           ofstream outs( tmp.path().asString().c_str() );
00258           outs << "username=" <<  username << endl;
00259           outs << "password=" <<  password << endl;
00260           outs.close();
00261 
00262           credentials = tmp;
00263           options["credentials"] = credentials.path().asString();
00264         }
00265         else
00266         {
00267           // Use 'guest' option unless explicitly disabled (bnc #547354)
00268           if ( options.has( "noguest" ) )
00269             options.erase( "noguest" );
00270           else
00271             // prevent smbmount from asking for password
00272             // only add this option if 'credentials' is not used (bnc #560496)
00273             options["guest"];
00274         }
00275 
00276         //
00278 
00279         try
00280         {
00281           mount.mount( path, mountpoint, "cifs",
00282                        options.asString(), environment );
00283           setMediaSource(media);
00284           break;
00285         }
00286         catch (const MediaMountException & e)
00287         {
00288           ZYPP_CAUGHT( e );
00289 
00290           if ( e.mountError() == "Permission denied" )
00291             authRequired = authenticate( authdata, firstTry );
00292           else
00293             ZYPP_RETHROW( e );
00294         }
00295 
00296         firstTry = false;
00297       }
00298       while ( authRequired );
00299 
00300       // wait for /etc/mtab update ...
00301       // (shouldn't be needed)
00302       int limit = 3;
00303       bool mountsucceeded;
00304       while( !(mountsucceeded=isAttached()) && --limit)
00305         sleep(1);
00306 
00307       if ( !mountsucceeded )
00308       {
00309         setMediaSource(MediaSourceRef());
00310         try
00311         {
00312           mount.umount(attachPoint().asString());
00313         }
00314         catch (const MediaException & excpt_r)
00315         {
00316           ZYPP_CAUGHT(excpt_r);
00317         }
00318         ZYPP_THROW(MediaMountException(
00319           "Unable to verify that the media was mounted",
00320           path, mountpoint
00321         ));
00322       }
00323     }
00324 
00326     //
00327     //  METHOD NAME : MediaCIFS::isAttached
00328     //  METHOD TYPE : bool
00329     //
00330     //  DESCRIPTION : Override check if media is attached.
00331     //
00332     bool
00333     MediaCIFS::isAttached() const
00334     {
00335       return checkAttached(true);
00336     }
00337 
00339     //
00340     //
00341     //  METHOD NAME : MediaCIFS::releaseFrom
00342     //  METHOD TYPE : PMError
00343     //
00344     //  DESCRIPTION : Asserted that media is attached.
00345     //
00346     void MediaCIFS::releaseFrom( const std::string & ejectDev )
00347     {
00348       Mount mount;
00349       mount.umount(attachPoint().asString());
00350     }
00351 
00353     //
00354     //  METHOD NAME : MediaCIFS::getFile
00355     //  METHOD TYPE : PMError
00356     //
00357     //  DESCRIPTION : Asserted that media is attached.
00358     //
00359     void MediaCIFS::getFile (const Pathname & filename) const
00360     {
00361       MediaHandler::getFile( filename );
00362     }
00363 
00365     //
00366     //  METHOD NAME : MediaCIFS::getDir
00367     //  METHOD TYPE : PMError
00368     //
00369     //  DESCRIPTION : Asserted that media is attached.
00370     //
00371     void MediaCIFS::getDir( const Pathname & dirname, bool recurse_r ) const
00372     {
00373       MediaHandler::getDir( dirname, recurse_r );
00374     }
00375 
00377     //
00378     //
00379     //  METHOD NAME : MediaCIFS::getDirInfo
00380     //  METHOD TYPE : PMError
00381     //
00382     //  DESCRIPTION : Asserted that media is attached and retlist is empty.
00383     //
00384     void MediaCIFS::getDirInfo( std::list<std::string> & retlist,
00385                                const Pathname & dirname, bool dots ) const
00386     {
00387       MediaHandler::getDirInfo( retlist, dirname, dots );
00388     }
00389 
00391     //
00392     //
00393     //  METHOD NAME : MediaCIFS::getDirInfo
00394     //  METHOD TYPE : PMError
00395     //
00396     //  DESCRIPTION : Asserted that media is attached and retlist is empty.
00397     //
00398     void MediaCIFS::getDirInfo( filesystem::DirContent & retlist,
00399                                const Pathname & dirname, bool dots ) const
00400     {
00401       MediaHandler::getDirInfo( retlist, dirname, dots );
00402     }
00403 
00404     bool MediaCIFS::getDoesFileExist( const Pathname & filename ) const
00405     {
00406       return MediaHandler::getDoesFileExist( filename );
00407     }
00408 
00409     bool MediaCIFS::authenticate(AuthData & authdata, bool firstTry) const
00410     {
00412       Target_Ptr target = zypp::getZYpp()->getTarget();
00413       CredentialManager cm(CredManagerOptions(target ? target->root() : ""));
00414 
00415       // get stored credentials
00416       AuthData_Ptr cmcred = cm.getCred(_url);
00417 
00418       AuthData_Ptr smbcred;
00419       smbcred.reset(new AuthData());
00420       callback::SendReport<AuthenticationReport> auth_report;
00421 
00422       // preset the username if present in current url
00423       if (!_url.getUsername().empty() && firstTry)
00424         smbcred->setUsername(_url.getUsername());
00425       // if CM has found some credentials, preset the username from there
00426       else if (cmcred)
00427         smbcred->setUsername(cmcred->username());
00428 
00429       // indicate we have no good credentials from CM
00430       cmcred.reset();
00431 
00432       string prompt_msg = str::form(
00434         _("Authentication required for '%s'"), _url.asString().c_str());
00435 
00436       // ask user
00437       if (auth_report->prompt(_url, prompt_msg, *smbcred))
00438       {
00439         DBG << "callback answer: retry" << endl
00440             << "AuthData: " << *smbcred << endl;
00441 
00442         if (smbcred->valid())
00443         {
00444           cmcred = smbcred;
00445             // if (credentials->username() != _url.getUsername())
00446             //   _url.setUsername(credentials->username());
00454         }
00455       }
00456       else
00457         DBG << "callback answer: cancel" << endl;
00458 
00459       // set username and password
00460       if (cmcred)
00461       {
00462         authdata.setUsername(cmcred->username());
00463         authdata.setPassword(cmcred->password());
00464 
00465         // save the credentials
00466         cmcred->setUrl(_url);
00467         cm.addCred(*cmcred);
00468         cm.save();
00469 
00470         return true;
00471       }
00472 
00473       return false;
00474     }
00475 
00476 
00477   } // namespace media
00478 } // namespace zypp

doxygen