1 /*
2 * Part of WCM Commander
3 * https://github.com/corporateshark/WCMCommander
4 * wcm@linderdaum.com
5 */
6
7 #ifdef _WIN32
8 # include <winsock2.h>
9 #endif
10
11
12 #include "vfs-sftp.h"
13
14 #ifdef LIBSSH2_EXIST
15
16 #include "string-util.h"
17
18 #define SSH_PASSWORD_ATTEMPTS 3
19
InitSSH()20 void InitSSH()
21 {
22 libssh2_init( 0 );
23 }
24
GetDefaultSshKeys(FSString & pub_key,FSString & private_key)25 bool GetDefaultSshKeys( FSString& pub_key, FSString& private_key )
26 {
27 // TODO: Get by-host keyfiles, configure keyfiles, etc etc
28 // http://askubuntu.com/questions/30788/does-ssh-key-need-to-be-named-id-rsa
29
30 #if _MSC_VER > 1700
31 char* home;
32 size_t size;
33 _dupenv_s(&home, &size, "HOME" );
34 #else
35 const char* home = getenv( "HOME" );
36 #endif
37
38 if ( !home )
39 {
40 return false;
41 }
42
43 pub_key = carray_cat<char>( home, "/.ssh/id_rsa.pub" ).data();
44 private_key = carray_cat<char>( home, "/.ssh/id_rsa" ).data();
45
46 #if _MSC_VER > 1700
47 // deallocate after _dupenv_s()
48 free(home);
49 #endif
50
51 struct stat sb;
52 if ( ( stat( pub_key.GetUtf8(), &sb ) != 0 ) || ( stat( private_key.GetUtf8(), &sb ) != 0 ) )
53 {
54 pub_key = "";
55 private_key = "";
56 return false;
57 }
58
59 return true;
60 }
61
62
63 enum INT_SSH_ERRORS
64 {
65 SSH_INTERROR_NOTSUPPORT = -20,
66 SSH_INTERROR_X3 = -21,
67 SSH_INTERROR_CONNECT = -22,
68 SSH_INTERROR_AUTH = -23,
69 SSH_INTERROR_FATAL = -24,
70 SSH_INTERROR_OUTOF = -25,
71 SSH_INTERROR_UNSUPPORTED_AUTH = -26,
72 SSH_INTERROR_STOPPED = -50
73 };
74
75
FSSftp(FSSftpParam * param)76 FSSftp::FSSftp( FSSftpParam* param )
77 : FS( SFTP ), sshSession( 0 ), sftpSession( 0 )
78 {
79 if ( param )
80 {
81 _operParam = *param;
82 _infoParam = *param;
83 }
84
85 for ( int i = 0; i < MAX_FILES; i++ )
86 {
87 fileTable[i] = 0;
88 }
89 }
90
91
WaitSocket(FSCInfo * info)92 void FSSftp::WaitSocket( FSCInfo* info ) //throw int(errno) or int(-2) on stop
93 {
94 while ( true )
95 {
96 if ( info && info->IsStopped() ) { throw int( -2 ); } //stopped
97
98 int dir = libssh2_session_block_directions( sshSession );
99
100 if ( ( dir & ( LIBSSH2_SESSION_BLOCK_INBOUND | LIBSSH2_SESSION_BLOCK_OUTBOUND ) ) == 0 )
101 {
102 return ;
103 }
104
105 int n = _sock.Select2(
106 ( dir & LIBSSH2_SESSION_BLOCK_INBOUND ) != 0,
107 ( dir & LIBSSH2_SESSION_BLOCK_OUTBOUND ) != 0,
108 3 );
109
110 if ( n > 0 ) { return; }
111 }
112 }
113
114
115 #define WHILE_EAGAIN_(retvar, a) \
116 while (true) {\
117 retvar = a;\
118 if (retvar != LIBSSH2_ERROR_EAGAIN) break;\
119 WaitSocket(info);\
120 }
121
CheckSessionEagain()122 void FSSftp::CheckSessionEagain()
123 {
124 int e = libssh2_session_last_errno( this->sshSession );
125
126 if ( e != LIBSSH2_ERROR_EAGAIN ) { throw int( e - 1000 ); }
127 }
128
TransSftpError(int e,LIBSSH2_SFTP * sftp)129 inline int TransSftpError( int e, LIBSSH2_SFTP* sftp )
130 {
131 if ( e == LIBSSH2_ERROR_SFTP_PROTOCOL ) { e = libssh2_sftp_last_error( sftp ); }
132
133 return e < 0 ? e - 1000 : e;
134 }
135
CheckSFTPEagain()136 void FSSftp::CheckSFTPEagain()
137 {
138 int e = libssh2_session_last_errno( sshSession );
139
140 if ( e == LIBSSH2_ERROR_EAGAIN ) { return; }
141
142 throw TransSftpError( e, sftpSession );
143 }
144
CheckSFTP(int err)145 inline void FSSftp::CheckSFTP( int err )
146 {
147 if ( !err ) { return; }
148
149 throw TransSftpError( err, sftpSession );
150 }
151
152
CopyToStrZ(const char * s,int size)153 static std::vector<char> CopyToStrZ( const char* s, int size )
154 {
155 if ( size <= 0 ) { size = 0; }
156
157 std::vector<char> p( size + 1 );
158
159 if ( size > 0 ) { memcpy( p.data(), s, size ); }
160
161 p[size] = 0;
162 return p;
163 }
164
165 static Mutex kbdIntMutex; //NO lock in callback
166 static FSCInfo* volatile kbdIntInfo = 0;
167 static FSSftpParam* volatile kbdIntParam = 0;
168
KbIntCallback(const char * name,int name_len,const char * instruction,int instruction_len,int num_prompts,const LIBSSH2_USERAUTH_KBDINT_PROMPT * prompts,LIBSSH2_USERAUTH_KBDINT_RESPONSE * responses,void ** anstract)169 void KbIntCallback(
170 const char* name, int name_len,
171 const char* instruction, int instruction_len,
172 int num_prompts,
173 const LIBSSH2_USERAUTH_KBDINT_PROMPT* prompts,
174 LIBSSH2_USERAUTH_KBDINT_RESPONSE* responses,
175 void** anstract )
176 {
177 if ( num_prompts <= 0 ) { return; }
178
179 if ( !kbdIntInfo ) { return; }
180
181 try
182 {
183
184 std::vector<FSPromptData> pData( num_prompts );
185 int i;
186
187 for ( i = 0; i < num_prompts; i++ )
188 {
189 pData[i].visible = prompts[i].echo != 0;
190 pData[i].prompt = std::string( prompts[i].text, prompts[i].length );
191 }
192
193 if ( !kbdIntInfo->Prompt(
194 utf8_to_unicode( "SFTP" ).data(),
195 utf8str_to_unicode( kbdIntParam->user + "@" + kbdIntParam->server ).data(),
196 pData.data(), num_prompts ) )
197 {
198 return;
199 }
200
201 for ( i = 0; i < num_prompts; i++ )
202 {
203 std::string str = ( char* )FSString( pData[i].prompt.c_str() ).Get( kbdIntParam->charset );
204
205 if ( str.data() )
206 {
207 int l = strlen( str.data() );
208 responses[i].length = l;
209 responses[i].text = ( char* ) malloc( l + 1 );
210
211 if ( responses[i].text )
212 {
213 #if _MSC_VER > 1700
214 Lstrncpy( responses[i].text, l + 1, str.data(), _TRUNCATE );
215 #else
216 Lstrncpy( responses[i].text, str.data(), l + 1 );
217 #endif
218 }
219 }
220 }
221
222 }
223 catch ( cexception* ex )
224 {
225 fprintf( stderr, "exception in kbdint callback used with libssh2: %s\n", ex->message() );
226 ex->destroy();
227 }
228 catch ( ... )
229 {
230 fprintf( stderr, "excention (...) in kbdint callback used with libssh2\n" );
231 }
232 }
233
CheckSession(int * err,FSCInfo * info)234 int FSSftp::CheckSession( int* err, FSCInfo* info )
235 {
236
237 if ( sshSession ) { return 0; }
238
239 try
240 {
241
242 unsigned ip;
243 int e;
244
245 if ( !GetHostIp( _operParam.server.c_str(), &ip, &e ) )
246 {
247 throw int( e );
248 }
249
250 _sock.Create();
251 _sock.Connect( ntohl( ip ), _operParam.port );
252
253 sshSession = libssh2_session_init();
254
255 if ( !sshSession ) { throw int( SSH_INTERROR_X3 ); }
256
257 libssh2_session_set_blocking( sshSession, 0 );
258
259 WHILE_EAGAIN_( e, libssh2_session_handshake( sshSession, _sock.Id() ) );
260
261 if ( e ) { throw int( e - 1000 ); }
262
263 FSString userName = "";
264
265 if ( !_operParam.user.empty() )
266 {
267 userName = _operParam.user.c_str();
268 }
269 else
270 {
271 #ifndef _WIN32
272 char* ret = getenv( "LOGNAME" );
273
274 if ( ret )
275 {
276 userName = FSString( sys_charset_id, ret );
277 _operParam.user = userName.GetUtf8();
278
279 MutexLock infoLock( &infoMutex );
280 _infoParam.user = userName.GetUtf8();
281 }
282
283 #endif
284 };
285
286 char* authList = 0;
287
288 char* charUserName = ( char* )userName.Get( _operParam.charset );
289
290 while ( true )
291 {
292 authList = libssh2_userauth_list( sshSession, charUserName, strlen( charUserName ) );
293
294 if ( authList ) { break; }
295
296 CheckSessionEagain();
297 WaitSocket( info );
298 }
299
300 //publickey,password,keyboard-interactive
301 static const char passId[] = "password";
302 static const char publickey[] = "publickey";
303 static const char kInterId[] = "keyboard-interactive";
304
305 static unicode_t userSymbol[] = { '@', 0 };
306
307 int ret = 0;
308 #if _MSC_VER > 1700
309 char* next_tok = nullptr;
310 for ( char* authorizationMethod = strtok_s( authList, ",", &next_tok );
311 authorizationMethod != nullptr;
312 authorizationMethod = strtok_s( nullptr, ",", &next_tok )
313 )
314 #else
315 for ( char* authorizationMethod = strtok( authList, "," );
316 authorizationMethod != nullptr;
317 authorizationMethod = strtok( nullptr, "," ) )
318 #endif
319 {
320 if ( !strcmp( authorizationMethod, publickey ) )
321 {
322 FSString public_key;
323 FSString private_key;
324 if( !GetDefaultSshKeys(public_key, private_key) )
325 {
326 continue;
327 }
328
329 WHILE_EAGAIN_( ret, libssh2_userauth_publickey_fromfile ( sshSession, charUserName, public_key.GetUtf8(), private_key.GetUtf8(), "" ) );
330
331 if ( !ret )
332 {
333 fprintf(stderr, "You shouldn't use keys with an empty passphrase!\n");
334 break;
335 }
336
337 // TODO: prompt for key password. Copied from SO, didn't work:
338 // http://stackoverflow.com/questions/14952702/
339 // else if (ret == LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED)
340 // {
341 // // if we get here it means the public key was initially accepted
342 // // but the private key has a non-empty passphrase
343 // for (int i = 0; i < SSH_PASSWORD_ATTEMPTS; ++i)
344 // {
345 //
346 // FSPromptData data;
347 // data.visible = false;
348 // data.prompt = utf8_to_unicode( "Private key password:" ).data();
349 //
350 // if ( !info->Prompt(
351 // utf8_to_unicode( "SFTP_" ).data(),
352 // carray_cat<unicode_t>( userName.GetUnicode(), userSymbol, _operParam.server.Data() ).data(),
353 // &data, 1 ) ) { throw int( SSH_INTERROR_STOPPED ); }
354 //
355 // char* password = ( char* )FSString( data.prompt.Data() ).Get( _operParam.charset );
356 //
357 // ret = libssh2_userauth_publickey_fromfile( sshSession,
358 // charUserName, public_key.GetUtf8(), private_key.GetUtf8(), password );
359 // if ( ret != LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED ) break;
360 // }
361 // }
362
363 if ( ret )
364 {
365 // http://www.libssh2.org/libssh2_session_last_error.html
366 // Do I get it right that when want_buf==0 I don't need to release the buffer?
367 char* buf;
368 libssh2_session_last_error( sshSession, &buf, NULL, 0 );
369 fprintf( stderr, "Authentication using key failed: %s!\n", buf );
370 }
371 }
372 else if ( !strcmp( authorizationMethod, passId ) )
373 {
374 FSPromptData data;
375 data.visible = false;
376 data.prompt = "Password:";
377
378 if ( !info->Prompt(
379 utf8_to_unicode( "SFTP_" ).data(),
380 carray_cat<unicode_t>( userName.GetUnicode(), userSymbol, utf8str_to_unicode(_operParam.server).data() ).data(),
381 &data, 1 ) ) { throw int( SSH_INTERROR_STOPPED ); }
382
383 WHILE_EAGAIN_( ret, libssh2_userauth_password( sshSession,
384 ( char* )FSString( _operParam.user.c_str() ).Get( _operParam.charset ),
385 ( char* )FSString( data.prompt.c_str() ).Get( _operParam.charset ) ) );
386
387 if ( !ret ) { break; }
388 }
389 else if ( !strcmp( authorizationMethod, kInterId ) )
390 {
391 MutexLock lock( &kbdIntMutex );
392 kbdIntInfo = info;
393 kbdIntParam = &_operParam;
394
395 WHILE_EAGAIN_( ret,
396 libssh2_userauth_keyboard_interactive( sshSession,
397 ( char* )FSString( _operParam.user.c_str() ).Get( _operParam.charset ),
398 KbIntCallback )
399 );
400
401 if ( ret == 0 ) { break; }
402 }
403
404 };
405
406 if ( ret != 0 ) { throw int( ret - 1000 ); }
407
408 while ( true )
409 {
410 sftpSession = libssh2_sftp_init( sshSession );
411
412 if ( sftpSession ) { break; }
413
414 if ( !sftpSession )
415 {
416 int e = libssh2_session_last_errno( sshSession );
417
418 if ( e != LIBSSH2_ERROR_EAGAIN ) { throw int( e - 1000 ); }
419 }
420
421 WaitSocket( info );
422 }
423
424 return 0;
425
426 }
427 catch ( int e )
428 {
429 if ( err ) { *err = e; }
430
431 if ( sshSession ) { libssh2_session_free( sshSession ); }
432
433 sshSession = 0;
434 sftpSession = 0;
435 _sock.Close( false );
436 return ( e == -2 ) ? -2 : -1;
437 }
438
439 }
440
CloseSession()441 void FSSftp::CloseSession()
442 {
443 if ( sshSession ) { libssh2_session_free( sshSession ); }
444
445 sshSession = 0;
446 sftpSession = 0;
447
448 if ( _sock.IsValid() ) { _sock.Close( false ); }
449 }
450
451
Flags()452 unsigned FSSftp::Flags() { return HAVE_READ | HAVE_WRITE | HAVE_SYMLINK | HAVE_SEEK; }
453
IsEEXIST(int err)454 bool FSSftp::IsEEXIST( int err ) { return err == EEXIST; }
IsENOENT(int err)455 bool FSSftp::IsENOENT( int err ) { return err == ENOENT; }
IsEXDEV(int err)456 bool FSSftp::IsEXDEV( int err ) { return err == EXDEV; }
457
Equal(FS * fs)458 bool FSSftp::Equal( FS* fs )
459 {
460 if ( !fs || fs->Type() != FS::SFTP ) { return false; }
461
462 if ( fs == this ) { return true; }
463
464 FSSftp* f = ( FSSftp* )fs;
465
466 MutexLock l1( &infoMutex );
467 MutexLock l2( &( f->infoMutex ) );
468
469 if ( _infoParam.isSet != f->_infoParam.isSet )
470 {
471 return false;
472 }
473
474 return _infoParam.server == f->_infoParam.server &&
475 _infoParam.user == f->_infoParam.user &&
476 _infoParam.port == f->_infoParam.port &&
477 _infoParam.charset == f->_infoParam.charset;
478 }
479
480
481
StrError(int err)482 FSString FSSftp::StrError( int err )
483 {
484 const char* s = "";
485
486 if ( err < -500 )
487 {
488 switch ( err + 1000 )
489 {
490 case LIBSSH2_ERROR_SOCKET_NONE:
491 s = "LIBSSH2_ERROR_SOCKET_NONE";
492 break;
493
494 case LIBSSH2_ERROR_BANNER_RECV:
495 s = "LIBSSH2_ERROR_BANNER_RECV";
496 break;
497
498 case LIBSSH2_ERROR_BANNER_SEND:
499 s = "LIBSSH2_ERROR_BANNER_SEND";
500 break;
501
502 case LIBSSH2_ERROR_INVALID_MAC:
503 s = "LIBSSH2_ERROR_INVALID_MAC";
504 break;
505
506 case LIBSSH2_ERROR_KEX_FAILURE:
507 s = "LIBSSH2_ERROR_KEX_FAILURE";
508 break;
509
510 case LIBSSH2_ERROR_ALLOC:
511 s = "LIBSSH2_ERROR_ALLOC";
512 break;
513
514 case LIBSSH2_ERROR_SOCKET_SEND:
515 s = "LIBSSH2_ERROR_SOCKET_SEND";
516 break;
517
518 case LIBSSH2_ERROR_KEY_EXCHANGE_FAILURE:
519 s = "LIBSSH2_ERROR_KEY_EXCHANGE_FAILURE";
520 break;
521
522 case LIBSSH2_ERROR_TIMEOUT:
523 s = "LIBSSH2_ERROR_TIMEOUT";
524 break;
525
526 case LIBSSH2_ERROR_HOSTKEY_INIT:
527 s = "LIBSSH2_ERROR_HOSTKEY_INIT";
528 break;
529
530 case LIBSSH2_ERROR_HOSTKEY_SIGN:
531 s = "LIBSSH2_ERROR_HOSTKEY_SIGN";
532 break;
533
534 case LIBSSH2_ERROR_DECRYPT:
535 s = "LIBSSH2_ERROR_DECRYPT";
536 break;
537
538 case LIBSSH2_ERROR_SOCKET_DISCONNECT:
539 s = "LIBSSH2_ERROR_SOCKET_DISCONNECT";
540 break;
541
542 case LIBSSH2_ERROR_PROTO:
543 s = "LIBSSH2_ERROR_PROTO";
544 break;
545
546 case LIBSSH2_ERROR_PASSWORD_EXPIRED:
547 s = "LIBSSH2_ERROR_PASSWORD_EXPIRED";
548 break;
549
550 case LIBSSH2_ERROR_FILE:
551 s = "LIBSSH2_ERROR_FILE";
552 break;
553
554 case LIBSSH2_ERROR_METHOD_NONE:
555 s = "LIBSSH2_ERROR_METHOD_NONE";
556 break;
557
558 case LIBSSH2_ERROR_AUTHENTICATION_FAILED:
559 s = "Authentication failed";
560 break;
561
562 case LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED:
563 s = "LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED";
564 break;
565
566 case LIBSSH2_ERROR_CHANNEL_OUTOFORDER:
567 s = "LIBSSH2_ERROR_CHANNEL_OUTOFORDER";
568 break;
569
570 case LIBSSH2_ERROR_CHANNEL_FAILURE:
571 s = "LIBSSH2_ERROR_CHANNEL_FAILURE";
572 break;
573
574 case LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED:
575 s = "LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED";
576 break;
577
578 case LIBSSH2_ERROR_CHANNEL_UNKNOWN:
579 s = "LIBSSH2_ERROR_CHANNEL_UNKNOWN";
580 break;
581
582 case LIBSSH2_ERROR_CHANNEL_WINDOW_EXCEEDED:
583 s = "LIBSSH2_ERROR_CHANNEL_WINDOW_EXCEEDED";
584 break;
585
586 case LIBSSH2_ERROR_CHANNEL_PACKET_EXCEEDED:
587 s = "LIBSSH2_ERROR_CHANNEL_PACKET_EXCEEDED";
588 break;
589
590 case LIBSSH2_ERROR_CHANNEL_CLOSED:
591 s = "LIBSSH2_ERROR_CHANNEL_CLOSED";
592 break;
593
594 case LIBSSH2_ERROR_CHANNEL_EOF_SENT:
595 s = "LIBSSH2_ERROR_CHANNEL_EOF_SENT";
596 break;
597
598 case LIBSSH2_ERROR_SCP_PROTOCOL:
599 s = "LIBSSH2_ERROR_SCP_PROTOCOL";
600 break;
601
602 case LIBSSH2_ERROR_ZLIB:
603 s = "LIBSSH2_ERROR_ZLIB";
604 break;
605
606 case LIBSSH2_ERROR_SOCKET_TIMEOUT:
607 s = "LIBSSH2_ERROR_SOCKET_TIMEOUT";
608 break;
609
610 case LIBSSH2_ERROR_SFTP_PROTOCOL:
611 s = "LIBSSH2_ERROR_SFTP_PROTOCOL";
612 break;
613
614 case LIBSSH2_ERROR_REQUEST_DENIED:
615 s = "LIBSSH2_ERROR_REQUEST_DENIED";
616 break;
617
618 case LIBSSH2_ERROR_METHOD_NOT_SUPPORTED:
619 s = "LIBSSH2_ERROR_METHOD_NOT_SUPPORTED";
620 break;
621
622 case LIBSSH2_ERROR_INVAL:
623 s = "LIBSSH2_ERROR_INVAL";
624 break;
625
626 case LIBSSH2_ERROR_INVALID_POLL_TYPE:
627 s = "LIBSSH2_ERROR_INVALID_POLL_TYPE";
628 break;
629
630 case LIBSSH2_ERROR_PUBLICKEY_PROTOCOL:
631 s = "LIBSSH2_ERROR_PUBLICKEY_PROTOCOL";
632 break;
633
634 case LIBSSH2_ERROR_EAGAIN:
635 s = "LIBSSH2_ERROR_EAGAIN";
636 break;
637
638 case LIBSSH2_ERROR_BUFFER_TOO_SMALL:
639 s = "LIBSSH2_ERROR_BUFFER_TOO_SMALL";
640 break;
641
642 case LIBSSH2_ERROR_BAD_USE:
643 s = "LIBSSH2_ERROR_BAD_USE";
644 break;
645
646 case LIBSSH2_ERROR_COMPRESS:
647 s = "LIBSSH2_ERROR_COMPRESS";
648 break;
649
650 case LIBSSH2_ERROR_OUT_OF_BOUNDARY:
651 s = "LIBSSH2_ERROR_OUT_OF_BOUNDARY";
652 break;
653
654 case LIBSSH2_ERROR_AGENT_PROTOCOL:
655 s = "LIBSSH2_ERROR_AGENT_PROTOCOL";
656 break;
657
658 case LIBSSH2_ERROR_SOCKET_RECV:
659 s = "LIBSSH2_ERROR_SOCKET_RECV";
660 break;
661
662 case LIBSSH2_ERROR_ENCRYPT:
663 s = "LIBSSH2_ERROR_ENCRYPT";
664 break;
665
666 case LIBSSH2_ERROR_BAD_SOCKET:
667 s = "LIBSSH2_ERROR_BAD_SOCKET";
668 break;
669
670 default:
671 s = "LIBSSH2_ERROR_???";
672 break;
673 }
674
675 }
676 else
677 {
678 switch ( err )
679 {
680 case SSH_INTERROR_NOTSUPPORT:
681 s = "not supported operation";
682 break;
683
684 case SSH_INTERROR_X3:
685 s = "X3";
686 break;
687
688 case SSH_INTERROR_CONNECT:
689 s = "connection failed";
690 break;
691
692 case SSH_INTERROR_AUTH:
693 s = "authorization failed";
694 break;
695
696 case SSH_INTERROR_FATAL:
697 s = "fatal ssh error";
698 break;
699
700 case SSH_INTERROR_STOPPED:
701 s = "Process stopped";
702 break;
703
704 default:
705 if ( err >= 0 )
706 {
707 sys_char_t buf[0x100];
708 FSString str;
709 str.SetSys( sys_error_str( err, buf, 0x100 ) );
710 return str;
711 }
712
713 char buf[0x100];
714 Lsnprintf( buf, sizeof( buf ), "err : %i ???", err );
715 FSString str( CS_UTF8, buf );
716 return str;
717 }
718 }
719
720 return FSString( CS_UTF8, s );
721 }
722
OpenRead(FSPath & path,int flags,int * err,FSCInfo * info)723 int FSSftp::OpenRead ( FSPath& path, int flags, int* err, FSCInfo* info )
724 {
725 MutexLock lock( &mutex );
726 int ret = CheckSession( err, info );
727
728 if ( ret ) { return ret; }
729
730 int n = 0;
731
732 for ( ; n < MAX_FILES; n++ )
733 if ( !fileTable[n] ) { break; }
734
735 if ( n >= MAX_FILES )
736 {
737 if ( err ) { *err = SSH_INTERROR_OUTOF; }
738
739 return -1;
740 }
741
742 try
743 {
744 LIBSSH2_SFTP_HANDLE* fd = 0;
745
746 while ( true )
747 {
748 fd = libssh2_sftp_open( sftpSession, ( char* )path.GetString( _operParam.charset, '/' ),
749 LIBSSH2_FXF_READ,
750 0 );
751
752 if ( fd ) { break; }
753
754 CheckSFTPEagain();
755 WaitSocket( info );
756 }
757
758 fileTable[n] = fd;
759
760 }
761 catch ( int e )
762 {
763 if ( err ) { *err = e; }
764
765 return ( e == -2 ) ? -2 : -1;
766 }
767
768 return n;
769 }
770
OpenCreate(FSPath & path,bool overwrite,int mode,int flags,int * err,FSCInfo * info)771 int FSSftp::OpenCreate ( FSPath& path, bool overwrite, int mode, int flags, int* err, FSCInfo* info )
772 {
773 MutexLock lock( &mutex );
774 int ret = CheckSession( err, info );
775
776 if ( ret ) { return ret; }
777
778 int n = 0;
779
780 try
781 {
782 if ( !overwrite )
783 {
784 /* странная херня, при наличии файла с O_EXCL, выдает не EEXIST, а "прерван системный вызов"
785 поэтому встанил эту дурацкую проверку на наличие
786 */
787 LIBSSH2_SFTP_ATTRIBUTES attr;
788 int ret;
789 WHILE_EAGAIN_( ret, libssh2_sftp_lstat( sftpSession, ( char* )path.GetString( _operParam.charset, '/' ), &attr ) );
790
791 if ( !ret ) { if ( err ) { *err = EEXIST; } return -1; }
792 }
793
794 for ( n = 0; n < MAX_FILES; n++ )
795 if ( !fileTable[n] ) { break; }
796
797 if ( n >= MAX_FILES )
798 {
799 if ( err ) { *err = SSH_INTERROR_OUTOF; }
800
801 return -1;
802 }
803
804
805 LIBSSH2_SFTP_HANDLE* fd = 0;
806
807 while ( true )
808 {
809 fd = libssh2_sftp_open( sftpSession,
810 ( char* )path.GetString( _operParam.charset, '/' ),
811 LIBSSH2_FXF_CREAT | LIBSSH2_FXF_WRITE | ( overwrite ? LIBSSH2_FXF_TRUNC : LIBSSH2_FXF_EXCL ),
812 mode );
813
814 if ( fd ) { break; }
815
816 CheckSFTPEagain();
817 WaitSocket( info );
818 }
819
820 fileTable[n] = fd;
821
822 }
823 catch ( int e )
824 {
825 if ( err ) { *err = e; }
826
827 return ( e == -2 ) ? -2 : -1;
828 }
829
830 return n;
831 }
832
Close(int fd,int * err,FSCInfo * info)833 int FSSftp::Close ( int fd, int* err, FSCInfo* info )
834 {
835 MutexLock lock( &mutex );
836 int ret = CheckSession( err, info );
837
838 if ( ret ) { return ret; }
839
840 if ( fd < 0 || fd >= MAX_FILES || !fileTable[fd] )
841 {
842 if ( err ) { *err = EINVAL; }
843
844 return -1;
845 }
846
847 try
848 {
849 int ret;
850 WHILE_EAGAIN_( ret, libssh2_sftp_close( fileTable[fd] ) );
851 CheckSFTP( ret );
852
853 }
854 catch ( int e )
855 {
856 if ( err ) { *err = e; }
857
858 return ( e == -2 ) ? -2 : -1;
859 }
860
861 fileTable[fd] = 0;
862 return 0;
863 }
864
Read(int fd,void * buf,int size,int * err,FSCInfo * info)865 int FSSftp::Read ( int fd, void* buf, int size, int* err, FSCInfo* info )
866 {
867 MutexLock lock( &mutex );
868 int ret = CheckSession( err, info );
869
870 if ( ret ) { return ret; }
871
872 if ( fd < 0 || fd >= MAX_FILES || !fileTable[fd] )
873 {
874 if ( err ) { *err = EINVAL; }
875
876 return -1;
877 }
878
879 try
880 {
881 int bytes;
882 WHILE_EAGAIN_( bytes, libssh2_sftp_read( fileTable[fd], ( char* )buf, size ) );
883
884 if ( bytes < 0 ) { CheckSFTP( bytes ); }
885
886 return bytes;
887 }
888 catch ( int e )
889 {
890 if ( err ) { *err = e; }
891
892 return ( e == -2 ) ? -2 : -1;
893 }
894 }
895
896
Write(int fd,void * buf,int size,int * err,FSCInfo * info)897 int FSSftp::Write ( int fd, void* buf, int size, int* err, FSCInfo* info )
898 {
899 MutexLock lock( &mutex );
900 int ret = CheckSession( err, info );
901
902 if ( ret ) { return ret; }
903
904 if ( fd < 0 || fd >= MAX_FILES || !fileTable[fd] )
905 {
906 if ( err ) { *err = EINVAL; }
907
908 return -1;
909 }
910
911 try
912 {
913 int bytes = 0;
914 char* s = ( char* )buf;
915
916 while ( size > 0 )
917 {
918 int ret;
919 WHILE_EAGAIN_( ret, libssh2_sftp_write( fileTable[fd], s, size ) );
920
921 if ( ret < 0 ) { CheckSFTP( ret ); }
922
923 if ( !ret ) { break; }
924
925 bytes += ret;
926 size -= ret;
927 s += ret;
928 }
929
930 return bytes;
931 }
932 catch ( int e )
933 {
934 if ( err ) { *err = e; }
935
936 return ( e == -2 ) ? -2 : -1;
937 }
938 }
939
Seek(int fd,SEEK_FILE_MODE mode,seek_t pos,seek_t * pRet,int * err,FSCInfo * info)940 int FSSftp::Seek( int fd, SEEK_FILE_MODE mode, seek_t pos, seek_t* pRet, int* err, FSCInfo* info )
941 {
942 MutexLock lock( &mutex );
943 int ret = CheckSession( err, info );
944
945 if ( ret ) { return ret; }
946
947 if ( fd < 0 || fd >= MAX_FILES || !fileTable[fd] )
948 {
949 if ( err ) { *err = EINVAL; }
950
951 return -1;
952 }
953
954 //???
955 if ( mode == FSEEK_BEGIN )
956 {
957 libssh2_sftp_seek64( fileTable[fd], pos );
958
959 if ( pRet ) { *pRet = pos; }
960
961 return 0;
962 }
963
964 if ( err )
965 {
966 *err = EINVAL;
967 }
968
969 return -1;
970 }
971
972
Rename(FSPath & oldpath,FSPath & newpath,int * err,FSCInfo * info)973 int FSSftp::Rename ( FSPath& oldpath, FSPath& newpath, int* err, FSCInfo* info )
974 {
975 MutexLock lock( &mutex );
976 int ret = CheckSession( err, info );
977
978 if ( ret ) { return ret; }
979
980 try
981 {
982 int ret;
983 WHILE_EAGAIN_( ret, libssh2_sftp_rename( sftpSession, ( char* ) oldpath.GetString( _operParam.charset, '/' ), ( char* ) newpath.GetString( _operParam.charset, '/' ) ) );
984 CheckSFTP( ret );
985 }
986 catch ( int e )
987 {
988
989 if ( err ) { *err = e; }
990
991 return ( e == -2 ) ? -2 : -1;
992 }
993
994 return 0;
995 }
996
997
MkDir(FSPath & path,int mode,int * err,FSCInfo * info)998 int FSSftp::MkDir ( FSPath& path, int mode, int* err, FSCInfo* info )
999 {
1000 MutexLock lock( &mutex );
1001 int ret = CheckSession( err, info );
1002
1003 if ( ret ) { return ret; }
1004
1005 try
1006 {
1007 int ret;
1008 WHILE_EAGAIN_( ret, libssh2_sftp_mkdir( sftpSession, ( char* )path.GetString( _operParam.charset, '/' ), mode ) );
1009 CheckSFTP( ret );
1010 }
1011 catch ( int e )
1012 {
1013 if ( err ) { *err = e; }
1014
1015 return ( e == -2 ) ? -2 : -1;
1016 }
1017
1018 return 0;
1019 }
1020
Delete(FSPath & path,int * err,FSCInfo * info)1021 int FSSftp::Delete ( FSPath& path, int* err, FSCInfo* info )
1022 {
1023 MutexLock lock( &mutex );
1024 int ret = CheckSession( err, info );
1025
1026 if ( ret ) { return ret; }
1027
1028 try
1029 {
1030 int ret;
1031 WHILE_EAGAIN_( ret, libssh2_sftp_unlink( sftpSession, ( char* )path.GetString( _operParam.charset, '/' ) ) );
1032 CheckSFTP( ret );
1033 }
1034 catch ( int e )
1035 {
1036 if ( err ) { *err = e; }
1037
1038 return ( e == -2 ) ? -2 : -1;
1039 }
1040
1041 return 0;
1042 }
1043
RmDir(FSPath & path,int * err,FSCInfo * info)1044 int FSSftp::RmDir ( FSPath& path, int* err, FSCInfo* info )
1045 {
1046 MutexLock lock( &mutex );
1047 int ret = CheckSession( err, info );
1048
1049 if ( ret ) { return ret; }
1050
1051 try
1052 {
1053 int ret;
1054 WHILE_EAGAIN_( ret, libssh2_sftp_rmdir( sftpSession, ( char* )path.GetString( _operParam.charset, '/' ) ) );
1055 CheckSFTP( ret );
1056 }
1057 catch ( int e )
1058 {
1059 if ( err ) { *err = e; }
1060
1061 return ( e == -2 ) ? -2 : -1;
1062 }
1063
1064 return 0;
1065 }
1066
SetFileTime(FSPath & path,FSTime cTime,FSTime aTime,FSTime mTime,int * err,FSCInfo * info)1067 int FSSftp::SetFileTime ( FSPath& path, FSTime cTime, FSTime aTime, FSTime mTime, int* err, FSCInfo* info )
1068 {
1069 MutexLock lock( &mutex );
1070 int ret = CheckSession( err, info );
1071
1072 if ( ret ) { return ret; }
1073
1074 LIBSSH2_SFTP_ATTRIBUTES attr;
1075 attr.flags = LIBSSH2_SFTP_ATTR_ACMODTIME;
1076 attr.atime = ( unsigned long )aTime;
1077 attr.mtime = ( unsigned long )mTime;
1078
1079 try
1080 {
1081 int ret;
1082 WHILE_EAGAIN_( ret, libssh2_sftp_setstat( sftpSession, ( char* )path.GetString( _operParam.charset, '/' ), &attr ) );
1083 CheckSFTP( ret );
1084 }
1085 catch ( int e )
1086 {
1087 if ( err ) { *err = e; }
1088
1089 return ( e == -2 ) ? -2 : -1;
1090 }
1091
1092 return 0;
1093 }
1094
CloseHandle(LIBSSH2_SFTP_HANDLE * h,FSCInfo * info)1095 void FSSftp::CloseHandle( LIBSSH2_SFTP_HANDLE* h, FSCInfo* info )
1096 {
1097 int ret;
1098 WHILE_EAGAIN_( ret, libssh2_sftp_close_handle( h ) );
1099
1100 if ( ret ) { throw int( ret - 1000 ); }
1101 }
1102
1103 struct SftpAttr
1104 {
1105 LIBSSH2_SFTP_ATTRIBUTES attr;
PermissionsSftpAttr1106 int Permissions() const { return ( attr.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS ) ? attr.permissions : 0; }
SizeSftpAttr1107 long long Size() const { return ( attr.flags & LIBSSH2_SFTP_ATTR_SIZE ) ? attr.filesize : 0; }
UidSftpAttr1108 int Uid() const { return ( attr.flags & LIBSSH2_SFTP_ATTR_UIDGID ) ? attr.uid : 0; }
GidSftpAttr1109 int Gid() const { return ( attr.flags & LIBSSH2_SFTP_ATTR_UIDGID ) ? attr.gid : 0; }
MTimeSftpAttr1110 time_t MTime() const { return ( attr.flags & LIBSSH2_SFTP_ATTR_ACMODTIME ) ? attr.mtime : 0; }
ATimeSftpAttr1111 time_t ATime() const { return ( attr.flags & LIBSSH2_SFTP_ATTR_ACMODTIME ) ? attr.atime : 0; }
IsLinkSftpAttr1112 bool IsLink() const { return ( attr.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS ) != 0 && ( attr.permissions & S_IFMT ) == S_IFLNK; }
1113 };
1114
1115
ReadDir(FSList * list,FSPath & path,int * err,FSCInfo * info)1116 int FSSftp::ReadDir ( FSList* list, FSPath& path, int* err, FSCInfo* info )
1117 {
1118 MutexLock lock( &mutex );
1119 int ret = CheckSession( err, info );
1120
1121 if ( ret ) { return ret; }
1122
1123 if ( !list ) { return 0; }
1124
1125 list->Clear();
1126
1127 try
1128 {
1129
1130 LIBSSH2_SFTP_HANDLE* dir = 0;
1131
1132 try
1133 {
1134
1135 while ( true )
1136 {
1137 dir = libssh2_sftp_opendir( sftpSession, ( char* )path.GetString( _operParam.charset, '/' ) );
1138
1139 if ( dir ) { break; }
1140
1141 CheckSFTPEagain();
1142 WaitSocket( info );
1143 }
1144
1145 while ( true )
1146 {
1147 char buf[4096];
1148 int len = 0;
1149 SftpAttr attr;
1150
1151
1152 WHILE_EAGAIN_( len, libssh2_sftp_readdir( dir, buf, sizeof( buf ) - 1, &attr.attr ) );
1153
1154 if ( len < 0 ) { CheckSFTP( len ); }
1155
1156 if ( len == 0 ) { break; }
1157
1158 if ( buf[0] == '.' && ( !buf[1] || ( buf[1] == '.' && !buf[2] ) ) )
1159 {
1160 continue;
1161 }
1162
1163 clPtr<FSNode> pNode = new FSNode();
1164
1165 #if defined(__APPLE__)
1166 if ( _operParam.charset == CS_UTF8 )
1167 {
1168 std::string normname = normalize_utf8_NFC( buf );
1169 pNode->name.Set( _operParam.charset, normname.data() );
1170 }
1171 else
1172 {
1173 pNode->name.Set( _operParam.charset, buf );
1174 }
1175 #else
1176 pNode->name.Set( _operParam.charset, buf );
1177 #endif
1178
1179 if ( attr.IsLink() )
1180 {
1181 FSPath pt = path;
1182 pt.Push( _operParam.charset, buf );
1183 char* fullPath = ( char* )pt.GetString( _operParam.charset, '/' );
1184
1185 WHILE_EAGAIN_( len, libssh2_sftp_readlink( sftpSession, fullPath, buf, sizeof( buf ) - 1 ) );
1186
1187 if ( len < 0 ) { CheckSFTP( len ); }
1188
1189 pNode->st.link.Set( _operParam.charset, buf );
1190
1191 int ret;
1192 WHILE_EAGAIN_( ret, libssh2_sftp_stat( sftpSession, fullPath, &attr.attr ) );
1193 }
1194
1195 pNode->st.mode = attr.Permissions();
1196 pNode->st.size = attr.Size();
1197 pNode->st.uid = attr.Uid();
1198 pNode->st.gid = attr.Gid();
1199 pNode->st.m_CreationTime = 0;
1200 pNode->st.m_LastAccessTime = attr.ATime();
1201 pNode->st.m_LastWriteTime = attr.MTime();
1202 pNode->st.m_ChangeTime = attr.MTime();
1203
1204 list->Append( pNode );
1205 }
1206
1207 }
1208 catch ( ... )
1209 {
1210 if ( dir ) { CloseHandle( dir, info ); }
1211
1212 throw;
1213 }
1214
1215 if ( dir ) { CloseHandle( dir, info ); }
1216
1217 }
1218 catch ( int e )
1219 {
1220 if ( err ) { *err = e; }
1221
1222 return ( e == -2 ) ? -2 : -1;
1223 }
1224
1225 return 0;
1226 }
1227
1228
Stat(FSPath & path,FSStat * st,int * err,FSCInfo * info)1229 int FSSftp::Stat ( FSPath& path, FSStat* st, int* err, FSCInfo* info )
1230 {
1231 MutexLock lock( &mutex );
1232 int ret = CheckSession( err, info );
1233
1234 if ( ret ) { return ret; }
1235
1236 char* fullPath = ( char* ) path.GetString( _operParam.charset, '/' );
1237
1238 try
1239 {
1240 SftpAttr attr;
1241 int ret;
1242 WHILE_EAGAIN_( ret, libssh2_sftp_lstat( sftpSession, fullPath, &attr.attr ) );
1243 CheckSFTP( ret );
1244
1245 if ( attr.IsLink() )
1246 {
1247 char buf[4096];
1248 int len;
1249
1250 WHILE_EAGAIN_( len, libssh2_sftp_readlink( sftpSession, fullPath, buf, sizeof( buf ) ) );
1251
1252 if ( len < 0 ) { CheckSFTP( len ); };
1253
1254 st->link.Set( _operParam.charset, buf );
1255
1256 int ret;
1257
1258 WHILE_EAGAIN_( ret, libssh2_sftp_stat( sftpSession, fullPath, &attr.attr ) );
1259
1260 if ( ret ) { attr.attr.permissions = 0; }
1261 }
1262
1263 st->mode = attr.Permissions();
1264 st->size = attr.Size();
1265 st->uid = attr.Uid();
1266 st->gid = attr.Gid();
1267 st->m_CreationTime = 0;
1268 st->m_LastAccessTime = attr.ATime();
1269 st->m_LastWriteTime = attr.MTime();
1270 st->m_ChangeTime = attr.MTime();
1271 }
1272 catch ( int e )
1273 {
1274 st->mode = 0;
1275
1276 if ( err ) { *err = e; }
1277
1278 return ( e == -2 ) ? -2 : -1;
1279 }
1280
1281 return 0;
1282 }
1283
FStat(int fd,FSStat * st,int * err,FSCInfo * info)1284 int FSSftp::FStat ( int fd, FSStat* st, int* err, FSCInfo* info )
1285 {
1286 MutexLock lock( &mutex );
1287 int ret = CheckSession( err, info );
1288
1289 if ( ret ) { return ret; }
1290
1291 if ( fd < 0 || fd >= MAX_FILES || !fileTable[fd] )
1292 {
1293 if ( err ) { *err = EINVAL; }
1294
1295 return -1;
1296 }
1297
1298 try
1299 {
1300 SftpAttr attr;
1301 int ret;
1302 WHILE_EAGAIN_( ret, libssh2_sftp_fstat( fileTable[fd], &attr.attr ) );
1303 CheckSFTP( ret );
1304
1305 st->mode = attr.Permissions();
1306 st->size = attr.Size();
1307 st->uid = attr.Uid();
1308 st->gid = attr.Gid();
1309 st->m_LastAccessTime = attr.ATime();
1310 st->m_LastWriteTime = attr.MTime();
1311 st->m_ChangeTime = attr.MTime();
1312 }
1313 catch ( int e )
1314 {
1315 st->mode = 0;
1316
1317 if ( err ) { *err = e; }
1318
1319 return ( e == -2 ) ? -2 : -1;
1320 }
1321
1322 return 0;
1323
1324
1325 }
1326
Symlink(FSPath & path,FSString & str,int * err,FSCInfo * info)1327 int FSSftp::Symlink ( FSPath& path, FSString& str, int* err, FSCInfo* info )
1328 {
1329 MutexLock lock( &mutex );
1330 int ret = CheckSession( err, info );
1331
1332 if ( ret ) { return ret; }
1333
1334 try
1335 {
1336 int ret;
1337 WHILE_EAGAIN_( ret, libssh2_sftp_symlink( sftpSession, ( char* )str.Get( _operParam.charset ), ( char* )path.GetString( _operParam.charset, '/' ) ) );
1338 CheckSFTP( ret );
1339 }
1340 catch ( int e )
1341 {
1342 if ( err ) { *err = e; }
1343
1344 return ( e == -2 ) ? -2 : -1;
1345 }
1346
1347 return 0;
1348 }
1349
StatVfs(FSPath & path,FSStatVfs * vst,int * err,FSCInfo * info)1350 int FSSftp::StatVfs( FSPath& path, FSStatVfs* vst, int* err, FSCInfo* info )
1351 {
1352 vst->size = 0;
1353 vst->avail = 0;
1354
1355 if ( err ) { *err = 0; }
1356
1357 return 0;
1358
1359 ///////////////////// отключено
1360 ///////////////////// зависает (libssh2_sftp_statvfs постоянно возвращает LIBSSH2_ERROR_EAGAIN)
1361 /*
1362 printf( "FSSftp::StatVfs 1 \n" );
1363 MutexLock lock( &mutex );
1364 int ret = CheckSession( err, info );
1365 if ( ret ) return ret;
1366 printf( "FSSftp::StatVfs 2 \n" );
1367 char *fullPath = ( char* )path.GetString( _operParam.charset, '/' );
1368
1369 try {
1370 printf( "FSSftp::StatVfs 3 \n" );
1371 struct _LIBSSH2_SFTP_STATVFS st;
1372 int ret;
1373 WHILE_EAGAIN_COUNT( ret, libssh2_sftp_statvfs( sftpSession, fullPath, strlen( fullPath ), &st ), 2 );
1374 if ( ret == LIBSSH2_ERROR_EAGAIN )
1375 throw( int( 0 ) );
1376 printf( "FSSftp::StatVfs 4 \n" );
1377 CheckSFTP( ret );
1378 printf( "FSSftp::StatVfs 5 \n" );
1379 vst->size = int64_t( st.f_blocks ) * st.f_frsize;
1380 vst->avail = int64_t( st.f_bavail ) * st.f_bsize;
1381
1382 }
1383 catch ( int e ) {
1384 vst->size = 0;
1385 vst->avail = 0;
1386 if ( err ) *err = e;
1387 return ( e == -2 ) ? -2 : -1;
1388 }
1389 return 0;
1390 */
1391 }
1392
Uri(FSPath & path)1393 FSString FSSftp::Uri( FSPath& path )
1394 {
1395 MutexLock lock( &infoMutex ); //infoMutex!!!
1396
1397 std::string a;
1398
1399 char port[0x100];
1400 Lsnprintf( port, sizeof( port ), ":%i", _infoParam.port );
1401
1402 a = std::string( "sftp://" ) + _infoParam.user + "@" + _infoParam.server + std::string(port) + path.GetUtf8( '/' );
1403
1404 return FSString( CS_UTF8, a.data() );
1405 }
1406
1407
~FSSftp()1408 FSSftp::~FSSftp()
1409 {
1410 CloseSession();
1411 }
1412
1413 #endif
1414
1415