forgot to add those
[platform/upstream/libzypp.git] / zypp / media / MediaAria2c.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/media/MediaAria2c.cc
10  *
11 */
12
13 #include <iostream>
14 #include <list>
15
16 #include "zypp/base/Logger.h"
17 #include "zypp/ExternalProgram.h"
18 #include "zypp/ProgressData.h"
19 #include "zypp/base/String.h"
20 #include "zypp/base/Gettext.h"
21 #include "zypp/base/Sysconfig.h"
22 #include "zypp/base/Gettext.h"
23 #include "zypp/ZYppCallbacks.h"
24
25 #include "zypp/media/MediaAria2c.h"
26 #include "zypp/media/proxyinfo/ProxyInfos.h"
27 #include "zypp/media/ProxyInfo.h"
28 #include "zypp/media/MediaUserAuth.h"
29 //#include "zypp/media/CurlConfig.h"
30 #include "zypp/thread/Once.h"
31 #include <cstdlib>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <sys/mount.h>
35 #include <errno.h>
36 #include <dirent.h>
37 #include <unistd.h>
38 #include <boost/format.hpp>
39
40 #define  DETECT_DIR_INDEX       0
41 #define  CONNECT_TIMEOUT        60
42 #define  TRANSFER_TIMEOUT       60 * 3
43 #define  TRANSFER_TIMEOUT_MAX   60 * 60
44
45
46 using namespace std;
47 using namespace zypp::base;
48
49 namespace
50 {
51   //zypp::thread::OnceFlag g_InitOnceFlag = PTHREAD_ONCE_INIT;
52   //zypp::thread::OnceFlag g_FreeOnceFlag = PTHREAD_ONCE_INIT;
53         /*
54   extern "C" void _do_aria2c_free_once()
55   {
56     curl_global_cleanup();
57   }
58
59   extern "C" void globalAria2cFreeOnce()
60   {
61     zypp::thread::callOnce(g_FreeOnceFlag, _do_aria2c_free_once);
62   }
63
64   extern "C" void _do_aria2c_init_once()
65   {
66     CURLcode ret = curl_global_init( CURL_GLOBAL_ALL );
67     if ( ret != 0 )
68     {
69       WAR << "curl global init failed" << endl;
70     }
71
72     //
73     // register at exit handler ?
74     // this may cause trouble, because we can protect it
75     // against ourself only.
76     // if the app sets an atexit handler as well, it will
77     // cause a double free while the second of them runs.
78     //
79     //std::atexit( globalFreeOnce);
80   }
81
82
83   inline void globalInitOnce()
84   {
85     zypp::thread::callOnce(g_InitOnceFlag, _do_aria2c_init_once);
86   }
87 */
88 /*
89   int log_curl(CURL *curl, curl_infotype info,
90                char *ptr, size_t len, void *max_lvl)
91   {
92     std::string pfx(" ");
93     long        lvl = 0;
94     switch( info)
95     {
96       case CURLINFO_TEXT:       lvl = 1; pfx = "*"; break;
97       case CURLINFO_HEADER_IN:  lvl = 2; pfx = "<"; break;
98       case CURLINFO_HEADER_OUT: lvl = 2; pfx = ">"; break;
99       default:                                      break;
100     }
101     if( lvl > 0 && max_lvl != NULL && lvl <= *((long *)max_lvl))
102     {
103       std::string                            msg(ptr, len);
104       std::list<std::string>                 lines;
105       std::list<std::string>::const_iterator line;
106       zypp::str::split(msg, std::back_inserter(lines), "\r\n");
107       for(line = lines.begin(); line != lines.end(); ++line)
108       {
109         DBG << pfx << " " << *line << endl;
110       }
111     }
112     return 0;
113   }
114         */
115 }
116
117 namespace zypp {
118   namespace media {
119
120   namespace {
121         /*
122     struct ProgressData
123     {
124       ProgressData(const long _timeout, const zypp::Url &_url = zypp::Url(),
125                    callback::SendReport<DownloadProgressReport> *_report=NULL)
126         : timeout(_timeout)
127         , reached(false)
128         , report(_report)
129         , drate_period(-1)
130         , dload_period(0)
131         , secs(0)
132         , drate_avg(-1)
133         , ltime( time(NULL))
134         , dload( 0)
135         , uload( 0)
136         , url(_url)
137       {}
138       long                                          timeout;
139       bool                                          reached;
140       callback::SendReport<DownloadProgressReport> *report;
141       // download rate of the last period (cca 1 sec)
142       double                                        drate_period;
143       // bytes downloaded at the start of the last period
144       double                                        dload_period;
145       // seconds from the start of the download
146       long                                          secs;
147       // average download rate
148       double                                        drate_avg;
149       // last time the progress was reported
150       time_t                                        ltime;
151       // bytes downloaded at the moment the progress was last reported
152       double                                        dload;
153       // bytes uploaded at the moment the progress was last reported
154       double                                        uload;
155       zypp::Url                                     url;
156     };
157         */
158
159     ///////////////////////////////////////////////////////////////////
160
161     inline void escape( string & str_r,
162                         const char char_r, const string & escaped_r ) {
163       for ( string::size_type pos = str_r.find( char_r );
164             pos != string::npos; pos = str_r.find( char_r, pos ) ) {
165               str_r.replace( pos, 1, escaped_r );
166             }
167     }
168
169     inline string escapedPath( string path_r ) {
170       escape( path_r, ' ', "%20" );
171       return path_r;
172     }
173
174     inline string unEscape( string text_r ) {
175       char * tmp = curl_unescape( text_r.c_str(), 0 );
176       string ret( tmp );
177       curl_free( tmp );
178       return ret;
179     }
180
181   }
182
183 ///////////////////////////////////////////////////////////////////
184 //
185 //        CLASS NAME : MediaAria2c
186 //
187 ///////////////////////////////////////////////////////////////////
188
189 Pathname MediaAria2c::_cookieFile = "/var/lib/YaST2/cookies";
190 Pathname MediaAria2c::_aria2cPath = "/usr/local/bin/aria2c";
191 std::string MediaAria2c::_aria2cVersion = "WE DON'T KNOW ARIA2C VERSION";
192
193 //check if aria2c is present in the system
194 bool
195 MediaAria2c::existsAria2cmd()
196 {
197     const char* argv[] =
198     {
199       "whereis",
200       "-b",
201       "aria2c",
202       NULL
203     };
204
205     ExternalProgram aria(argv, ExternalProgram::Stderr_To_Stdout);
206            
207     std::string ariaResponse( aria.receiveLine());
208     string::size_type pos = ariaResponse.find('/', 0 );
209     if( pos != string::npos )
210         return true;
211     else
212         return false;
213 }
214
215 const char *const MediaAria2c::agentString()
216 {
217         static const std::string _value( str::form( "ZYpp %s (with %s)", VERSION, MediaAria2c::_aria2cVersion.c_str() ));
218         return _value.c_str();
219
220 }
221
222 MediaAria2c::MediaAria2c( const Url &      url_r,
223                       const Pathname & attach_point_hint_r )
224     : MediaHandler( url_r, attach_point_hint_r,
225                     "/", // urlpath at attachpoint
226                     true ) // does_download
227       //_curl( NULL )
228 {
229         /*
230   _curlError[0] = '\0';
231   _curlDebug = 0L;
232         */
233
234   MIL << "MediaAria2c::MediaAria2c(" << url_r << ", " << attach_point_hint_r << ")" << endl;
235       
236   //globalInitOnce();
237
238   if( !attachPoint().empty())
239   {
240     PathInfo ainfo(attachPoint());
241     Pathname apath(attachPoint() + "XXXXXX");
242     char    *atemp = ::strdup( apath.asString().c_str());
243     char    *atest = NULL;
244     if( !ainfo.isDir() || !ainfo.userMayRWX() ||
245          atemp == NULL || (atest=::mkdtemp(atemp)) == NULL)
246     {
247       WAR << "attach point " << ainfo.path()
248           << " is not useable for " << url_r.getScheme() << endl;
249       setAttachPoint("", true);
250     }
251     else if( atest != NULL)
252       ::rmdir(atest);
253
254     if( atemp != NULL)
255       ::free(atemp);
256   }
257
258    //At this point, we initialize aria2c path
259    _aria2cPath = Pathname( whereisAria2c().asString() );
260
261    //Get aria2c version
262    _aria2cVersion = getAria2cVersion();
263 }
264
265 /*
266 void MediaAria2c::setCookieFile( const Pathname &fileName )
267 {
268   _cookieFile = fileName;
269 }*/
270
271
272 ///////////////////////////////////////////////////////////////////
273 //
274 //
275 //        METHOD NAME : MediaAria2c::attachTo
276 //        METHOD TYPE : PMError
277 //
278 //        DESCRIPTION : Asserted that not already attached, and attachPoint is a directory.
279 //
280 void MediaAria2c::attachTo (bool next)
281 {
282
283   if ( next )
284     ZYPP_THROW(MediaNotSupportedException(_url));
285
286   if ( !_url.isValid() )
287     ZYPP_THROW(MediaBadUrlException(_url));
288
289 /*
290   CurlConfig curlconf;
291   CurlConfig::parseConfig(curlconf); // parse ~/.curlrc
292
293   curl_version_info_data *curl_info = NULL;
294   curl_info = curl_version_info(CURLVERSION_NOW);
295   // curl_info does not need any free (is static)
296   if (curl_info->protocols)
297   {
298     const char * const *proto;
299     std::string        scheme( _url.getScheme());
300     bool               found = false;
301     for(proto=curl_info->protocols; !found && *proto; ++proto)
302     {
303       if( scheme == std::string((const char *)*proto))
304         found = true;
305     }
306     if( !found)
307     {
308       std::string msg("Unsupported protocol '");
309       msg += scheme;
310       msg += "'";
311       ZYPP_THROW(MediaBadUrlException(_url, msg));
312     }
313   }*/
314
315   if( !isUseableAttachPoint(attachPoint()))
316   {
317     std::string mountpoint = createAttachPoint().asString();
318
319     if( mountpoint.empty())
320       ZYPP_THROW( MediaBadAttachPointException(url()));
321
322     setAttachPoint( mountpoint, true);
323   }
324
325   disconnectFrom(); // clean _curl if needed
326 /*
327   _curl = curl_easy_init();
328   if ( !_curl ) {
329     ZYPP_THROW(MediaAria2cInitException(_url));
330   }
331
332   {
333     char *ptr = getenv("ZYPP_MEDIA_CURL_DEBUG");
334     _curlDebug = (ptr && *ptr) ? str::strtonum<long>( ptr) : 0L;
335     if( _curlDebug > 0)
336     {
337       curl_easy_setopt( _curl, CURLOPT_VERBOSE, 1);
338       curl_easy_setopt( _curl, CURLOPT_DEBUGFUNCTION, log_curl);
339       curl_easy_setopt( _curl, CURLOPT_DEBUGDATA, &_curlDebug);
340     }
341   }
342
343   CURLcode ret = curl_easy_setopt( _curl, CURLOPT_ERRORBUFFER, _curlError );
344   if ( ret != 0 ) {
345     disconnectFrom();
346     ZYPP_THROW(MediaAria2cSetOptException(_url, "Error setting error buffer"));
347   }
348
349   ret = curl_easy_setopt( _curl, CURLOPT_FAILONERROR, true );
350   if ( ret != 0 ) {
351     disconnectFrom();
352     ZYPP_THROW(MediaAria2cSetOptException(_url, _curlError));
353   }
354
355   ret = curl_easy_setopt( _curl, CURLOPT_NOSIGNAL, 1 );
356   if ( ret != 0 ) {
357     disconnectFrom();
358     ZYPP_THROW(MediaAria2cSetOptException(_url, _curlError));
359   }
360
361  
362    * Transfer timeout
363   
364   {
365     _xfer_timeout = TRANSFER_TIMEOUT;
366
367     std::string param(_url.getQueryParam("timeout"));
368     if( !param.empty())
369     {
370       long num = str::strtonum<long>( param);
371       if( num >= 0 && num <= TRANSFER_TIMEOUT_MAX)
372         _xfer_timeout = num;
373     }
374   }
375
376  
377   ** Connect timeout
378  
379   ret = curl_easy_setopt( _curl, CURLOPT_CONNECTTIMEOUT, CONNECT_TIMEOUT);
380   if ( ret != 0 ) {
381     disconnectFrom();
382     ZYPP_THROW(MediaAria2cSetOptException(_url, _curlError));
383   }
384
385   if ( _url.getScheme() == "http" ) {
386     // follow any Location: header that the server sends as part of
387     // an HTTP header (#113275)
388     ret = curl_easy_setopt ( _curl, CURLOPT_FOLLOWLOCATION, true );
389     if ( ret != 0) {
390       disconnectFrom();
391       ZYPP_THROW(MediaAria2cSetOptException(_url, _curlError));
392     }
393     ret = curl_easy_setopt ( _curl, CURLOPT_MAXREDIRS, 3L );
394     if ( ret != 0) {
395       disconnectFrom();
396       ZYPP_THROW(MediaAria2cSetOptException(_url, _curlError));
397     }
398
399     ret = curl_easy_setopt ( _curl, CURLOPT_USERAGENT, agentString() );
400
401
402     if ( ret != 0) {
403       disconnectFrom();
404       ZYPP_THROW(MediaAria2cSetOptException(_url, _curlError));
405     }
406   }
407
408   if ( _url.getScheme() == "https" )
409   {
410     bool verify_peer = false;
411     bool verify_host = false;
412
413     std::string verify( _url.getQueryParam("ssl_verify"));
414     if( verify.empty() ||
415         verify == "yes")
416     {
417       verify_peer = true;
418       verify_host = true;
419     }
420     else
421     if( verify == "no")
422     {
423       verify_peer = false;
424       verify_host = false;
425     }
426     else
427     {
428       std::vector<std::string>                 flags;
429       std::vector<std::string>::const_iterator flag;
430       str::split( verify, std::back_inserter(flags), ",");
431       for(flag = flags.begin(); flag != flags.end(); ++flag)
432       {
433         if( *flag == "host")
434         {
435           verify_host = true;
436         }
437         else
438         if( *flag == "peer")
439         {
440           verify_peer = true;
441         }
442         else
443         {
444                 disconnectFrom();
445           ZYPP_THROW(MediaBadUrlException(_url, "Unknown ssl_verify flag"));
446         }
447       }
448     }
449
450     _ca_path = Pathname(_url.getQueryParam("ssl_capath")).asString();
451     if( _ca_path.empty())
452     {
453         _ca_path = "/etc/ssl/certs/";
454     }
455     else
456     if( !PathInfo(_ca_path).isDir() || !Pathname(_ca_path).absolute())
457     {
458         disconnectFrom();
459         ZYPP_THROW(MediaBadUrlException(_url, "Invalid ssl_capath path"));
460     }
461
462     if( verify_peer || verify_host)
463     {
464       ret = curl_easy_setopt( _curl, CURLOPT_CAPATH, _ca_path.c_str());
465       if ( ret != 0 ) {
466         disconnectFrom();
467         ZYPP_THROW(MediaAria2cSetOptException(_url, _curlError));
468       }
469     }
470
471     ret = curl_easy_setopt( _curl, CURLOPT_SSL_VERIFYPEER, verify_peer ? 1L : 0L);
472     if ( ret != 0 ) {
473       disconnectFrom();
474       ZYPP_THROW(MediaAria2cSetOptException(_url, _curlError));
475     }
476     ret = curl_easy_setopt( _curl, CURLOPT_SSL_VERIFYHOST, verify_host ? 2L : 0L);
477     if ( ret != 0 ) {
478       disconnectFrom();
479       ZYPP_THROW(MediaAria2cSetOptException(_url, _curlError));
480     }
481
482     ret = curl_easy_setopt ( _curl, CURLOPT_USERAGENT, agentString() );
483     if ( ret != 0) {
484       disconnectFrom();
485       ZYPP_THROW(MediaAria2cSetOptException(_url, _curlError));
486     }
487   }
488
489
490   ---------------------------------------------------------------*
491    CURLOPT_USERPWD: [user name]:[password]
492
493    Url::username/password -> CURLOPT_USERPWD
494    If not provided, anonymous FTP identification
495    *---------------------------------------------------------------
496
497   if ( _url.getUsername().empty() ) {
498     if ( _url.getScheme() == "ftp" ) {
499       string id = "yast2@";
500       id += VERSION;
501       DBG << "Anonymous FTP identification: '" << id << "'" << endl;
502       _userpwd = "anonymous:" + id;
503     }
504   } else {
505     _userpwd = _url.getUsername();
506     if ( _url.getPassword().size() ) {
507       _userpwd += ":" + _url.getPassword();
508     }
509   }
510
511   if ( _userpwd.size() ) {
512     _userpwd = unEscape( _userpwd );
513     ret = curl_easy_setopt( _curl, CURLOPT_USERPWD, _userpwd.c_str() );
514     if ( ret != 0 ) {
515       disconnectFrom();
516       ZYPP_THROW(MediaAria2cSetOptException(_url, _curlError));
517     }
518
519     // HTTP authentication type
520     if(_url.getScheme() == "http" || _url.getScheme() == "https")
521     {
522       string use_auth = _url.getQueryParam("auth");
523       if( use_auth.empty())
524         use_auth = "digest,basic";
525
526       try
527       {
528         long auth = CurlAuthData::auth_type_str2long(use_auth);
529         if( auth != CURLAUTH_NONE)
530         {
531           DBG << "Enabling HTTP authentication methods: " << use_auth
532               << " (CURLOPT_HTTPAUTH=" << auth << ")" << std::endl;
533
534           ret = curl_easy_setopt( _curl, CURLOPT_HTTPAUTH, auth);
535           if ( ret != 0 ) {
536             disconnectFrom();
537             ZYPP_THROW(MediaAria2cSetOptException(_url, _curlError));
538           }
539         }
540       }
541       catch (MediaException & ex_r)
542       {
543         string auth_hint = getAuthHint();
544
545         DBG << "Rethrowing as MediaUnauthorizedException. auth hint: '"
546             << auth_hint << "'" << endl;
547
548         ZYPP_THROW(MediaUnauthorizedException(
549           _url, ex_r.msg(), _curlError, auth_hint
550         ));
551       }
552     }
553   }
554
555   ---------------------------------------------------------------*
556    CURLOPT_PROXY: host[:port]
557
558    Url::option(proxy and proxyport) -> CURLOPT_PROXY
559    If not provided, /etc/sysconfig/proxy is evaluated
560    *---------------------------------------------------------------*
561
562   _proxy = _url.getQueryParam( "proxy" );
563
564   if ( ! _proxy.empty() ) {
565     string proxyport( _url.getQueryParam( "proxyport" ) );
566     if ( ! proxyport.empty() ) {
567       _proxy += ":" + proxyport;
568     }
569   } else {
570
571     ProxyInfo proxy_info (ProxyInfo::ImplPtr(new ProxyInfoSysconfig("proxy")));
572
573     if ( proxy_info.enabled())
574     {
575       bool useproxy = true;
576
577       std::list<std::string> nope = proxy_info.noProxy();
578       for (ProxyInfo::NoProxyIterator it = proxy_info.noProxyBegin();
579            it != proxy_info.noProxyEnd();
580            it++)
581       {
582         std::string host( str::toLower(_url.getHost()));
583         std::string temp( str::toLower(*it));
584
585         // no proxy if it points to a suffix
586         // preceeded by a '.', that maches
587         // the trailing portion of the host.
588         if( temp.size() > 1 && temp.at(0) == '.')
589         {
590           if(host.size() > temp.size() &&
591              host.compare(host.size() - temp.size(), temp.size(), temp) == 0)
592           {
593             DBG << "NO_PROXY: '" << *it  << "' matches host '"
594                                  << host << "'" << endl;
595             useproxy = false;
596             break;
597           }
598         }
599         else
600         // no proxy if we have an exact match
601         if( host == temp)
602         {
603           DBG << "NO_PROXY: '" << *it  << "' matches host '"
604                                << host << "'" << endl;
605           useproxy = false;
606           break;
607         }
608       }
609
610       if ( useproxy ) {
611         _proxy = proxy_info.proxy(_url.getScheme());
612       }
613     }
614   }
615
616
617   DBG << "Proxy: " << (_proxy.empty() ? "-none-" : _proxy) << endl;
618
619   if ( ! _proxy.empty() ) {
620
621     ret = curl_easy_setopt( _curl, CURLOPT_PROXY, _proxy.c_str() );
622     if ( ret != 0 ) {
623       disconnectFrom();
624       ZYPP_THROW(MediaAria2cSetOptException(_url, _curlError));
625     }
626
627     *---------------------------------------------------------------*
628      CURLOPT_PROXYUSERPWD: [user name]:[password]
629
630      Url::option(proxyuser and proxypassword) -> CURLOPT_PROXYUSERPWD
631      If not provided, $HOME/.curlrc is evaluated
632      *---------------------------------------------------------------*
633
634     _proxyuserpwd = _url.getQueryParam( "proxyuser" );
635
636     if ( ! _proxyuserpwd.empty() ) {
637       string proxypassword( _url.getQueryParam( "proxypassword" ) );
638       if ( ! proxypassword.empty() ) {
639         _proxyuserpwd += ":" + proxypassword;
640       }
641     } else {
642       if (curlconf.proxyuserpwd.empty())
643         DBG << "~/.curlrc does not contain the proxy-user option" << endl;
644       else
645       {
646         _proxyuserpwd = curlconf.proxyuserpwd;
647         DBG << "using proxy-user from ~/.curlrc" << endl;
648       }
649     }
650
651     _proxyuserpwd = unEscape( _proxyuserpwd );
652     ret = curl_easy_setopt( _curl, CURLOPT_PROXYUSERPWD, _proxyuserpwd.c_str() );
653     if ( ret != 0 ) {
654       disconnectFrom();
655       ZYPP_THROW(MediaAria2cSetOptException(_url, _curlError));
656     }
657   }
658
659   *---------------------------------------------------------------*
660    *---------------------------------------------------------------*
661
662   _currentCookieFile = _cookieFile.asString();
663
664   ret = curl_easy_setopt( _curl, CURLOPT_COOKIEFILE,
665                           _currentCookieFile.c_str() );
666   if ( ret != 0 ) {
667     disconnectFrom();
668     ZYPP_THROW(MediaAria2cSetOptException(_url, _curlError));
669   }
670
671   ret = curl_easy_setopt( _curl, CURLOPT_COOKIEJAR,
672                           _currentCookieFile.c_str() );
673   if ( ret != 0 ) {
674     disconnectFrom();
675     ZYPP_THROW(MediaAria2cSetOptException(_url, _curlError));
676   }
677
678   ret = curl_easy_setopt( _curl, CURLOPT_PROGRESSFUNCTION,
679                           &progressCallback );
680   if ( ret != 0 ) {
681     disconnectFrom();
682     ZYPP_THROW(MediaAria2cSetOptException(_url, _curlError));
683   }
684
685   ret = curl_easy_setopt( _curl, CURLOPT_NOPROGRESS, false );
686   if ( ret != 0 ) {
687     disconnectFrom();
688     ZYPP_THROW(MediaAria2cSetOptException(_url, _curlError));
689   }
690 */
691   // FIXME: need a derived class to propelly compare url's
692   MediaSourceRef media( new MediaSource(_url.getScheme(), _url.asString()));
693   setMediaSource(media);
694         
695 }
696
697 bool
698 MediaAria2c::checkAttachPoint(const Pathname &apoint) const
699 {
700   return MediaHandler::checkAttachPoint( apoint, true, true);
701 }
702
703 ///////////////////////////////////////////////////////////////////
704 //
705 //
706 //        METHOD NAME : MediaAria2c::disconnectFrom
707 //        METHOD TYPE : PMError
708 //
709 void MediaAria2c::disconnectFrom()
710 {
711         /*
712   if ( _curl )
713   {
714     curl_easy_cleanup( _curl );
715     _curl = NULL;
716   }*/
717 }
718
719
720 ///////////////////////////////////////////////////////////////////
721 //
722 //
723 //        METHOD NAME : MediaAria2c::releaseFrom
724 //        METHOD TYPE : void
725 //
726 //        DESCRIPTION : Asserted that media is attached.
727 //
728 void MediaAria2c::releaseFrom( const std::string & ejectDev )
729 {
730   disconnect();
731 }
732
733 static Url getFileUrl(const Url & url, const Pathname & filename)
734 {
735   Url newurl(url);
736   string path = url.getPathName();
737   if ( !path.empty() && path != "/" && *path.rbegin() == '/' &&
738        filename.absolute() )
739   {
740     // If url has a path with trailing slash, remove the leading slash from
741     // the absolute file name
742     path += filename.asString().substr( 1, filename.asString().size() - 1 );
743   }
744   else if ( filename.relative() )
745   {
746     // Add trailing slash to path, if not already there
747     if (path.empty()) path = "/";
748     else if (*path.rbegin() != '/' ) path += "/";
749     // Remove "./" from begin of relative file name
750     path += filename.asString().substr( 2, filename.asString().size() - 2 );
751   }
752   else
753   {
754     path += filename.asString();
755   }
756
757   newurl.setPathName(path);
758   return newurl;
759 }
760
761
762 ///////////////////////////////////////////////////////////////////
763 //
764 //        METHOD NAME : MediaAria2c::getFile
765 //        METHOD TYPE : void
766 //
767 void MediaAria2c::getFile( const Pathname & filename ) const
768 {
769     // Use absolute file name to prevent access of files outside of the
770     // hierarchy below the attach point.    
771     getFileCopy(filename, localPath(filename).absolutename());
772 }
773
774 ///////////////////////////////////////////////////////////////////
775 //
776 //        METHOD NAME : MediaAria2c::getFileCopy
777 //        METHOD TYPE : void
778 //
779 void MediaAria2c::getFileCopy( const Pathname & filename , const Pathname & target) const
780 {
781   callback::SendReport<DownloadProgressReport> report;
782
783   Url fileurl(getFileUrl(_url, filename));  
784
785   bool retry = false;
786   CurlAuthData auth_data;
787
788   do
789   {
790     try
791     {   
792         report->start(_url, target.asString() );        
793
794         const char* argv[] =
795         {
796             _aria2cPath.c_str(),
797             str::form("--user-agent=\"%s\""
798                       , agentString()).c_str(),
799             "--summary-interval=1",
800             "--follow-metalink=mem",
801             "--check-integrity=true",
802             str::form("--dir=\"%s\"", target.dirname().c_str()).c_str(),
803             fileurl.asString().c_str(),
804             NULL
805         };
806
807         ExternalProgram aria(argv, ExternalProgram::Stderr_To_Stdout);     
808         int nLine = 0;   
809
810         //Process response
811         for(std::string ariaResponse( aria.receiveLine());
812             ariaResponse.length(); 
813             ariaResponse = aria.receiveLine())
814         { 
815             //cout << ariaResponse;
816             if (!ariaResponse.substr(0,9).compare("[#2 SIZE:")) {
817                 
818                 if (!nLine) 
819                 {
820                     size_t left_bound = ariaResponse.find('(',0) + 1;
821                     size_t count = ariaResponse.find('%',left_bound) - left_bound;
822                     //cout << ariaResponse.substr(left_bound, count) << endl;
823                     //progressData.toMax();
824                     report->progress ( std::atoi(ariaResponse.substr(left_bound, count).c_str()), _url, -1, -1 );
825                     nLine = 1;
826                 } 
827                 else
828                 {
829                     nLine = 0;
830                 }
831                 
832                 
833             } 
834         }
835         aria.close();
836         
837         report->finish( _url ,  zypp::media::DownloadProgressReport::NO_ERROR, "");
838         retry = false;
839     }
840
841
842     // retry with proper authentication data
843     catch (MediaUnauthorizedException & ex_r)
844     {
845       callback::SendReport<AuthenticationReport> auth_report;
846
847         /*
848
849         Here, we can try using this aria2c options - TODO
850
851         --http-user=USER
852            Set HTTP user. This affects all URLs.
853
854        --http-passwd=PASSWD
855            Set HTTP password. This affects all URLs.
856         */
857
858       //if (!_url.getUsername().empty() && !retry)
859       //  auth_data.setUserName(_url.getUsername());
860
861       //string prompt_msg;
862       //if (retry || !_url.getUsername().empty())
863       //  prompt_msg = _("Invalid user name or password.");
864       //else // first prompt
865       //  prompt_msg = boost::str(boost::format(
866       //    _("Authentication required for '%s'")) % _url.asString());
867
868       // set available authentication types from the exception
869       //auth_data.setAuthType(ex_r.hint());
870
871       //if (auth_report->prompt(_url, prompt_msg, auth_data))
872       //{
873         //DBG << "callback answer: retry" << endl
874          //   << "CurlAuthData: " << auth_data << endl;
875
876         //if (auth_data.valid()) {
877           //_userpwd = auth_data.getUserPwd();
878
879           // set username and password
880           //CURLcode ret = curl_easy_setopt(_curl, CURLOPT_USERPWD, _userpwd.c_str());
881           //if ( ret != 0 ) ZYPP_THROW(MediaAria2cSetOptException(_url, _curlError));
882
883           // set auth type
884           //ret = curl_easy_setopt(_curl, CURLOPT_HTTPAUTH, auth_data.authType());
885           //if ( ret != 0 ) ZYPP_THROW(MediaAria2cSetOptException(_url, _curlError));
886         //}
887
888         //retry = true;
889       //}
890       //else
891       //{
892       //  DBG << "callback answer: cancel" << endl;
893       //  report->finish(fileurl, zypp::media::DownloadProgressReport::ACCESS_DENIED, ex_r.asUserString());
894       //  ZYPP_RETHROW(ex_r);
895       //}
896     }
897     // unexpected exception
898     catch (MediaException & excpt_r)
899     {
900       // FIXME: error number fix
901       report->finish(fileurl, zypp::media::DownloadProgressReport::ERROR, excpt_r.asUserString());
902       ZYPP_RETHROW(excpt_r);
903     }
904   }
905   while (retry);
906
907   report->finish(fileurl, zypp::media::DownloadProgressReport::NO_ERROR, "");
908 }
909
910 bool MediaAria2c::getDoesFileExist( const Pathname & filename ) const
911 {
912         return true;
913         /*
914   bool retry = false;
915   CurlAuthData auth_data;
916
917   do
918   {
919     try
920     {
921       return doGetDoesFileExist( filename );
922     }
923     // authentication problem, retry with proper authentication data
924     catch (MediaUnauthorizedException & ex_r)
925     {
926       callback::SendReport<AuthenticationReport> auth_report;
927
928       if (!_url.getUsername().empty() && !retry)
929         auth_data.setUserName(_url.getUsername());
930
931       string prompt_msg;
932       if (retry || !_url.getUsername().empty())
933         prompt_msg = _("Invalid user name or password.");
934       else // first prompt
935         prompt_msg = boost::str(boost::format(
936           _("Authentication required for '%s'")) % _url.asString());
937
938       // set available authentication types from the exception
939       auth_data.setAuthType(ex_r.hint());
940
941       if (auth_report->prompt(_url, prompt_msg, auth_data))
942       {
943         DBG << "callback answer: retry" << endl
944             << "CurlAuthData: " << auth_data << endl;
945
946         if (auth_data.valid()) {
947           _userpwd = auth_data.getUserPwd();
948
949           // set username and password
950           //CURLcode ret = curl_easy_setopt(_curl, CURLOPT_USERPWD, _userpwd.c_str());
951           //if ( ret != 0 ) ZYPP_THROW(MediaAria2cSetOptException(_url, _curlError));
952
953           // set auth type
954           //ret = curl_easy_setopt(_curl, CURLOPT_HTTPAUTH, auth_data.authType());
955           //if ( ret != 0 ) ZYPP_THROW(MediaAria2cSetOptException(_url, _curlError));
956         }
957
958         retry = true;
959       }
960       else
961       {
962         DBG << "callback answer: cancel" << endl;
963         ZYPP_RETHROW(ex_r);
964       }
965     }
966     // unexpected exception
967     catch (MediaException & excpt_r)
968     {
969       ZYPP_RETHROW(excpt_r);
970     }
971   }
972   while (retry);
973
974   return false;
975 */
976 }
977
978 /*
979 bool MediaAria2c::doGetDoesFileExist( const Pathname & filename ) const
980 {
981         
982   DBG << filename.asString() << endl;
983
984   if(!_url.isValid())
985     ZYPP_THROW(MediaBadUrlException(_url));
986
987   if(_url.getHost().empty())
988     ZYPP_THROW(MediaBadUrlEmptyHostException(_url));
989
990   string path = _url.getPathName();
991   if ( !path.empty() && path != "/" && *path.rbegin() == '/' &&
992         filename.absolute() ) {
993       // If url has a path with trailing slash, remove the leading slash from
994       // the absolute file name
995     path += filename.asString().substr( 1, filename.asString().size() - 1 );
996   } else if ( filename.relative() ) {
997       // Add trailing slash to path, if not already there
998     if ( !path.empty() && *path.rbegin() != '/' ) path += "/";
999     // Remove "./" from begin of relative file name
1000     path += filename.asString().substr( 2, filename.asString().size() - 2 );
1001   } else {
1002     path += filename.asString();
1003   }
1004
1005   Url url( _url );
1006   url.setPathName( path );
1007
1008   DBG << "URL: " << url.asString() << endl;
1009     // Use URL without options and without username and passwd
1010     // (some proxies dislike them in the URL).
1011     // Curl seems to need the just scheme, hostname and a path;
1012     // the rest was already passed as curl options (in attachTo).
1013   Url curlUrl( url );
1014
1015     // Use asString + url::ViewOptions instead?
1016   curlUrl.setUsername( "" );
1017   curlUrl.setPassword( "" );
1018   curlUrl.setPathParams( "" );
1019   curlUrl.setQueryString( "" );
1020   curlUrl.setFragment( "" );
1021
1022   //
1023     // See also Bug #154197 and ftp url definition in RFC 1738:
1024     // The url "ftp://user@host/foo/bar/file" contains a path,
1025     // that is relative to the user's home.
1026     // The url "ftp://user@host//foo/bar/file" (or also with
1027     // encoded slash as %2f) "ftp://user@host/%2ffoo/bar/file"
1028     // contains an absolute path.
1029   //
1030   string urlBuffer( curlUrl.asString());
1031   CURLcode ret = curl_easy_setopt( _curl, CURLOPT_URL,
1032                                    urlBuffer.c_str() );
1033   if ( ret != 0 ) {
1034     ZYPP_THROW(MediaAria2cSetOptException(url, _curlError));
1035   }
1036
1037   // instead of returning no data with NOBODY, we return
1038   // little data, that works with broken servers, and
1039   // works for ftp as well, because retrieving only headers
1040   // ftp will return always OK code ?
1041   if (  _url.getScheme() == "http" ||  _url.getScheme() == "https" )
1042     ret = curl_easy_setopt( _curl, CURLOPT_NOBODY, 1 );
1043   else
1044     ret = curl_easy_setopt( _curl, CURLOPT_RANGE, "0-1" );
1045
1046   if ( ret != 0 ) {
1047     curl_easy_setopt( _curl, CURLOPT_NOBODY, NULL );
1048     curl_easy_setopt( _curl, CURLOPT_RANGE, NULL );
1049      yes, this is why we never got to get NOBODY working before,
1050        because setting it changes this option too, and we also
1051        need to reset it
1052        See: http://curl.haxx.se/mail/archive-2005-07/0073.html
1053     
1054     curl_easy_setopt( _curl, CURLOPT_HTTPGET, 1 );
1055     ZYPP_THROW(MediaAria2cSetOptException(url, _curlError));
1056   }
1057
1058
1059   FILE *file = ::fopen( "/dev/null", "w" );
1060   if ( !file ) {
1061       ::fclose(file);
1062       ERR << "fopen failed for /dev/null" << endl;
1063       curl_easy_setopt( _curl, CURLOPT_NOBODY, NULL );
1064       curl_easy_setopt( _curl, CURLOPT_RANGE, NULL );
1065        yes, this is why we never got to get NOBODY working before,
1066        because setting it changes this option too, and we also
1067        need to reset it
1068        See: http://curl.haxx.se/mail/archive-2005-07/0073.html
1069       
1070       curl_easy_setopt( _curl, CURLOPT_HTTPGET, 1 );
1071       if ( ret != 0 ) {
1072           ZYPP_THROW(MediaAria2cSetOptException(url, _curlError));
1073       }
1074       ZYPP_THROW(MediaWriteException("/dev/null"));
1075   }
1076
1077   ret = curl_easy_setopt( _curl, CURLOPT_WRITEDATA, file );
1078   if ( ret != 0 ) {
1079       ::fclose(file);
1080       std::string err( _curlError);
1081       curl_easy_setopt( _curl, CURLOPT_RANGE, NULL );
1082       curl_easy_setopt( _curl, CURLOPT_NOBODY, NULL );
1083        yes, this is why we never got to get NOBODY working before,
1084        because setting it changes this option too, and we also
1085        need to reset it
1086        See: http://curl.haxx.se/mail/archive-2005-07/0073.html
1087      
1088       curl_easy_setopt( _curl, CURLOPT_HTTPGET, 1 );
1089       if ( ret != 0 ) {
1090           ZYPP_THROW(MediaAria2cSetOptException(url, _curlError));
1091       }
1092       ZYPP_THROW(MediaAria2cSetOptException(url, err));
1093   }
1094     // Set callback and perform.
1095   //ProgressData progressData(_xfer_timeout, url, &report);
1096   //report->start(url, dest);
1097   //if ( curl_easy_setopt( _curl, CURLOPT_PROGRESSDATA, &progressData ) != 0 ) {
1098   //  WAR << "Can't set CURLOPT_PROGRESSDATA: " << _curlError << endl;;
1099   //}
1100
1101   CURLcode ok = curl_easy_perform( _curl );
1102   MIL << "perform code: " << ok << " [ " << curl_easy_strerror(ok) << " ]" << endl;
1103
1104   // reset curl settings
1105   if (  _url.getScheme() == "http" ||  _url.getScheme() == "https" )
1106   {
1107     ret = curl_easy_setopt( _curl, CURLOPT_NOBODY, NULL );
1108      yes, this is why we never got to get NOBODY working before,
1109        because setting it changes this option too, and we also
1110        need to reset it
1111        See: http://curl.haxx.se/mail/archive-2005-07/0073.html
1112     
1113     ret = curl_easy_setopt( _curl, CURLOPT_HTTPGET, 1 );
1114   }
1115   else
1116     ret = curl_easy_setopt( _curl, CURLOPT_RANGE, NULL );
1117
1118   if ( ret != 0 )
1119   {
1120     ZYPP_THROW(MediaAria2cSetOptException(url, _curlError));
1121   }
1122
1123   if ( ok != 0 )
1124   {
1125     ::fclose( file );
1126
1127     std::string err;
1128     try
1129     {
1130       bool err_file_not_found = false;
1131       switch ( ok )
1132       {
1133       case CURLE_FTP_COULDNT_RETR_FILE:
1134       case CURLE_FTP_ACCESS_DENIED:
1135         err_file_not_found = true;
1136         break;
1137       case CURLE_HTTP_RETURNED_ERROR:
1138         {
1139           long httpReturnCode = 0;
1140           CURLcode infoRet = curl_easy_getinfo( _curl,
1141                                                 CURLINFO_RESPONSE_CODE,
1142                                                 &httpReturnCode );
1143           if ( infoRet == CURLE_OK )
1144           {
1145             string msg = "HTTP response: " +
1146                           str::numstring( httpReturnCode );
1147             if ( httpReturnCode == 401 )
1148             {
1149               std::string auth_hint = getAuthHint();
1150
1151               DBG << msg << " Login failed (URL: " << url.asString() << ")" << std::endl;
1152               DBG << "MediaUnauthorizedException auth hint: '" << auth_hint << "'" << std::endl;
1153
1154               ZYPP_THROW(MediaUnauthorizedException(
1155                 url, "Login failed.", _curlError, auth_hint
1156               ));
1157             }
1158             else
1159             if ( httpReturnCode == 403)
1160             {
1161                ZYPP_THROW(MediaForbiddenException(url));
1162             }
1163             else
1164             if ( httpReturnCode == 404)
1165             {
1166                err_file_not_found = true;
1167                break;
1168             }
1169
1170             msg += err;
1171             DBG << msg << " (URL: " << url.asString() << ")" << std::endl;
1172             ZYPP_THROW(MediaAria2cException(url, msg, _curlError));
1173           }
1174           else
1175           {
1176             string msg = "Unable to retrieve HTTP response:";
1177             msg += err;
1178             DBG << msg << " (URL: " << url.asString() << ")" << std::endl;
1179             ZYPP_THROW(MediaAria2cException(url, msg, _curlError));
1180           }
1181         }
1182         break;
1183       case CURLE_UNSUPPORTED_PROTOCOL:
1184       case CURLE_URL_MALFORMAT:
1185       case CURLE_URL_MALFORMAT_USER:
1186       case CURLE_BAD_PASSWORD_ENTERED:
1187       case CURLE_FTP_USER_PASSWORD_INCORRECT:
1188         err = "Login failed";
1189         break;
1190       case CURLE_COULDNT_RESOLVE_PROXY:
1191       case CURLE_COULDNT_RESOLVE_HOST:
1192       case CURLE_COULDNT_CONNECT:
1193       case CURLE_FTP_CANT_GET_HOST:
1194         err = "Connection failed";
1195         break;
1196       case CURLE_WRITE_ERROR:
1197         err = "Write error";
1198         break;
1199       case CURLE_ABORTED_BY_CALLBACK:
1200       case CURLE_OPERATION_TIMEOUTED:
1201         err  = "Timeout reached";
1202         ZYPP_THROW(MediaTimeoutException(url));
1203         break;
1204       case CURLE_SSL_CACERT:
1205         ZYPP_THROW(MediaBadCAException(url,_curlError));
1206       case CURLE_SSL_PEER_CERTIFICATE:
1207       default:
1208         err = curl_easy_strerror(ok);
1209         if (err.empty())
1210           err = "Unrecognized error";
1211         break;
1212       }
1213
1214       if( err_file_not_found)
1215       {
1216         // file does not exists
1217         return false;
1218       }
1219       else
1220       {
1221         // there was an error
1222         ZYPP_THROW(MediaAria2cException(url, string(), _curlError));
1223       }
1224     }
1225     catch (const MediaException & excpt_r)
1226     {
1227       ZYPP_RETHROW(excpt_r);
1228     }
1229   }
1230
1231   // exists
1232   return ( ok == CURLE_OK );
1233   //if ( curl_easy_setopt( _curl, CURLOPT_PROGRESSDATA, NULL ) != 0 ) {
1234   //  WAR << "Can't unset CURLOPT_PROGRESSDATA: " << _curlError << endl;;
1235   //}
1236
1237 }
1238 */
1239
1240 /*
1241 void MediaAria2c::doGetFileCopy( const Pathname & filename , const Pathname & target, callback::SendReport<DownloadProgressReport> & report) const
1242 {
1243     DBG << filename.asString() << endl;
1244
1245     if(!_url.isValid())
1246       ZYPP_THROW(MediaBadUrlException(_url));
1247
1248     if(_url.getHost().empty())
1249       ZYPP_THROW(MediaBadUrlEmptyHostException(_url));
1250
1251     Url url(getFileUrl(_url, filename));
1252
1253     Pathname dest = target.absolutename();
1254     if( assert_dir( dest.dirname() ) )
1255     {
1256       DBG << "assert_dir " << dest.dirname() << " failed" << endl;
1257       ZYPP_THROW( MediaSystemException(url, "System error on " + dest.dirname().asString()) );
1258     }
1259
1260     DBG << "URL: " << url.asString() << endl;
1261     // Use URL without options and without username and passwd
1262     // (some proxies dislike them in the URL).
1263     // Curl seems to need the just scheme, hostname and a path;
1264     // the rest was already passed as curl options (in attachTo).
1265     Url curlUrl( url );
1266
1267     // Use asString + url::ViewOptions instead?
1268     curlUrl.setUsername( "" );
1269     curlUrl.setPassword( "" );
1270     curlUrl.setPathParams( "" );
1271     curlUrl.setQueryString( "" );
1272     curlUrl.setFragment( "" );
1273
1274     //
1275     // See also Bug #154197 and ftp url definition in RFC 1738:
1276     // The url "ftp://user@host/foo/bar/file" contains a path,
1277     // that is relative to the user's home.
1278     // The url "ftp://user@host//foo/bar/file" (or also with
1279     // encoded slash as %2f) "ftp://user@host/%2ffoo/bar/file"
1280     // contains an absolute path.
1281     //
1282     string urlBuffer( curlUrl.asString());
1283     CURLcode ret = curl_easy_setopt( _curl, CURLOPT_URL,
1284                                      urlBuffer.c_str() );
1285     if ( ret != 0 ) {
1286       ZYPP_THROW(MediaAria2cSetOptException(url, _curlError));
1287     }
1288
1289     // set IFMODSINCE time condition (no download if not modified)
1290     curl_easy_setopt(_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE);
1291     curl_easy_setopt(_curl, CURLOPT_TIMEVALUE, PathInfo(target).mtime());
1292
1293     string destNew = target.asString() + ".new.zypp.XXXXXX";
1294     char *buf = ::strdup( destNew.c_str());
1295     if( !buf)
1296     {
1297       ERR << "out of memory for temp file name" << endl;
1298       ZYPP_THROW(MediaSystemException(
1299         url, "out of memory for temp file name"
1300       ));
1301     }
1302
1303     int tmp_fd = ::mkstemp( buf );
1304     if( tmp_fd == -1)
1305     {
1306       free( buf);
1307       ERR << "mkstemp failed for file '" << destNew << "'" << endl;
1308       ZYPP_THROW(MediaWriteException(destNew));
1309     }
1310     destNew = buf;
1311     free( buf);
1312
1313     FILE *file = ::fdopen( tmp_fd, "w" );
1314     if ( !file ) {
1315       ::close( tmp_fd);
1316       filesystem::unlink( destNew );
1317       ERR << "fopen failed for file '" << destNew << "'" << endl;
1318       ZYPP_THROW(MediaWriteException(destNew));
1319     }
1320
1321     DBG << "dest: " << dest << endl;
1322     DBG << "temp: " << destNew << endl;
1323
1324     ret = curl_easy_setopt( _curl, CURLOPT_WRITEDATA, file );
1325     if ( ret != 0 ) {
1326       ::fclose( file );
1327       filesystem::unlink( destNew );
1328       ZYPP_THROW(MediaAria2cSetOptException(url, _curlError));
1329     }
1330
1331     // Set callback and perform.
1332     ProgressData progressData(_xfer_timeout, url, &report);
1333     report->start(url, dest);
1334     if ( curl_easy_setopt( _curl, CURLOPT_PROGRESSDATA, &progressData ) != 0 ) {
1335       WAR << "Can't set CURLOPT_PROGRESSDATA: " << _curlError << endl;;
1336     }
1337
1338     ret = curl_easy_perform( _curl );
1339
1340     if ( curl_easy_setopt( _curl, CURLOPT_PROGRESSDATA, NULL ) != 0 ) {
1341       WAR << "Can't unset CURLOPT_PROGRESSDATA: " << _curlError << endl;;
1342     }
1343
1344     if ( ret != 0 ) {
1345       ERR << "curl error: " << ret << ": " << _curlError
1346           << ", temp file size " << PathInfo(destNew).size()
1347           << " byte." << endl;
1348
1349       ::fclose( file );
1350       filesystem::unlink( destNew );
1351
1352       std::string err;
1353       try {
1354        bool err_file_not_found = false;
1355        switch ( ret ) {
1356         case CURLE_UNSUPPORTED_PROTOCOL:
1357         case CURLE_URL_MALFORMAT:
1358         case CURLE_URL_MALFORMAT_USER:
1359           err = " Bad URL";
1360         case CURLE_HTTP_RETURNED_ERROR:
1361           {
1362             long httpReturnCode = 0;
1363             CURLcode infoRet = curl_easy_getinfo( _curl,
1364                                                   CURLINFO_RESPONSE_CODE,
1365                                                   &httpReturnCode );
1366             if ( infoRet == CURLE_OK ) {
1367               string msg = "HTTP response: " +
1368                            str::numstring( httpReturnCode );
1369               if ( httpReturnCode == 401 )
1370               {
1371                 std::string auth_hint = getAuthHint();
1372
1373                 DBG << msg << " Login failed (URL: " << url.asString() << ")" << std::endl;
1374                 DBG << "MediaUnauthorizedException auth hint: '" << auth_hint << "'" << std::endl;
1375
1376                 ZYPP_THROW(MediaUnauthorizedException(
1377                   url, "Login failed.", _curlError, auth_hint
1378                 ));
1379               }
1380               else
1381               if ( httpReturnCode == 403)
1382               {
1383                  ZYPP_THROW(MediaForbiddenException(url));
1384               }
1385               else
1386               if ( httpReturnCode == 404)
1387               {
1388                  ZYPP_THROW(MediaFileNotFoundException(_url, filename));
1389               }
1390
1391               msg += err;
1392               DBG << msg << " (URL: " << url.asString() << ")" << std::endl;
1393               ZYPP_THROW(MediaAria2cException(url, msg, _curlError));
1394             }
1395             else
1396             {
1397               string msg = "Unable to retrieve HTTP response:";
1398               msg += err;
1399               DBG << msg << " (URL: " << url.asString() << ")" << std::endl;
1400               ZYPP_THROW(MediaAria2cException(url, msg, _curlError));
1401             }
1402           }
1403           break;
1404         case CURLE_FTP_COULDNT_RETR_FILE:
1405         case CURLE_FTP_ACCESS_DENIED:
1406           err = "File not found";
1407           err_file_not_found = true;
1408           break;
1409         case CURLE_BAD_PASSWORD_ENTERED:
1410         case CURLE_FTP_USER_PASSWORD_INCORRECT:
1411           err = "Login failed";
1412           break;
1413         case CURLE_COULDNT_RESOLVE_PROXY:
1414         case CURLE_COULDNT_RESOLVE_HOST:
1415         case CURLE_COULDNT_CONNECT:
1416         case CURLE_FTP_CANT_GET_HOST:
1417           err = "Connection failed";
1418           break;
1419         case CURLE_WRITE_ERROR:
1420           err = "Write error";
1421           break;
1422         case CURLE_ABORTED_BY_CALLBACK:
1423         case CURLE_OPERATION_TIMEDOUT:
1424           if( progressData.reached)
1425           {
1426             err  = "Timeout reached";
1427             ZYPP_THROW(MediaTimeoutException(url));
1428           }
1429           else
1430           {
1431             err = "User abort";
1432           }
1433           break;
1434         case CURLE_SSL_PEER_CERTIFICATE:
1435         default:
1436           err = "Unrecognized error";
1437           break;
1438        }
1439        if( err_file_not_found)
1440        {
1441          ZYPP_THROW(MediaFileNotFoundException(_url, filename));
1442        }
1443        else
1444        {
1445          ZYPP_THROW(MediaAria2cException(url, err, _curlError));
1446        }
1447       }
1448       catch (const MediaException & excpt_r)
1449       {
1450         ZYPP_RETHROW(excpt_r);
1451       }
1452     }
1453 #if DETECT_DIR_INDEX
1454     else
1455     if(curlUrl.getScheme() == "http" ||
1456        curlUrl.getScheme() == "https")
1457     {
1458       //
1459       // try to check the effective url and set the not_a_file flag
1460       // if the url path ends with a "/", what usually means, that
1461       // we've received a directory index (index.html content).
1462       //
1463       // Note: This may be dangerous and break file retrieving in
1464       //       case of some server redirections ... ?
1465       //
1466       bool      not_a_file = false;
1467       char     *ptr = NULL;
1468       CURLcode  ret = curl_easy_getinfo( _curl,
1469                                          CURLINFO_EFFECTIVE_URL,
1470                                          &ptr);
1471       if ( ret == CURLE_OK && ptr != NULL)
1472       {
1473         try
1474         {
1475           Url         eurl( ptr);
1476           std::string path( eurl.getPathName());
1477           if( !path.empty() && path != "/" && *path.rbegin() == '/')
1478           {
1479             DBG << "Effective url ("
1480                 << eurl
1481                 << ") seems to provide the index of a directory"
1482                 << endl;
1483             not_a_file = true;
1484           }
1485         }
1486         catch( ... )
1487         {}
1488       }
1489
1490       if( not_a_file)
1491       {
1492         ::fclose( file );
1493         filesystem::unlink( destNew );
1494         ZYPP_THROW(MediaNotAFileException(_url, filename));
1495       }
1496     }
1497 #endif // DETECT_DIR_INDEX
1498
1499     long httpReturnCode = 0;
1500     CURLcode infoRet = curl_easy_getinfo(_curl,
1501                                          CURLINFO_RESPONSE_CODE,
1502                                          &httpReturnCode);
1503     bool modified = true;
1504     if (infoRet == CURLE_OK)
1505     {
1506       DBG << "HTTP response: " + str::numstring(httpReturnCode);
1507       cout << "HTTP response: " + str::numstring(httpReturnCode);
1508       if ( httpReturnCode == 304 ) // not modified
1509       {
1510         DBG << " Not modified.";
1511         modified = false;
1512       }
1513       DBG << endl;
1514     }
1515     else
1516     {
1517       WAR << "Could not get the reponse code." << endl;
1518     }
1519
1520     if (modified || infoRet != CURLE_OK)
1521     {
1522       // apply umask
1523       if ( ::fchmod( ::fileno(file), filesystem::applyUmaskTo( 0644 ) ) )
1524       {
1525         ERR << "Failed to chmod file " << destNew << endl;
1526       }
1527       ::fclose( file );
1528
1529       // move the temp file into dest
1530       if ( rename( destNew, dest ) != 0 ) {
1531         ERR << "Rename failed" << endl;
1532         ZYPP_THROW(MediaWriteException(dest));
1533       }
1534     }
1535     else
1536     {
1537       // close and remove the temp file
1538       ::fclose( file );
1539       filesystem::unlink( destNew );
1540     }
1541
1542     DBG << "done: " << PathInfo(dest) << endl;
1543 }
1544 */
1545
1546
1547 ///////////////////////////////////////////////////////////////////
1548 //
1549 //
1550 //        METHOD NAME : MediaAria2c::getDir
1551 //        METHOD TYPE : PMError
1552 //
1553 //        DESCRIPTION : Asserted that media is attached
1554 //
1555 void MediaAria2c::getDir( const Pathname & dirname, bool recurse_r ) const
1556 {
1557   filesystem::DirContent content;
1558   getDirInfo( content, dirname, /*dots*/false );
1559
1560   for ( filesystem::DirContent::const_iterator it = content.begin(); it != content.end(); ++it ) {
1561       Pathname filename = dirname + it->name;
1562       int res = 0;
1563
1564       switch ( it->type ) {
1565       case filesystem::FT_NOT_AVAIL: // old directory.yast contains no typeinfo at all
1566       case filesystem::FT_FILE:
1567         getFile( filename );
1568         break;
1569       case filesystem::FT_DIR: // newer directory.yast contain at least directory info
1570         if ( recurse_r ) {
1571           getDir( filename, recurse_r );
1572         } else {
1573           res = assert_dir( localPath( filename ) );
1574           if ( res ) {
1575             WAR << "Ignore error (" << res <<  ") on creating local directory '" << localPath( filename ) << "'" << endl;
1576           }
1577         }
1578         break;
1579       default:
1580         // don't provide devices, sockets, etc.
1581         break;
1582       }
1583   }
1584 }
1585
1586 ///////////////////////////////////////////////////////////////////
1587 //
1588 //
1589 //        METHOD NAME : MediaAria2c::getDirInfo
1590 //        METHOD TYPE : PMError
1591 //
1592 //        DESCRIPTION : Asserted that media is attached and retlist is empty.
1593 //
1594 void MediaAria2c::getDirInfo( std::list<std::string> & retlist,
1595                                const Pathname & dirname, bool dots ) const
1596 {
1597   getDirectoryYast( retlist, dirname, dots );
1598 }
1599
1600 ///////////////////////////////////////////////////////////////////
1601 //
1602 //
1603 //        METHOD NAME : MediaAria2c::getDirInfo
1604 //        METHOD TYPE : PMError
1605 //
1606 //        DESCRIPTION : Asserted that media is attached and retlist is empty.
1607 //
1608 void MediaAria2c::getDirInfo( filesystem::DirContent & retlist,
1609                             const Pathname & dirname, bool dots ) const
1610 {
1611   getDirectoryYast( retlist, dirname, dots );
1612 }
1613
1614 ///////////////////////////////////////////////////////////////////
1615 //
1616 //
1617 //        METHOD NAME : MediaAria2c::getAria2cVersion
1618 //
1619 //        DESCRIPTION : We get Aria2c version
1620 //
1621 std::string MediaAria2c::getAria2cVersion() 
1622 {
1623     const char* argv[] =
1624     {
1625         _aria2cPath.c_str(),
1626       "--version",
1627       NULL
1628     };
1629
1630     ExternalProgram aria(argv, ExternalProgram::Stderr_To_Stdout);
1631
1632     std::string vResponse = aria.receiveLine();
1633     aria.close();
1634     return vResponse;
1635 }
1636
1637 ///////////////////////////////////////////////////////////////////
1638 //
1639 //
1640 //        METHOD NAME : MediaAria2c::whereisAria2c
1641 //
1642 //        DESCRIPTION : Get aria2c path
1643 //
1644 #define ARIA_DEFAULT_BINARY "/usr/bin/aria2c"
1645
1646 Pathname MediaAria2c::whereisAria2c()
1647 {
1648     Pathname aria2cPathr(ARIA_DEFAULT_BINARY);
1649     
1650     const char* argv[] =
1651     {
1652       "whereis",
1653       "-b",
1654       "aria2c",
1655       NULL
1656     };
1657
1658     ExternalProgram aria(argv, ExternalProgram::Stderr_To_Stdout);
1659            
1660     std::string ariaResponse( aria.receiveLine());
1661     aria.close();
1662     
1663     string::size_type pos = ariaResponse.find('/', 0 );
1664     if( pos != string::npos ) 
1665     {
1666         aria2cPathr = ariaResponse;
1667         string::size_type pose = ariaResponse.find(' ', pos + 1 );
1668         aria2cPathr = ariaResponse.substr( pos , pose - pos );
1669         MIL << "We will use aria2c located here:  " << ariaResponse.substr( pos , pose - pos) << endl;
1670     }
1671     else 
1672     {
1673         MIL << "We don't know were is ari2ac binary. We will use aria2c located here:  " << aria2cPathr << endl;
1674     }
1675     
1676     return aria2cPathr;
1677 }
1678
1679 ///////////////////////////////////////////////////////////////////
1680 //
1681 //
1682 //        METHOD NAME : MediaAria2c::progressCallback
1683 //        METHOD TYPE : int
1684 //
1685 //        DESCRIPTION : Progress callback triggered from MediaAria2c::getFile
1686 //
1687 /*
1688 int MediaAria2c::progressCallback( void *clientp,
1689                                  double dltotal, double dlnow,
1690                                  double ultotal, double ulnow)
1691 {
1692   ProgressData *pdata = reinterpret_cast<ProgressData *>(clientp);
1693   if( pdata)
1694   {
1695     time_t now   = time(NULL);
1696     if( now > 0)
1697     {
1698         // reset time of last change in case initial time()
1699         // failed or the time was adjusted (goes backward)
1700         if( pdata->ltime <= 0 || pdata->ltime > now)
1701         {
1702           pdata->ltime = now;
1703         }
1704
1705         // start time counting as soon as first data arrives
1706         // (skip the connection / redirection time at begin)
1707         time_t dif = 0;
1708         if (dlnow > 0 || ulnow > 0)
1709         {
1710           dif = (now - pdata->ltime);
1711           dif = dif > 0 ? dif : 0;
1712
1713           pdata->secs += dif;
1714         }
1715
1716         // update the drate_avg and drate_period only after a second has passed
1717         // (this callback is called much more often than a second)
1718         // otherwise the values would be far from accurate when measuring
1719         // the time in seconds
1720         //! \todo more accurate download rate computationn, e.g. compute average value from last 5 seconds, or work with milliseconds instead of seconds
1721
1722         if ( pdata->secs > 1 && (dif > 0 || dlnow == dltotal ))
1723           pdata->drate_avg = (dlnow / pdata->secs);
1724
1725         if ( dif > 0 )
1726         {
1727           pdata->drate_period = ((dlnow - pdata->dload_period) / dif);
1728           pdata->dload_period = dlnow;
1729         }
1730     }
1731
1732     // send progress report first, abort transfer if requested
1733     if( pdata->report)
1734     {
1735       if (!(*(pdata->report))->progress(int( dlnow * 100 / dltotal ),
1736                                         pdata->url,
1737                                         pdata->drate_avg,
1738                                         pdata->drate_period))
1739       {
1740         return 1; // abort transfer
1741       }
1742     }
1743
1744     // check if we there is a timeout set
1745     if( pdata->timeout > 0)
1746     {
1747       if( now > 0)
1748       {
1749         bool progress = false;
1750
1751         // update download data if changed, mark progress
1752         if( dlnow != pdata->dload)
1753         {
1754           progress     = true;
1755           pdata->dload = dlnow;
1756           pdata->ltime = now;
1757         }
1758         // update upload data if changed, mark progress
1759         if( ulnow != pdata->uload)
1760         {
1761           progress     = true;
1762           pdata->uload = ulnow;
1763           pdata->ltime = now;
1764         }
1765
1766         if( !progress && (now >= (pdata->ltime + pdata->timeout)))
1767         {
1768           pdata->reached = true;
1769           return 1; // aborts transfer
1770         }
1771       }
1772     }
1773   }
1774   return 0;
1775 }
1776 */
1777 /*
1778 string MediaAria2c::getAuthHint() const
1779 {
1780   long auth_info = CURLAUTH_NONE;
1781
1782   CURLcode infoRet =
1783     curl_easy_getinfo(_curl, CURLINFO_HTTPAUTH_AVAIL, &auth_info);
1784
1785   if(infoRet == CURLE_OK)
1786   {
1787     return CurlAuthData::auth_type_long2str(auth_info);
1788   }
1789
1790   return "";
1791 }*/
1792
1793   } // namespace media
1794 } // namespace zypp
1795 //