1use std::error;
2use std::ffi::{self, CStr};
3use std::fmt;
4use std::io;
5use std::str;
6
7/// An error returned from various "easy" operations.
8///
9/// This structure wraps a `CURLcode`.
10#[derive(Clone, PartialEq)]
11pub struct Error {
12 code: curl_sys::CURLcode,
13 extra: Option<Box<str>>,
14}
15
16impl Error {
17 /// Creates a new error from the underlying code returned by libcurl.
18 pub fn new(code: curl_sys::CURLcode) -> Error {
19 Error { code, extra: None }
20 }
21
22 /// Stores some extra information about this error inside this error.
23 ///
24 /// This is typically used with `take_error_buf` on the easy handles to
25 /// couple the extra `CURLOPT_ERRORBUFFER` information with an `Error` being
26 /// returned.
27 pub fn set_extra(&mut self, extra: String) {
28 self.extra = Some(extra.into());
29 }
30
31 /// Returns whether this error corresponds to CURLE_UNSUPPORTED_PROTOCOL.
32 pub fn is_unsupported_protocol(&self) -> bool {
33 self.code == curl_sys::CURLE_UNSUPPORTED_PROTOCOL
34 }
35
36 /// Returns whether this error corresponds to CURLE_FAILED_INIT.
37 pub fn is_failed_init(&self) -> bool {
38 self.code == curl_sys::CURLE_FAILED_INIT
39 }
40
41 /// Returns whether this error corresponds to CURLE_URL_MALFORMAT.
42 pub fn is_url_malformed(&self) -> bool {
43 self.code == curl_sys::CURLE_URL_MALFORMAT
44 }
45
46 // /// Returns whether this error corresponds to CURLE_NOT_BUILT_IN.
47 // pub fn is_not_built_in(&self) -> bool {
48 // self.code == curl_sys::CURLE_NOT_BUILT_IN
49 // }
50
51 /// Returns whether this error corresponds to CURLE_COULDNT_RESOLVE_PROXY.
52 pub fn is_couldnt_resolve_proxy(&self) -> bool {
53 self.code == curl_sys::CURLE_COULDNT_RESOLVE_PROXY
54 }
55
56 /// Returns whether this error corresponds to CURLE_COULDNT_RESOLVE_HOST.
57 pub fn is_couldnt_resolve_host(&self) -> bool {
58 self.code == curl_sys::CURLE_COULDNT_RESOLVE_HOST
59 }
60
61 /// Returns whether this error corresponds to CURLE_COULDNT_CONNECT.
62 pub fn is_couldnt_connect(&self) -> bool {
63 self.code == curl_sys::CURLE_COULDNT_CONNECT
64 }
65
66 /// Returns whether this error corresponds to CURLE_REMOTE_ACCESS_DENIED.
67 pub fn is_remote_access_denied(&self) -> bool {
68 self.code == curl_sys::CURLE_REMOTE_ACCESS_DENIED
69 }
70
71 /// Returns whether this error corresponds to CURLE_PARTIAL_FILE.
72 pub fn is_partial_file(&self) -> bool {
73 self.code == curl_sys::CURLE_PARTIAL_FILE
74 }
75
76 /// Returns whether this error corresponds to CURLE_QUOTE_ERROR.
77 pub fn is_quote_error(&self) -> bool {
78 self.code == curl_sys::CURLE_QUOTE_ERROR
79 }
80
81 /// Returns whether this error corresponds to CURLE_HTTP_RETURNED_ERROR.
82 pub fn is_http_returned_error(&self) -> bool {
83 self.code == curl_sys::CURLE_HTTP_RETURNED_ERROR
84 }
85
86 /// Returns whether this error corresponds to CURLE_READ_ERROR.
87 pub fn is_read_error(&self) -> bool {
88 self.code == curl_sys::CURLE_READ_ERROR
89 }
90
91 /// Returns whether this error corresponds to CURLE_WRITE_ERROR.
92 pub fn is_write_error(&self) -> bool {
93 self.code == curl_sys::CURLE_WRITE_ERROR
94 }
95
96 /// Returns whether this error corresponds to CURLE_UPLOAD_FAILED.
97 pub fn is_upload_failed(&self) -> bool {
98 self.code == curl_sys::CURLE_UPLOAD_FAILED
99 }
100
101 /// Returns whether this error corresponds to CURLE_OUT_OF_MEMORY.
102 pub fn is_out_of_memory(&self) -> bool {
103 self.code == curl_sys::CURLE_OUT_OF_MEMORY
104 }
105
106 /// Returns whether this error corresponds to CURLE_OPERATION_TIMEDOUT.
107 pub fn is_operation_timedout(&self) -> bool {
108 self.code == curl_sys::CURLE_OPERATION_TIMEDOUT
109 }
110
111 /// Returns whether this error corresponds to CURLE_RANGE_ERROR.
112 pub fn is_range_error(&self) -> bool {
113 self.code == curl_sys::CURLE_RANGE_ERROR
114 }
115
116 /// Returns whether this error corresponds to CURLE_HTTP_POST_ERROR.
117 pub fn is_http_post_error(&self) -> bool {
118 self.code == curl_sys::CURLE_HTTP_POST_ERROR
119 }
120
121 /// Returns whether this error corresponds to CURLE_SSL_CONNECT_ERROR.
122 pub fn is_ssl_connect_error(&self) -> bool {
123 self.code == curl_sys::CURLE_SSL_CONNECT_ERROR
124 }
125
126 /// Returns whether this error corresponds to CURLE_BAD_DOWNLOAD_RESUME.
127 pub fn is_bad_download_resume(&self) -> bool {
128 self.code == curl_sys::CURLE_BAD_DOWNLOAD_RESUME
129 }
130
131 /// Returns whether this error corresponds to CURLE_FILE_COULDNT_READ_FILE.
132 pub fn is_file_couldnt_read_file(&self) -> bool {
133 self.code == curl_sys::CURLE_FILE_COULDNT_READ_FILE
134 }
135
136 /// Returns whether this error corresponds to CURLE_FUNCTION_NOT_FOUND.
137 pub fn is_function_not_found(&self) -> bool {
138 self.code == curl_sys::CURLE_FUNCTION_NOT_FOUND
139 }
140
141 /// Returns whether this error corresponds to CURLE_ABORTED_BY_CALLBACK.
142 pub fn is_aborted_by_callback(&self) -> bool {
143 self.code == curl_sys::CURLE_ABORTED_BY_CALLBACK
144 }
145
146 /// Returns whether this error corresponds to CURLE_BAD_FUNCTION_ARGUMENT.
147 pub fn is_bad_function_argument(&self) -> bool {
148 self.code == curl_sys::CURLE_BAD_FUNCTION_ARGUMENT
149 }
150
151 /// Returns whether this error corresponds to CURLE_INTERFACE_FAILED.
152 pub fn is_interface_failed(&self) -> bool {
153 self.code == curl_sys::CURLE_INTERFACE_FAILED
154 }
155
156 /// Returns whether this error corresponds to CURLE_TOO_MANY_REDIRECTS.
157 pub fn is_too_many_redirects(&self) -> bool {
158 self.code == curl_sys::CURLE_TOO_MANY_REDIRECTS
159 }
160
161 /// Returns whether this error corresponds to CURLE_UNKNOWN_OPTION.
162 pub fn is_unknown_option(&self) -> bool {
163 self.code == curl_sys::CURLE_UNKNOWN_OPTION
164 }
165
166 /// Returns whether this error corresponds to CURLE_PEER_FAILED_VERIFICATION.
167 pub fn is_peer_failed_verification(&self) -> bool {
168 self.code == curl_sys::CURLE_PEER_FAILED_VERIFICATION
169 }
170
171 /// Returns whether this error corresponds to CURLE_GOT_NOTHING.
172 pub fn is_got_nothing(&self) -> bool {
173 self.code == curl_sys::CURLE_GOT_NOTHING
174 }
175
176 /// Returns whether this error corresponds to CURLE_SSL_ENGINE_NOTFOUND.
177 pub fn is_ssl_engine_notfound(&self) -> bool {
178 self.code == curl_sys::CURLE_SSL_ENGINE_NOTFOUND
179 }
180
181 /// Returns whether this error corresponds to CURLE_SSL_ENGINE_SETFAILED.
182 pub fn is_ssl_engine_setfailed(&self) -> bool {
183 self.code == curl_sys::CURLE_SSL_ENGINE_SETFAILED
184 }
185
186 /// Returns whether this error corresponds to CURLE_SEND_ERROR.
187 pub fn is_send_error(&self) -> bool {
188 self.code == curl_sys::CURLE_SEND_ERROR
189 }
190
191 /// Returns whether this error corresponds to CURLE_RECV_ERROR.
192 pub fn is_recv_error(&self) -> bool {
193 self.code == curl_sys::CURLE_RECV_ERROR
194 }
195
196 /// Returns whether this error corresponds to CURLE_SSL_CERTPROBLEM.
197 pub fn is_ssl_certproblem(&self) -> bool {
198 self.code == curl_sys::CURLE_SSL_CERTPROBLEM
199 }
200
201 /// Returns whether this error corresponds to CURLE_SSL_CIPHER.
202 pub fn is_ssl_cipher(&self) -> bool {
203 self.code == curl_sys::CURLE_SSL_CIPHER
204 }
205
206 /// Returns whether this error corresponds to CURLE_SSL_CACERT.
207 pub fn is_ssl_cacert(&self) -> bool {
208 self.code == curl_sys::CURLE_SSL_CACERT
209 }
210
211 /// Returns whether this error corresponds to CURLE_BAD_CONTENT_ENCODING.
212 pub fn is_bad_content_encoding(&self) -> bool {
213 self.code == curl_sys::CURLE_BAD_CONTENT_ENCODING
214 }
215
216 /// Returns whether this error corresponds to CURLE_FILESIZE_EXCEEDED.
217 pub fn is_filesize_exceeded(&self) -> bool {
218 self.code == curl_sys::CURLE_FILESIZE_EXCEEDED
219 }
220
221 /// Returns whether this error corresponds to CURLE_USE_SSL_FAILED.
222 pub fn is_use_ssl_failed(&self) -> bool {
223 self.code == curl_sys::CURLE_USE_SSL_FAILED
224 }
225
226 /// Returns whether this error corresponds to CURLE_SEND_FAIL_REWIND.
227 pub fn is_send_fail_rewind(&self) -> bool {
228 self.code == curl_sys::CURLE_SEND_FAIL_REWIND
229 }
230
231 /// Returns whether this error corresponds to CURLE_SSL_ENGINE_INITFAILED.
232 pub fn is_ssl_engine_initfailed(&self) -> bool {
233 self.code == curl_sys::CURLE_SSL_ENGINE_INITFAILED
234 }
235
236 /// Returns whether this error corresponds to CURLE_LOGIN_DENIED.
237 pub fn is_login_denied(&self) -> bool {
238 self.code == curl_sys::CURLE_LOGIN_DENIED
239 }
240
241 /// Returns whether this error corresponds to CURLE_CONV_FAILED.
242 pub fn is_conv_failed(&self) -> bool {
243 self.code == curl_sys::CURLE_CONV_FAILED
244 }
245
246 /// Returns whether this error corresponds to CURLE_CONV_REQD.
247 pub fn is_conv_required(&self) -> bool {
248 self.code == curl_sys::CURLE_CONV_REQD
249 }
250
251 /// Returns whether this error corresponds to CURLE_SSL_CACERT_BADFILE.
252 pub fn is_ssl_cacert_badfile(&self) -> bool {
253 self.code == curl_sys::CURLE_SSL_CACERT_BADFILE
254 }
255
256 /// Returns whether this error corresponds to CURLE_SSL_CRL_BADFILE.
257 pub fn is_ssl_crl_badfile(&self) -> bool {
258 self.code == curl_sys::CURLE_SSL_CRL_BADFILE
259 }
260
261 /// Returns whether this error corresponds to CURLE_SSL_SHUTDOWN_FAILED.
262 pub fn is_ssl_shutdown_failed(&self) -> bool {
263 self.code == curl_sys::CURLE_SSL_SHUTDOWN_FAILED
264 }
265
266 /// Returns whether this error corresponds to CURLE_AGAIN.
267 pub fn is_again(&self) -> bool {
268 self.code == curl_sys::CURLE_AGAIN
269 }
270
271 /// Returns whether this error corresponds to CURLE_SSL_ISSUER_ERROR.
272 pub fn is_ssl_issuer_error(&self) -> bool {
273 self.code == curl_sys::CURLE_SSL_ISSUER_ERROR
274 }
275
276 /// Returns whether this error corresponds to CURLE_CHUNK_FAILED.
277 pub fn is_chunk_failed(&self) -> bool {
278 self.code == curl_sys::CURLE_CHUNK_FAILED
279 }
280
281 /// Returns whether this error corresponds to CURLE_HTTP2.
282 pub fn is_http2_error(&self) -> bool {
283 self.code == curl_sys::CURLE_HTTP2
284 }
285
286 /// Returns whether this error corresponds to CURLE_HTTP2_STREAM.
287 pub fn is_http2_stream_error(&self) -> bool {
288 self.code == curl_sys::CURLE_HTTP2_STREAM
289 }
290
291 // /// Returns whether this error corresponds to CURLE_NO_CONNECTION_AVAILABLE.
292 // pub fn is_no_connection_available(&self) -> bool {
293 // self.code == curl_sys::CURLE_NO_CONNECTION_AVAILABLE
294 // }
295
296 /// Returns the value of the underlying error corresponding to libcurl.
297 pub fn code(&self) -> curl_sys::CURLcode {
298 self.code
299 }
300
301 /// Returns the general description of this error code, using curl's
302 /// builtin `strerror`-like functionality.
303 pub fn description(&self) -> &str {
304 unsafe {
305 let s = curl_sys::curl_easy_strerror(self.code);
306 assert!(!s.is_null());
307 str::from_utf8(CStr::from_ptr(s).to_bytes()).unwrap()
308 }
309 }
310
311 /// Returns the extra description of this error, if any is available.
312 pub fn extra_description(&self) -> Option<&str> {
313 self.extra.as_deref()
314 }
315}
316
317impl fmt::Display for Error {
318 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
319 let desc: &str = self.description();
320 match self.extra {
321 Some(ref s: &Box) => write!(f, "[{}] {} ({})", self.code(), desc, s),
322 None => write!(f, "[{}] {}", self.code(), desc),
323 }
324 }
325}
326
327impl fmt::Debug for Error {
328 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
329 f&mut DebugStruct<'_, '_>.debug_struct("Error")
330 .field("description", &self.description())
331 .field("code", &self.code)
332 .field(name:"extra", &self.extra)
333 .finish()
334 }
335}
336
337impl error::Error for Error {}
338
339/// An error returned from "share" operations.
340///
341/// This structure wraps a `CURLSHcode`.
342#[derive(Clone, PartialEq)]
343pub struct ShareError {
344 code: curl_sys::CURLSHcode,
345}
346
347impl ShareError {
348 /// Creates a new error from the underlying code returned by libcurl.
349 pub fn new(code: curl_sys::CURLSHcode) -> ShareError {
350 ShareError { code }
351 }
352
353 /// Returns whether this error corresponds to CURLSHE_BAD_OPTION.
354 pub fn is_bad_option(&self) -> bool {
355 self.code == curl_sys::CURLSHE_BAD_OPTION
356 }
357
358 /// Returns whether this error corresponds to CURLSHE_IN_USE.
359 pub fn is_in_use(&self) -> bool {
360 self.code == curl_sys::CURLSHE_IN_USE
361 }
362
363 /// Returns whether this error corresponds to CURLSHE_INVALID.
364 pub fn is_invalid(&self) -> bool {
365 self.code == curl_sys::CURLSHE_INVALID
366 }
367
368 /// Returns whether this error corresponds to CURLSHE_NOMEM.
369 pub fn is_nomem(&self) -> bool {
370 self.code == curl_sys::CURLSHE_NOMEM
371 }
372
373 // /// Returns whether this error corresponds to CURLSHE_NOT_BUILT_IN.
374 // pub fn is_not_built_in(&self) -> bool {
375 // self.code == curl_sys::CURLSHE_NOT_BUILT_IN
376 // }
377
378 /// Returns the value of the underlying error corresponding to libcurl.
379 pub fn code(&self) -> curl_sys::CURLSHcode {
380 self.code
381 }
382
383 /// Returns curl's human-readable version of this error.
384 pub fn description(&self) -> &str {
385 unsafe {
386 let s = curl_sys::curl_share_strerror(self.code);
387 assert!(!s.is_null());
388 str::from_utf8(CStr::from_ptr(s).to_bytes()).unwrap()
389 }
390 }
391}
392
393impl fmt::Display for ShareError {
394 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
395 self.description().fmt(f)
396 }
397}
398
399impl fmt::Debug for ShareError {
400 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
401 write!(
402 f,
403 "ShareError {{ description: {:?}, code: {} }}",
404 self.description(),
405 self.code
406 )
407 }
408}
409
410impl error::Error for ShareError {}
411
412/// An error from "multi" operations.
413///
414/// THis structure wraps a `CURLMcode`.
415#[derive(Clone, PartialEq)]
416pub struct MultiError {
417 code: curl_sys::CURLMcode,
418}
419
420impl MultiError {
421 /// Creates a new error from the underlying code returned by libcurl.
422 pub fn new(code: curl_sys::CURLMcode) -> MultiError {
423 MultiError { code }
424 }
425
426 /// Returns whether this error corresponds to CURLM_BAD_HANDLE.
427 pub fn is_bad_handle(&self) -> bool {
428 self.code == curl_sys::CURLM_BAD_HANDLE
429 }
430
431 /// Returns whether this error corresponds to CURLM_BAD_EASY_HANDLE.
432 pub fn is_bad_easy_handle(&self) -> bool {
433 self.code == curl_sys::CURLM_BAD_EASY_HANDLE
434 }
435
436 /// Returns whether this error corresponds to CURLM_OUT_OF_MEMORY.
437 pub fn is_out_of_memory(&self) -> bool {
438 self.code == curl_sys::CURLM_OUT_OF_MEMORY
439 }
440
441 /// Returns whether this error corresponds to CURLM_INTERNAL_ERROR.
442 pub fn is_internal_error(&self) -> bool {
443 self.code == curl_sys::CURLM_INTERNAL_ERROR
444 }
445
446 /// Returns whether this error corresponds to CURLM_BAD_SOCKET.
447 pub fn is_bad_socket(&self) -> bool {
448 self.code == curl_sys::CURLM_BAD_SOCKET
449 }
450
451 /// Returns whether this error corresponds to CURLM_UNKNOWN_OPTION.
452 pub fn is_unknown_option(&self) -> bool {
453 self.code == curl_sys::CURLM_UNKNOWN_OPTION
454 }
455
456 /// Returns whether this error corresponds to CURLM_CALL_MULTI_PERFORM.
457 pub fn is_call_perform(&self) -> bool {
458 self.code == curl_sys::CURLM_CALL_MULTI_PERFORM
459 }
460
461 // /// Returns whether this error corresponds to CURLM_ADDED_ALREADY.
462 // pub fn is_added_already(&self) -> bool {
463 // self.code == curl_sys::CURLM_ADDED_ALREADY
464 // }
465
466 /// Returns the value of the underlying error corresponding to libcurl.
467 pub fn code(&self) -> curl_sys::CURLMcode {
468 self.code
469 }
470
471 /// Returns curl's human-readable description of this error.
472 pub fn description(&self) -> &str {
473 unsafe {
474 let s = curl_sys::curl_multi_strerror(self.code);
475 assert!(!s.is_null());
476 str::from_utf8(CStr::from_ptr(s).to_bytes()).unwrap()
477 }
478 }
479}
480
481impl fmt::Display for MultiError {
482 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
483 self.description().fmt(f)
484 }
485}
486
487impl fmt::Debug for MultiError {
488 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
489 f&mut DebugStruct<'_, '_>.debug_struct("MultiError")
490 .field("description", &self.description())
491 .field(name:"code", &self.code)
492 .finish()
493 }
494}
495
496impl error::Error for MultiError {}
497
498/// An error from "form add" operations.
499///
500/// THis structure wraps a `CURLFORMcode`.
501#[derive(Clone, PartialEq)]
502pub struct FormError {
503 code: curl_sys::CURLFORMcode,
504}
505
506impl FormError {
507 /// Creates a new error from the underlying code returned by libcurl.
508 pub fn new(code: curl_sys::CURLFORMcode) -> FormError {
509 FormError { code }
510 }
511
512 /// Returns whether this error corresponds to CURL_FORMADD_MEMORY.
513 pub fn is_memory(&self) -> bool {
514 self.code == curl_sys::CURL_FORMADD_MEMORY
515 }
516
517 /// Returns whether this error corresponds to CURL_FORMADD_OPTION_TWICE.
518 pub fn is_option_twice(&self) -> bool {
519 self.code == curl_sys::CURL_FORMADD_OPTION_TWICE
520 }
521
522 /// Returns whether this error corresponds to CURL_FORMADD_NULL.
523 pub fn is_null(&self) -> bool {
524 self.code == curl_sys::CURL_FORMADD_NULL
525 }
526
527 /// Returns whether this error corresponds to CURL_FORMADD_UNKNOWN_OPTION.
528 pub fn is_unknown_option(&self) -> bool {
529 self.code == curl_sys::CURL_FORMADD_UNKNOWN_OPTION
530 }
531
532 /// Returns whether this error corresponds to CURL_FORMADD_INCOMPLETE.
533 pub fn is_incomplete(&self) -> bool {
534 self.code == curl_sys::CURL_FORMADD_INCOMPLETE
535 }
536
537 /// Returns whether this error corresponds to CURL_FORMADD_ILLEGAL_ARRAY.
538 pub fn is_illegal_array(&self) -> bool {
539 self.code == curl_sys::CURL_FORMADD_ILLEGAL_ARRAY
540 }
541
542 /// Returns whether this error corresponds to CURL_FORMADD_DISABLED.
543 pub fn is_disabled(&self) -> bool {
544 self.code == curl_sys::CURL_FORMADD_DISABLED
545 }
546
547 /// Returns the value of the underlying error corresponding to libcurl.
548 pub fn code(&self) -> curl_sys::CURLFORMcode {
549 self.code
550 }
551
552 /// Returns a human-readable description of this error code.
553 pub fn description(&self) -> &str {
554 match self.code {
555 curl_sys::CURL_FORMADD_MEMORY => "allocation failure",
556 curl_sys::CURL_FORMADD_OPTION_TWICE => "one option passed twice",
557 curl_sys::CURL_FORMADD_NULL => "null pointer given for string",
558 curl_sys::CURL_FORMADD_UNKNOWN_OPTION => "unknown option",
559 curl_sys::CURL_FORMADD_INCOMPLETE => "form information not complete",
560 curl_sys::CURL_FORMADD_ILLEGAL_ARRAY => "illegal array in option",
561 curl_sys::CURL_FORMADD_DISABLED => {
562 "libcurl does not have support for this option compiled in"
563 }
564 _ => "unknown form error",
565 }
566 }
567}
568
569impl fmt::Display for FormError {
570 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
571 self.description().fmt(f)
572 }
573}
574
575impl fmt::Debug for FormError {
576 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
577 f&mut DebugStruct<'_, '_>.debug_struct("FormError")
578 .field("description", &self.description())
579 .field(name:"code", &self.code)
580 .finish()
581 }
582}
583
584impl error::Error for FormError {}
585
586impl From<ffi::NulError> for Error {
587 fn from(_: ffi::NulError) -> Error {
588 Error {
589 code: curl_sys::CURLE_CONV_FAILED,
590 extra: None,
591 }
592 }
593}
594
595impl From<Error> for io::Error {
596 fn from(e: Error) -> io::Error {
597 io::Error::new(kind:io::ErrorKind::Other, error:e)
598 }
599}
600
601impl From<ShareError> for io::Error {
602 fn from(e: ShareError) -> io::Error {
603 io::Error::new(kind:io::ErrorKind::Other, error:e)
604 }
605}
606
607impl From<MultiError> for io::Error {
608 fn from(e: MultiError) -> io::Error {
609 io::Error::new(kind:io::ErrorKind::Other, error:e)
610 }
611}
612
613impl From<FormError> for io::Error {
614 fn from(e: FormError) -> io::Error {
615 io::Error::new(kind:io::ErrorKind::Other, error:e)
616 }
617}
618