1 module libssh.channel; 2 3 import core.time; 4 import std.algorithm.searching; 5 import std.algorithm.mutation; 6 7 import libssh.c_bindings.libssh; 8 import libssh.c_bindings.server; 9 import libssh.c_bindings.callbacks; 10 import libssh.c_bindings.ctypes; 11 import libssh.errors; 12 import libssh.utils; 13 import libssh.session; 14 15 class SSHChannelSet { 16 this() { 17 } 18 19 void add(SSHChannel channel) { 20 if (!this._channels.canFind(channel)) { 21 this._channels ~= channel; 22 } 23 } 24 25 void remove(SSHChannel channel) { 26 this._channels = std.algorithm.mutation.remove!(a => a == channel)(this._channels); 27 } 28 29 bool isSet(SSHChannel channel) { 30 return this._settedChannels.canFind(channel._channel); 31 } 32 33 void reset() { 34 this._settedChannels = []; 35 } 36 37 private { 38 SSHChannel[] _channels = []; 39 ssh_channel[] _settedChannels = []; 40 } 41 } 42 43 class SSHChannel : Disposable { 44 enum PollEof = int.min; 45 enum ReadAgain = int.min; 46 enum WriteAgain = int.min; 47 48 alias OnDataCallback = uint delegate(SSHChannel channel, void[] data, bool isStdErr); 49 alias OnPtyRequestCallback = bool delegate(SSHChannel channel, string term, int width, 50 int height, int pxWidth, int pxHeight); 51 alias OnShellRequestCallback = bool delegate(SSHChannel channel); 52 alias OnPtyWindowChangeRequestCallback = bool delegate(SSHChannel channel, int width, 53 int height, int pxWidth, int pxHeight); 54 alias OnExecRequestCallback = bool delegate(SSHChannel channel, string command); 55 alias OnEnvRequestCallback = bool delegate(SSHChannel channel, string name, string value); 56 alias OnSubsystemRequestCallback = bool delegate(SSHChannel channel, string subsystem); 57 alias OnEOFCallback = void delegate(SSHChannel channel); 58 alias OnCloseCallback = void delegate(SSHChannel channel); 59 alias OnSignalCallback = void delegate(SSHChannel channel, string signal); // TODO: maybe signal should be enum? 60 alias OnExitStatusCallback = void delegate(SSHChannel channel, int exitStatus); 61 alias OnExitSignalCallback = void delegate(SSHChannel channel, string signal, bool isCoreDump, 62 string errMsg, string lang); // TODO: maybe signal should be enum? 63 alias OnAuthAgentRequestCallback = void delegate(SSHChannel channel); 64 alias OnX11RequestCallback = void delegate(SSHChannel channel, bool isSingleConnection, 65 string authProtocol, string authCookie, uint screenNumber); 66 67 @property onEOFCallback() { 68 return this._onEOFCallback; 69 } 70 71 @property void onEOFCallback(OnEOFCallback cb) { 72 this._onEOFCallback = cb; 73 if (cb is null) { 74 this._callbacks.channel_eof_function = null; 75 } else { 76 this._callbacks.channel_eof_function = &nativeOnEOFCallback; 77 } 78 ssh_set_channel_callbacks(this._channel, &this._callbacks); 79 } 80 81 @property onCloseCallback() { 82 return this._onCloseCallback; 83 } 84 85 @property void onCloseCallback(OnCloseCallback cb) { 86 this._onCloseCallback = cb; 87 if (cb is null) { 88 this._callbacks.channel_close_function = null; 89 } else { 90 this._callbacks.channel_close_function = &nativeOnCloseCallback; 91 } 92 ssh_set_channel_callbacks(this._channel, &this._callbacks); 93 } 94 95 @property onSignalCallback() { 96 return this._onSignalCallback; 97 } 98 99 @property void onSignalCallback(OnSignalCallback cb) { 100 this._onSignalCallback = cb; 101 if (cb is null) { 102 this._callbacks.channel_signal_function = null; 103 } else { 104 this._callbacks.channel_signal_function = &nativeOnSignalCallback; 105 } 106 ssh_set_channel_callbacks(this._channel, &this._callbacks); 107 } 108 109 @property onExitStatusCallback() { 110 return this._onExitStatusCallback; 111 } 112 113 @property void onExitStatusCallback(OnExitStatusCallback cb) { 114 this._onExitStatusCallback = cb; 115 if (cb is null) { 116 this._callbacks.channel_exit_status_function = null; 117 } else { 118 this._callbacks.channel_exit_status_function = &nativeOnExitStatusCallback; 119 } 120 ssh_set_channel_callbacks(this._channel, &this._callbacks); 121 } 122 123 @property onExitSignalCallback() { 124 return this._onExitSignalCallback; 125 } 126 127 @property void onExitSignalCallback(OnExitSignalCallback cb) { 128 this._onExitSignalCallback = cb; 129 if (cb is null) { 130 this._callbacks.channel_exit_signal_function = null; 131 } else { 132 this._callbacks.channel_exit_signal_function = &nativeOnExitSignalCallback; 133 } 134 ssh_set_channel_callbacks(this._channel, &this._callbacks); 135 } 136 137 @property onAuthAgentRequestCallback() { 138 return this._onAuthAgentRequestCallback; 139 } 140 141 @property void onAuthAgentRequestCallback(OnAuthAgentRequestCallback cb) { 142 this._onAuthAgentRequestCallback = cb; 143 if (cb is null) { 144 this._callbacks.channel_auth_agent_req_function = null; 145 } else { 146 this._callbacks.channel_auth_agent_req_function = &nativeOnAuthAgentRequestCallback; 147 } 148 ssh_set_channel_callbacks(this._channel, &this._callbacks); 149 } 150 151 @property onX11RequestCallback() { 152 return this._onX11RequestCallback; 153 } 154 155 @property void onX11RequestCallback(OnX11RequestCallback cb) { 156 this._onX11RequestCallback = cb; 157 if (cb is null) { 158 this._callbacks.channel_x11_req_function = null; 159 } else { 160 this._callbacks.channel_x11_req_function = &nativeOnX11tRequestCallback; 161 } 162 ssh_set_channel_callbacks(this._channel, &this._callbacks); 163 } 164 165 @property OnDataCallback onDataCallback() { 166 return this._onDataCallback; 167 } 168 169 @property void onDataCallback(OnDataCallback cb) { 170 this._onDataCallback = cb; 171 if (cb is null) { 172 this._callbacks.channel_data_function = null; 173 } else { 174 this._callbacks.channel_data_function = &nativeOnData; 175 } 176 ssh_set_channel_callbacks(this._channel, &this._callbacks); 177 } 178 179 @property OnPtyRequestCallback onPtyRequestCallback() { 180 return this._onPtyRequestCallback; 181 } 182 183 @property void onPtyRequestCallback(OnPtyRequestCallback cb) { 184 this._onPtyRequestCallback = cb; 185 if (cb is null) { 186 this._callbacks.channel_pty_request_function = null; 187 } else { 188 this._callbacks.channel_pty_request_function = &nativeOnPtyRequestCallback; 189 } 190 ssh_set_channel_callbacks(this._channel, &this._callbacks); 191 } 192 193 @property OnShellRequestCallback onShellRequestCallback() { 194 return this._onShellRequestCallback; 195 } 196 197 @property void onShellRequestCallback(OnShellRequestCallback cb) { 198 this._onShellRequestCallback = cb; 199 if (cb is null) { 200 this._callbacks.channel_shell_request_function = null; 201 } else { 202 this._callbacks.channel_shell_request_function = &nativeOnShellRequestCallback; 203 } 204 ssh_set_channel_callbacks(this._channel, &this._callbacks); 205 } 206 207 @property OnPtyWindowChangeRequestCallback onPtyWindowChangeRequestCallback() { 208 return this._onPtyWindowChangeRequestCallback; 209 } 210 211 @property void onPtyWindowChangeRequestCallback(OnPtyWindowChangeRequestCallback cb) { 212 this._onPtyWindowChangeRequestCallback = cb; 213 if (cb is null) { 214 this._callbacks.channel_pty_window_change_function = null; 215 } else { 216 this._callbacks.channel_pty_window_change_function = &nativeOnPtyWindowChangeRequestCallback; 217 } 218 ssh_set_channel_callbacks(this._channel, &this._callbacks); 219 } 220 221 @property OnExecRequestCallback onExecRequestCallback() { 222 return this._onExecRequestCallback; 223 } 224 225 @property void onExecRequestCallback(OnExecRequestCallback cb) { 226 this._onExecRequestCallback = cb; 227 if (cb is null) { 228 this._callbacks.channel_exec_request_function = null; 229 } else { 230 this._callbacks.channel_exec_request_function = &nativeOnExecRequestCallback; 231 } 232 ssh_set_channel_callbacks(this._channel, &this._callbacks); 233 } 234 235 @property OnEnvRequestCallback onEnvRequestCallback() { 236 return this._onEnvRequestCallback; 237 } 238 239 @property void onEnvRequestCallback(OnEnvRequestCallback cb) { 240 this._onEnvRequestCallback = cb; 241 if (cb is null) { 242 this._callbacks.channel_env_request_function = null; 243 } else { 244 this._callbacks.channel_env_request_function = &nativeOnEnvRequestCallback; 245 } 246 ssh_set_channel_callbacks(this._channel, &this._callbacks); 247 } 248 249 @property OnSubsystemRequestCallback onSubsystemRequestCallback() { 250 return this._onSubsystemRequestCallback; 251 } 252 253 @property void onSubsystemRequestCallback(OnSubsystemRequestCallback cb) { 254 this._onSubsystemRequestCallback = cb; 255 if (cb is null) { 256 this._callbacks.channel_subsystem_request_function = null; 257 } else { 258 this._callbacks.channel_subsystem_request_function = &nativeOnSubsystemRequestCallback; 259 } 260 ssh_set_channel_callbacks(this._channel, &this._callbacks); 261 } 262 263 @property SSHSession parent() { 264 return this._parent; 265 } 266 267 @property int exitStatus() { 268 return ssh_channel_get_exit_status(this._channel); 269 } 270 271 @property bool isClosed() { 272 return ssh_channel_is_closed(this._channel) == 0 ? false : true; 273 } 274 275 @property bool isEof() { 276 return ssh_channel_is_eof(this._channel) == 0 ? false : true; 277 } 278 279 @property bool isOpen() { 280 return ssh_channel_is_open(this._channel) == 0 ? false : true; 281 } 282 283 @property void blocking(bool v) { 284 ssh_channel_set_blocking(this._channel, v ? 1 : 0); 285 } 286 287 /** 288 * returns false if in nonblocking mode and call has to be done again. 289 **/ 290 bool openSession() { 291 auto rc = ssh_channel_open_session(this._channel); 292 if (rc == SSH_AGAIN) { 293 return false; 294 } 295 if (rc != SSH_OK) { 296 throw new SSHException(this._parent._session); 297 } 298 return true; 299 } 300 301 /** 302 * returns false if in nonblocking mode and call has to be done again. 303 **/ 304 bool requestExec(string cmd) { 305 auto rc = ssh_channel_request_exec(this._channel, toStrZ(cmd)); 306 if (rc == SSH_AGAIN) { 307 return false; 308 } 309 if (rc != SSH_OK) { 310 throw new SSHException(this._parent._session); 311 } 312 return true; 313 } 314 315 /** 316 * returns false if in nonblocking mode and call has to be done again. 317 **/ 318 bool requestPty() { 319 auto rc = ssh_channel_request_pty(this._channel); 320 if (rc == SSH_AGAIN) { 321 return false; 322 } 323 if (rc != SSH_OK) { 324 throw new SSHException(this._parent._session); 325 } 326 return true; 327 } 328 329 /** 330 * returns false if in nonblocking mode and call has to be done again. 331 **/ 332 bool requestPtySize(string terminalType = "vt100", int width = 80, int height = 25) { 333 auto rc = ssh_channel_request_pty_size(this._channel, toStrZ(terminalType), width, height); 334 if (rc == SSH_AGAIN) { 335 return false; 336 } 337 if (rc != SSH_OK) { 338 throw new SSHException(this._parent._session); 339 } 340 return true; 341 } 342 343 /** 344 * returns false if in nonblocking mode and call has to be done again. 345 **/ 346 bool requestShell() { 347 auto rc = ssh_channel_request_shell(this._channel); 348 if (rc == SSH_AGAIN) { 349 return false; 350 } 351 if (rc != SSH_OK) { 352 throw new SSHException(this._parent._session); 353 } 354 return true; 355 } 356 357 /** 358 * returns false if in nonblocking mode and call has to be done again. 359 **/ 360 bool requestSubsystem(string subsystem) { 361 auto rc = ssh_channel_request_subsystem(this._channel, toStrZ(subsystem)); 362 if (rc == SSH_AGAIN) { 363 return false; 364 } 365 if (rc != SSH_OK) { 366 throw new SSHException(this._parent._session); 367 } 368 return true; 369 } 370 371 /** 372 * returns false if in nonblocking mode and call has to be done again. 373 **/ 374 bool requestX11(bool singleConnection, string protocol, string cookie, 375 int screenNumber) { 376 auto rc = ssh_channel_request_x11(this._channel, singleConnection ? 1 : 0, 377 toStrZ(protocol), toStrZ(cookie), screenNumber); 378 if (rc == SSH_AGAIN) { 379 return false; 380 } 381 if (rc != SSH_OK) { 382 throw new SSHException(this._parent._session); 383 } 384 return true; 385 } 386 387 // TODO: maybe signal should be enum? 388 void sendSignal(string signal) { 389 auto rc = ssh_channel_request_send_signal(this._channel, toStrZ(signal)); 390 if (rc != SSH_OK) { 391 throw new SSHException(this._parent._session); 392 } 393 } 394 395 void sendEof() { 396 auto rc = ssh_channel_send_eof(this._channel); 397 if (rc != SSH_OK) { 398 throw new SSHException(this._parent._session); 399 } 400 } 401 402 // TODO: maybe signal should be enum? 403 void sendExitSignal(string signal, bool core, string errMsg, string lang) { 404 auto rc = ssh_channel_request_send_exit_signal(this._channel, toStrZ(signal), 405 core ? 1 : 0, toStrZ(errMsg), toStrZ(lang)); 406 if (rc != SSH_OK) { 407 throw new SSHException(this._parent._session); 408 } 409 } 410 411 void sendExitStatus(int exitStatus) { 412 auto rc = ssh_channel_request_send_exit_status(this._channel, exitStatus); 413 if (rc != SSH_OK) { 414 throw new SSHException(this._parent._session); 415 } 416 } 417 418 /** 419 * returns SSHChannel.PollEof if EOF 420 **/ 421 int poll(bool isStdErr) { 422 auto rc = ssh_channel_poll(this._channel, isStdErr ? 1 : 0); 423 if (rc == SSH_EOF) { 424 return SSHChannel.PollEof; 425 } 426 if (rc == SSH_ERROR) { 427 throw new SSHException(this._parent._session); 428 } 429 return rc; 430 } 431 432 /** 433 * returns SSHChannel.PollEof if EOF 434 **/ 435 int pollTimeout(int timeout, bool isStdErr) { 436 auto rc = ssh_channel_poll_timeout(this._channel, timeout, isStdErr ? 1 : 0); 437 if (rc == SSH_EOF) { 438 return -1; 439 } 440 if (rc == SSH_ERROR) { 441 throw new SSHException(this._parent._session); 442 } 443 return rc; 444 } 445 446 /** 447 * returns SSHChannel.ReadAgain in nonblocking mode 448 **/ 449 int read(void[] dest, bool isStdErr) { 450 auto rc = ssh_channel_read(this._channel, dest.ptr, cast(uint) dest.length, 451 isStdErr ? 1 : 0); 452 if (rc == SSH_ERROR) { 453 throw new SSHException(this._parent._session); 454 } 455 if (rc == SSH_AGAIN) { 456 return SSHChannel.ReadAgain; 457 } 458 return rc; 459 } 460 461 /** 462 * returns SSHChannel.ReadAgain in nonblocking mode 463 **/ 464 int readNonBlocking(void[] dest, bool isStdErr) { 465 auto rc = ssh_channel_read_nonblocking(this._channel, dest.ptr, cast(uint) dest.length, 466 isStdErr ? 1 : 0); 467 if (rc == SSH_ERROR) { 468 throw new SSHException(this._parent._session); 469 } 470 if (rc == SSH_AGAIN) { 471 return SSHChannel.ReadAgain; 472 } 473 return rc; 474 } 475 476 /** 477 * returns SSHChannel.ReadAgain in nonblocking mode 478 **/ 479 int readTimeout(void[] dest, bool isStdErr, int timeout) { 480 auto rc = ssh_channel_read_timeout(this._channel, dest.ptr, cast(uint) dest.length, 481 isStdErr ? 1 : 0, timeout); 482 if (rc == SSH_ERROR) { 483 throw new SSHException(this._parent._session); 484 } 485 if (rc == SSH_AGAIN) { 486 return SSHChannel.ReadAgain; 487 } 488 return rc; 489 } 490 491 /** 492 * returns SSHChannel.WriteAgain in nonblocking mode 493 **/ 494 int write(const void[] src) { 495 auto rc = ssh_channel_write(this._channel, src.ptr, cast(uint) src.length); 496 if (rc == SSH_ERROR) { 497 throw new SSHException(this._parent._session); 498 } 499 if (rc == SSH_AGAIN) { 500 return SSHChannel.WriteAgain; 501 } 502 return rc; 503 } 504 505 /** 506 * returns SSHChannel.WriteAgain in nonblocking mode 507 **/ 508 int writeStdErr(const void[] src) { 509 auto rc = ssh_channel_write_stderr(this._channel, src.ptr, cast(uint) src.length); 510 if (rc == SSH_ERROR) { 511 throw new SSHException(this._parent._session); 512 } 513 if (rc == SSH_AGAIN) { 514 return SSHChannel.WriteAgain; 515 } 516 return rc; 517 } 518 519 /** 520 * returns false if in nonblocking mode and call has to be done again. 521 **/ 522 bool setEnv(string name, string value) { 523 auto rc = ssh_channel_request_env(this._channel, toStrZ(name), toStrZ(value)); 524 if (rc == SSH_AGAIN) { 525 return false; 526 } 527 if (rc != SSH_OK) { 528 throw new SSHException(this._parent._session); 529 } 530 return true; 531 } 532 533 void changePtySize(int width, int height) { 534 auto rc = ssh_channel_change_pty_size(this._channel, width, height); 535 if (rc != SSH_OK) { 536 throw new SSHException(this._parent._session); 537 } 538 } 539 540 SSHChannel acceptX11(int timeoutMs) { 541 auto result = ssh_channel_accept_x11(this._channel, timeoutMs); 542 if (result is null) { 543 return null; 544 } 545 return new SSHChannel(this._parent, result); 546 } 547 548 /** 549 * return false if in nonblocking mode and call has to be done again. 550 * */ 551 bool openAuthAgent() { 552 auto rc = ssh_channel_open_auth_agent(this._channel); 553 if (rc == SSH_AGAIN) { 554 return false; 555 } 556 if (rc != SSH_OK) { 557 throw new SSHException(this._parent._session); 558 } 559 return true; 560 } 561 562 /** 563 * return false if in nonblocking mode and call has to be done again. 564 * */ 565 bool openForward(string remoteHost, ushort remotePort, string srcHost, 566 ushort localPort) { 567 assert(remoteHost !is null); 568 auto rc = ssh_channel_open_forward(this._channel, toStrZ(remoteHost), remotePort, 569 toStrZ(srcHost), localPort); 570 if (rc == SSH_AGAIN) { 571 return false; 572 } 573 if (rc != SSH_OK) { 574 throw new SSHException(this._parent._session); 575 } 576 return true; 577 } 578 579 /** 580 * return false if in nonblocking mode and call has to be done again. 581 * */ 582 bool openReverseForward(string remoteHost, ushort remotePort, string srcHost, 583 ushort localPort) { 584 assert(remoteHost !is null); 585 auto rc = ssh_channel_open_reverse_forward(this._channel, toStrZ(remoteHost), remotePort, 586 toStrZ(srcHost), localPort); 587 if (rc == SSH_AGAIN) { 588 return false; 589 } 590 if (rc != SSH_OK) { 591 throw new SSHException(this._parent._session); 592 } 593 return true; 594 } 595 596 /** 597 * returns false if in nonblocking mode and call has to be done again. 598 **/ 599 bool openX11(string origAddr, ushort origPort) { 600 auto rc = ssh_channel_open_x11(this._channel, toStrZ(origAddr), origPort); 601 if (rc == SSH_AGAIN) { 602 return false; 603 } 604 if (rc != SSH_OK) { 605 throw new SSHException(this._parent._session); 606 } 607 return true; 608 } 609 610 611 void close() { 612 auto rc = ssh_channel_close(this._channel); 613 if (rc != SSH_OK) { 614 throw new SSHException(this._parent._session); 615 } 616 } 617 618 override void dispose() { 619 this._dispose(false); 620 } 621 622 /** 623 * return false if the select(2) syscall was interrupted, then relaunch the function. 624 * */ 625 static bool select(SSHChannelSet readChans, SSHChannelSet writeChans, 626 SSHChannelSet exceptChans, Duration timeout) { 627 628 timeval timeoutVal; 629 timeout.split!("seconds", "usecs")(timeoutVal.tv_sec, timeoutVal.tv_usec); 630 631 ssh_channel[] forRead = null; 632 ssh_channel[] forWrite = null; 633 ssh_channel[] forExcept = null; 634 635 if (readChans !is null && readChans._channels.length > 0) { 636 forRead = new ssh_channel[readChans._channels.length + 1]; 637 for (auto i = 0; i < readChans._channels.length; i++) { 638 forRead[i] = readChans._channels[i]._channel; 639 } 640 forRead[readChans._channels.length] = null; 641 } 642 643 if (writeChans !is null && writeChans._channels.length > 0) { 644 forWrite = new ssh_channel[writeChans._channels.length + 1]; 645 for (auto i = 0; i < writeChans._channels.length; i++) { 646 forWrite[i] = writeChans._channels[i]._channel; 647 } 648 forWrite[writeChans._channels.length] = null; 649 } 650 651 if (exceptChans !is null && exceptChans._channels.length > 0) { 652 forExcept = new ssh_channel[exceptChans._channels.length + 1]; 653 for (auto i = 0; i < exceptChans._channels.length; i++) { 654 forExcept[i] = exceptChans._channels[i]._channel; 655 } 656 forExcept[exceptChans._channels.length] = null; 657 } 658 659 ssh_channel* forReadPtr = forRead !is null ? forRead.ptr : null; 660 ssh_channel* forWritePtr = forWrite !is null ? forWrite.ptr : null; 661 ssh_channel* forExceptPtr = forExcept !is null ? forExcept.ptr : null; 662 663 auto rc = ssh_channel_select(forReadPtr, forWritePtr, forExceptPtr, &timeoutVal); 664 665 if (rc == ssh_error_types_e.SSH_EINTR || rc == SSH_AGAIN) { 666 return false; 667 } 668 checkForRCError(rc, rc); 669 670 size_t i = 0; 671 if (forRead !is null) { 672 while (forRead[i] !is null) { 673 readChans._settedChannels ~= forRead[i]; 674 i += 1; 675 } 676 } 677 678 if (forWrite !is null) { 679 i = 0; 680 while (forWrite[i] !is null) { 681 writeChans._settedChannels ~= forWrite[i]; 682 i += 1; 683 } 684 } 685 686 if (forExcept !is null) { 687 i = 0; 688 while (forExcept[i] !is null) { 689 exceptChans._settedChannels ~= forExcept[i]; 690 i += 1; 691 } 692 } 693 694 return true; 695 } 696 697 ~this() { 698 this._dispose(true); 699 } 700 701 package { 702 this(SSHSession parent, ssh_channel channel) { 703 this._parent = parent; 704 this._channel = channel; 705 parent.registerChannel(this); 706 707 ssh_callbacks_init(this._callbacks); 708 this._callbacks.userdata = cast(void*) this; 709 } 710 711 ssh_channel _channel; 712 } 713 714 private { 715 SSHSession _parent; // Need to GC not deleted session before any user code stopped using of this object 716 717 ssh_channel_callbacks_struct _callbacks; 718 719 OnEOFCallback _onEOFCallback; 720 OnCloseCallback _onCloseCallback; 721 OnSignalCallback _onSignalCallback; 722 OnExitStatusCallback _onExitStatusCallback; 723 OnExitSignalCallback _onExitSignalCallback; 724 OnAuthAgentRequestCallback _onAuthAgentRequestCallback; 725 OnX11RequestCallback _onX11RequestCallback; 726 OnDataCallback _onDataCallback; 727 OnPtyRequestCallback _onPtyRequestCallback; 728 OnShellRequestCallback _onShellRequestCallback; 729 OnPtyWindowChangeRequestCallback _onPtyWindowChangeRequestCallback; 730 OnExecRequestCallback _onExecRequestCallback; 731 OnEnvRequestCallback _onEnvRequestCallback; 732 OnSubsystemRequestCallback _onSubsystemRequestCallback; 733 734 void _dispose(bool fromDtor) { 735 if (this._channel !is null) { 736 ssh_channel_free(this._channel); 737 738 if (!fromDtor) { 739 this._parent.freeChannel(this); 740 } 741 742 this._channel = null; 743 this._parent = null; 744 } 745 } 746 } 747 } 748 749 private { 750 extern(C) int nativeOnData(ssh_session session, ssh_channel channel, void *data, uint len, 751 int is_stderr, void* userdata) { 752 auto channelObj = cast(SSHChannel) userdata; 753 754 if (channelObj._onDataCallback is null) { 755 return SSH_ERROR; 756 } 757 758 try { 759 return channelObj._onDataCallback(channelObj, data[0 .. len], 760 is_stderr == 0 ? false : true); 761 } catch (Exception) { 762 return SSH_ERROR; 763 } 764 } 765 766 extern(C) void nativeOnEOFCallback(ssh_session session, ssh_channel channel, void* userdata) { 767 auto channelObj = cast(SSHChannel) userdata; 768 769 if (channelObj is null || channelObj._onEOFCallback is null) { 770 return; 771 } 772 773 channelObj._onEOFCallback(channelObj); 774 } 775 776 extern(C) void nativeOnCloseCallback(ssh_session session, ssh_channel channel, void* userdata) { 777 auto channelObj = cast(SSHChannel) userdata; 778 779 if (channelObj is null || channelObj._onCloseCallback is null) { 780 return; 781 } 782 783 channelObj._onCloseCallback(channelObj); 784 } 785 786 extern(C) void nativeOnSignalCallback(ssh_session session, ssh_channel channel, const char* signal, 787 void* userdata) { 788 auto channelObj = cast(SSHChannel) userdata; 789 790 if (channelObj is null || channelObj._onSignalCallback is null) { 791 return; 792 } 793 794 channelObj._onSignalCallback(channelObj, fromStrZ(signal)); 795 } 796 797 extern(C) void nativeOnExitStatusCallback(ssh_session session, ssh_channel channel, int exit_status, 798 void* userdata) { 799 auto channelObj = cast(SSHChannel) userdata; 800 801 if (channelObj is null || channelObj._onExitStatusCallback is null) { 802 return; 803 } 804 805 channelObj._onExitStatusCallback(channelObj, exit_status); 806 } 807 808 extern(C) void nativeOnExitSignalCallback(ssh_session session, ssh_channel channel, const char* signal, 809 int core, const char* errmsg, const char* lang, void* userdata) { 810 auto channelObj = cast(SSHChannel) userdata; 811 812 if (channelObj is null || channelObj._onExitSignalCallback is null) { 813 return; 814 } 815 816 channelObj._onExitSignalCallback(channelObj, fromStrZ(signal), core == 0 ? false : true, 817 fromStrZ(errmsg), fromStrZ(lang)); 818 } 819 820 extern(C) int nativeOnPtyRequestCallback(ssh_session session, ssh_channel channel, 821 const char* term, int width, int height, int pxwidth, int pxheight, void *userdata) { 822 auto channelObj = cast(SSHChannel) userdata; 823 824 if (channelObj._onPtyRequestCallback is null) { 825 return -1; 826 } 827 828 try { 829 auto result = channelObj._onPtyRequestCallback(channelObj, fromStrZ(term), width, 830 height, pxwidth, pxheight); 831 return result ? 0 : -1; 832 } catch(Exception) { 833 return -1; 834 } 835 } 836 837 extern(C) int nativeOnShellRequestCallback(ssh_session session, ssh_channel channel, 838 void *userdata) { 839 auto channelObj = cast(SSHChannel) userdata; 840 841 if (channelObj._onShellRequestCallback is null) { 842 return 1; 843 } 844 845 try { 846 auto result = channelObj._onShellRequestCallback(channelObj); 847 return result ? 0 : 1; 848 } catch(Exception) { 849 return 1; 850 } 851 } 852 853 extern(C) void nativeOnAuthAgentRequestCallback(ssh_session session, ssh_channel channel, 854 void* userdata) { 855 auto channelObj = cast(SSHChannel) userdata; 856 857 if (channelObj is null || channelObj._onAuthAgentRequestCallback is null) { 858 return; 859 } 860 861 channelObj._onAuthAgentRequestCallback(channelObj); 862 } 863 864 extern(C) void nativeOnX11tRequestCallback(ssh_session session, ssh_channel channel, 865 int single_connection, const char *auth_protocol, const char *auth_cookie, 866 uint screen_number, void* userdata) { 867 auto channelObj = cast(SSHChannel) userdata; 868 869 if (channelObj is null || channelObj._onX11RequestCallback is null) { 870 return; 871 } 872 873 channelObj._onX11RequestCallback(channelObj, single_connection == 0 ? false : true, 874 fromStrZ(auth_protocol), fromStrZ(auth_cookie), screen_number); 875 } 876 877 extern(C) int nativeOnPtyWindowChangeRequestCallback(ssh_session session, ssh_channel channel, 878 int width, int height, int pxwidth, int pxheight, void *userdata) { 879 auto channelObj = cast(SSHChannel) userdata; 880 881 if (channelObj._onPtyWindowChangeRequestCallback is null) { 882 return -1; 883 } 884 885 try { 886 auto result = channelObj._onPtyWindowChangeRequestCallback(channelObj, width, height, 887 pxwidth, pxheight); 888 return result ? 0 : -1; 889 } catch(Exception) { 890 return -1; 891 } 892 } 893 894 extern(C) int nativeOnExecRequestCallback(ssh_session session, ssh_channel channel, 895 const char* command, void* userdata) { 896 auto channelObj = cast(SSHChannel) userdata; 897 898 if (channelObj._onExecRequestCallback is null) { 899 return 1; 900 } 901 902 try { 903 auto result = channelObj._onExecRequestCallback(channelObj, fromStrZ(command)); 904 return result ? 0 : 1; 905 } catch(Exception) { 906 return -1; 907 } 908 } 909 910 extern(C) int nativeOnEnvRequestCallback(ssh_session session, ssh_channel channel, 911 const char* env_name, const char* env_value, void* userdata) { 912 auto channelObj = cast(SSHChannel) userdata; 913 914 if (channelObj._onEnvRequestCallback is null) { 915 return 1; 916 } 917 918 try { 919 auto result = channelObj._onEnvRequestCallback(channelObj, fromStrZ(env_name), 920 fromStrZ(env_value)); 921 return result ? 0 : 1; 922 } catch (Exception) { 923 return 1; 924 } 925 } 926 927 extern(C) int nativeOnSubsystemRequestCallback(ssh_session session, ssh_channel channel, 928 const char* subsystem, void* userdata) { 929 auto channelObj = cast(SSHChannel) userdata; 930 931 if (channelObj._onSubsystemRequestCallback is null) { 932 return 1; 933 } 934 935 try { 936 auto result = channelObj._onSubsystemRequestCallback(channelObj, fromStrZ(subsystem)); 937 return result ? 0 : 1; 938 } catch (Exception) { 939 return 1; 940 } 941 } 942 }