Imported Upstream version 17.22.1
[platform/upstream/libzypp.git] / zypp / zyppng / media / network / networkrequesterror.cc
1 #include <zypp/zyppng/media/network/private/networkrequesterror_p.h>
2 #include <zypp/zyppng/media/network/request.h>
3 #include <zypp/media/CurlHelper.h>
4 #include <zypp/base/Gettext.h>
5 #include <zypp/media/MediaUserAuth.h>
6 #include <curl/curl.h>
7
8 namespace zyppng {
9
10 ZYPP_IMPL_PRIVATE(NetworkRequestError);
11
12 NetworkRequestErrorPrivate::NetworkRequestErrorPrivate(NetworkRequestError::Type code, std::string &&msg, std::map<std::string, boost::any> &&extraInfo)
13   : _errorCode(code)
14   , _errorMessage( std::move(msg) )
15 , _extraInfo( std::move(extraInfo) )
16 { }
17
18 NetworkRequestErrorPrivate *NetworkRequestErrorPrivate::clone() const
19 {
20   return new NetworkRequestErrorPrivate( *this );
21 }
22
23 NetworkRequestError NetworkRequestErrorPrivate::customError( NetworkRequestError::Type t, std::string &&errorMsg, std::map<std::string, boost::any> &&extraInfo )
24 {
25   return NetworkRequestError( *new NetworkRequestErrorPrivate(t, errorMsg.empty() ? typeToString(t) : std::move(errorMsg), std::move(extraInfo)) );
26 }
27
28 NetworkRequestError NetworkRequestErrorPrivate::fromCurlError( NetworkRequest &req, int nativeCode , const char *errBuf )
29 {
30
31   Url url = req.url();
32   NetworkRequestError::Type c = NetworkRequestError::NoError;
33   std::string err;
34   std::map<std::string, boost::any> extraInfo;
35
36   if ( nativeCode != 0 ) {
37
38     const char *nativeErr = curl_easy_strerror( static_cast<CURLcode>(nativeCode) );
39     if ( nativeErr != nullptr )
40       extraInfo.insert( { "nativeErrorCodeDesc",  std::string( nativeErr ) } );
41
42     if ( errBuf != nullptr )
43       extraInfo.insert( { "nativeErrorDesc",  std::string( errBuf ) } );
44
45     extraInfo.insert( { "requestUrl", url } );
46     extraInfo.insert( { "curlCode", nativeCode } );
47     extraInfo.insert( { "filepath", req.targetFilePath().asString() } );
48     extraInfo.insert( { "lastRedirect", req.lastRedirectInfo() } );
49
50     switch ( nativeCode )
51     {
52       case CURLE_UNSUPPORTED_PROTOCOL:
53         c = NetworkRequestError::UnsupportedProtocol;
54         err = typeToString( c );
55         if ( !req.lastRedirectInfo().empty() )
56         {
57           err += " or redirect (";
58           err += req.lastRedirectInfo();
59           err += ")";
60         }
61         break;
62       case CURLE_URL_MALFORMAT: case CURLE_URL_MALFORMAT_USER:
63         c = NetworkRequestError::MalformedURL;
64         break;
65       case CURLE_LOGIN_DENIED:
66         c = NetworkRequestError::Unauthorized;
67         break;
68       case CURLE_HTTP_RETURNED_ERROR: {
69         long httpReturnCode = 0;
70         CURLcode infoRet = curl_easy_getinfo( req.nativeHandle(),
71           CURLINFO_RESPONSE_CODE,
72           &httpReturnCode );
73
74         if ( infoRet == CURLE_OK ) {
75           extraInfo.insert( { "responseCode", httpReturnCode } );
76
77           std::string msg = "HTTP response: " + zypp::str::numstring( httpReturnCode );
78           switch ( httpReturnCode )
79           {
80             case 401: {
81               std::string auth_hint;
82               {
83                 long auth_info = CURLAUTH_NONE;
84
85                 CURLcode infoRet =
86                   curl_easy_getinfo(req.nativeHandle(), CURLINFO_HTTPAUTH_AVAIL, &auth_info);
87
88                 if(infoRet == CURLE_OK) {
89                   extraInfo.insert( { "authHint", zypp::media::CurlAuthData::auth_type_long2str(auth_info) } );
90                 }
91               }
92
93               //if there is already a user:password entry in the settings the auth simply failed
94               //@TODO write a testcase for this
95               if ( !req.transferSettings().userPassword().empty() ) {
96                 c = NetworkRequestError::AuthFailed;
97               } else {
98                 c = NetworkRequestError::Unauthorized;
99               }
100
101               break;
102             }
103
104             case 502: // bad gateway (bnc #1070851)
105             case 503: // service temporarily unavailable (bnc #462545)
106               c = NetworkRequestError::TemporaryProblem;
107               err = zypp::str::form( _("Location '%s' is temporarily unaccessible."), url.asString().c_str() );
108               break;
109             case 504: // gateway timeout
110               c = NetworkRequestError::Timeout;
111               err = zypp::str::form(_("Timeout exceeded when accessing '%s'."), url.asString().c_str() );
112               break;
113             case 403: {
114               std::string msg403;
115               if ( url.getHost().find(".suse.com") != std::string::npos )
116                 msg403 = _("Visit the SUSE Customer Center to check whether your registration is valid and has not expired.");
117               else if (url.asString().find("novell.com") != std::string::npos)
118                 msg403 = _("Visit the Novell Customer Center to check whether your registration is valid and has not expired.");
119
120               c = NetworkRequestError::Forbidden;
121               err = msg403;
122               break;
123             }
124             case 404:
125             case 410:
126               c = NetworkRequestError::NotFound;
127               err = zypp::str::form( _("File '%s' not found on medium '%s'"), req.targetFilePath().c_str(), url.asString().c_str() );
128               break;
129
130             default:
131               c = NetworkRequestError::ServerReturnedError;
132               err = zypp::str::form(_("Download (curl) error for '%s':\n"
133                                         "Error code: %s\n"), url.asString().c_str(), zypp::str::numstring( httpReturnCode ).c_str() ) ;
134               break;
135           }
136         } else {
137           c = NetworkRequestError::ServerReturnedError;
138           err = zypp::str::form(_("Download (curl) error for '%s':\n"
139                                     "Unable to retrieve HTTP response\n"), url.asString().c_str() ) ;
140         }
141       }
142       break;
143       case CURLE_FTP_COULDNT_RETR_FILE:
144 #if CURLVERSION_AT_LEAST(7,16,0)
145       case CURLE_REMOTE_FILE_NOT_FOUND:
146 #endif
147       case CURLE_FTP_ACCESS_DENIED:
148       case CURLE_TFTP_NOTFOUND:
149         c = NetworkRequestError::NotFound;
150         break;
151       case CURLE_BAD_PASSWORD_ENTERED:
152       case CURLE_FTP_USER_PASSWORD_INCORRECT:
153         c = NetworkRequestError::AuthFailed;
154         break;
155       case CURLE_COULDNT_RESOLVE_PROXY:
156       case CURLE_COULDNT_RESOLVE_HOST:
157       case CURLE_COULDNT_CONNECT:
158       case CURLE_FTP_CANT_GET_HOST:
159         c = NetworkRequestError::ConnectionFailed;
160         break;
161       case CURLE_WRITE_ERROR:
162         c = NetworkRequestError::InternalError;
163         break;
164       case CURLE_PARTIAL_FILE:
165       case CURLE_OPERATION_TIMEDOUT:
166         c = NetworkRequestError::Timeout;
167         break;
168       case CURLE_ABORTED_BY_CALLBACK:
169         c = NetworkRequestError::Cancelled;
170         break;
171       case CURLE_PEER_FAILED_VERIFICATION:
172         c = NetworkRequestError::PeerCertificateInvalid;
173         break;
174       default:
175         c = NetworkRequestError::ServerReturnedError;
176         err = "Curl error " + zypp::str::numstring( nativeCode );
177         break;
178     }
179   }
180
181   if ( err.empty() )
182     err = typeToString( c );
183
184   return NetworkRequestError( *new NetworkRequestErrorPrivate(c, std::move(err), std::move(extraInfo)) );
185 }
186
187 NetworkRequestError NetworkRequestErrorPrivate::fromCurlMError( int nativeCode )
188 {
189   const char *nativeErr = curl_multi_strerror( static_cast<CURLMcode>(nativeCode) );
190
191   std::map<std::string, boost::any> extraInfo;
192   extraInfo.insert( { "curlMCode", nativeCode } );
193
194   std::string err;
195   if ( nativeErr == nullptr )
196     err = "The dispatcher returned an unknown error";
197   else
198     err = std::string( nativeErr );
199
200   return NetworkRequestError( *new NetworkRequestErrorPrivate(NetworkRequestError::InternalError, std::move(err), std::move(extraInfo)) );
201 }
202
203
204 NetworkRequestError::NetworkRequestError(zyppng::NetworkRequestErrorPrivate &d )
205   : d_ptr( &d )
206 { }
207
208 NetworkRequestError::NetworkRequestError()
209   : d_ptr( new NetworkRequestErrorPrivate( NoError, {}, {} ) )
210 { }
211
212 NetworkRequestError::Type NetworkRequestError::type() const
213 {
214   return d_func()->_errorCode;
215 }
216
217 std::string NetworkRequestError::toString() const
218 {
219   return d_func()->_errorMessage;
220 }
221
222 bool NetworkRequestError::isError() const
223 {
224   return d_func()->_errorCode != NoError;
225 }
226
227 const std::map<std::string, boost::any> &NetworkRequestError::extraInfo() const
228 {
229   return d_func()->_extraInfo;
230 }
231
232 std::string NetworkRequestErrorPrivate::typeToString( NetworkRequestError::Type t )
233 {
234   switch ( t ) {
235     case NetworkRequestError::NoError:
236       return "No error";
237     case NetworkRequestError::InternalError:
238       return "Internal Error";
239     case NetworkRequestError::Cancelled:
240       return "The request was cancelled";
241     case NetworkRequestError::ExceededMaxLen:
242       return "The request exceeded the maximum download size";
243     case NetworkRequestError::InvalidChecksum:
244       return "The downloaded data did not result in a valid checksum";
245     case NetworkRequestError::PeerCertificateInvalid:
246       return "The peer certificate could not be verified";
247     case NetworkRequestError::ConnectionFailed:
248       return "Connection failed";
249     case NetworkRequestError::UnsupportedProtocol:
250       return "Unsupported protocol";
251     case NetworkRequestError::MalformedURL:
252       return "Bad URL";
253     case NetworkRequestError::TemporaryProblem:
254       return "Requested location is temporarily unaccessible.";
255     case NetworkRequestError::Timeout:
256       return "Timeout reached";
257     case NetworkRequestError::Forbidden:
258       return "Access to requested URL is forbidden.";
259     case NetworkRequestError::NotFound:
260       return "File not found";
261     case NetworkRequestError::Unauthorized:
262       return "Authentication required but not provided.";
263     case NetworkRequestError::AuthFailed:
264       return "Login failed.";
265     case NetworkRequestError::ServerReturnedError:
266       return "Server returned an error for the given request.";
267   }
268   return std::string();
269 }
270
271 std::string NetworkRequestError::nativeErrorString() const
272 {
273   Z_D();
274
275   auto it = d->_extraInfo.find("nativeErrorDesc");
276   if ( it != d->_extraInfo.end() ) {
277     try {
278       return boost::any_cast<std::string>( it->second );
279     } catch ( const boost::bad_any_cast &) { }
280   }
281
282   it = d->_extraInfo.find("nativeErrorCodeDesc");
283   if ( it != d->_extraInfo.end() ) {
284     try {
285       return boost::any_cast<std::string>( it->second );
286     } catch ( const boost::bad_any_cast &) { }
287   }
288
289   return std::string();
290 }
291
292 }