      * Example: print a remote ascii file using sftp
      *
     h DFTACTGRP(*NO) ACTGRP(*NEW)
     h OPTION(*NOSHOWCPY)
     h BNDDIR('LIBSSH2')
     h BNDDIR('QC2LE')
      *
      * Copyright (C) The libssh2 project and its contributors.
      *
      * Usage:
      *
      * CALL SFTPXMPLE ('<host>' '<port>' '<user>' '<password>' '<filepath>')
      *
      * SPDX-License-Identifier: BSD-3-Clause
      *
     fQPRINT    o    f  120        printer
      *
      /include LIBSSH2RPG,SSH2_SFTP
      /include LIBSSH2RPG,SSH2_CCSID
      *
     d                 pi
     d host                         120
     d port                           5
     d user                          20
     d password                     120
     d filepath                     120
      *
      **************************************************************************
      *                          External definitions
      **************************************************************************
      *
     d atoi            pr            10i 0 extproc('atoi')
     d  numstr                         *   value options(*string)
      *
     d inet_addr       pr            10u 0 extproc('inet_addr')
     d  char_addr                      *   value options(*string)
      *
     d socket          pr            10i 0 extproc('socket')
     d  domain                       10i 0 value
     d  type                         10i 0 value
     d  protocol                     10i 0 value
      *
     d AF_INET         c                   2
     d SOCK_STREAM     c                   1
     d IPPROTO_IP      c                   0
      *
     d connect         pr            10i 0 extproc('connect')
     d  sockfd                       10i 0 value
     d  addr                           *   value
     d  addrlen                      10u 0 value
      *
     d sockaddr_in     ds                  based(######typedef######)
     d                                     align qualified
     d  sin_family                    5i 0
     d  sin_port                      5i 0
     d  sin_addr                     10u 0
     d  sin_zero                      8
      *
     d shutdown        pr            10i 0 extproc('shutdown')
     d  socket                       10i 0 value
     d  how                          10i 0 value
      *
     d SHUT_RDWR       c                   2
      *
     d qmhsndpm        pr                  extpgm('QMHSNDPM')
     d  msgid                         7    const
     d  qmsgfn                       20    const
     d  data                     999999    const options(*varsize)
     d  datalength                   10u 0 const
     d  msgtype                      10    const
     d  csentry                  999999    const options(*varsize)
     d  cscounter                    10u 0 const
     d  msgkey                        4
     d  errcode                  999999    options(*varsize)
     d  csentrylen                   10u 0 const options(*nopass)
     d  csqual                       20    const options(*nopass)
     d  waittime                     10u 0 const options(*nopass)
     d  csentrytype                  10    const options(*nopass)
     d  ccsid                        10u 0 const options(*nopass)
      *
      **************************************************************************
      *                               Constants
      **************************************************************************
      *
     d EBCDIC_CR       c                   X'0D'
     d EBCDIC_LF       c                   X'25'
      *
      **************************************************************************
      *                            Global storage
      **************************************************************************
      *
     d sc              s               *   inz(*NULL)                           String cache
     d session         s               *   inz(*NULL)                           Session
     d sftp_session    s               *   inz(*NULL)                           LIBSSH2_SFTP *
     d sftp_handle     s               *   inz(*NULL)                           LIBSSH2_SFTP_HANDLE*
     d sin             ds                  likeds(sockaddr_in)                  Remote IP address
     d sock            s             10i 0 inz(LIBSSH2_INVALID_SOCKET)          Socket descriptor
     d rc              s             10i 0                                      Result code
     d hostlen         s             10u 0                                      Host name length
      *
      **************************************************************************
      *                            Main program
      **************************************************************************

        // Initialize ssh lbrary
        rc = libssh2_init(0);
        if rc <> 0;
            error('libssh2 initialization failed (' + %trim(%char(rc)) + ')');
        else;
            // Build remote address
            sin.sin_family = AF_INET;
            hostlen = trimmed_length(host: %len(host): %addr(port));
            if hostlen <> 0;
                 sin.sin_addr = inet_addr(%subst(host: 1: hostlen));
            else;
                sin.sin_addr = inet_addr('127.0.0.1');
            endif;
            sin.sin_port = atoi(port);
            if sin.sin_port <= 0;
                sin.sin_port = 22;
            endif;
            sin.sin_zero = *ALLX'00';

            main();
            cleanout();
        endif;

        *inlr = *on;
      /space 3
        begsr *pssr;
            cleanout();
        endsr;
      /eject
      **************************************************************************
      * Main procedure
      **************************************************************************
      *
     p main            b
      *
     d buf             s           1024                                         Data buffer
     d nread           s             10i 0                                      Read bytes count

        // Connect to remote server
        sock = socket(AF_INET: SOCK_STREAM: IPPROTO_IP);
        if sock = LIBSSH2_INVALID_SOCKET;
            error('failed to create socket');
            return;
        endif;
        if connect(sock: %addr(sin): %size(sin)) <> 0;
            error('failed to connect');
            return;
        endif;

        // Create a session instance
        session = libssh2_session_init();
        if session = *NULL;
            error('Could not initialize SSH session');
            return;
        endif;

        // Since we have set non-blocking, tell libssh2 we are blocking
        libssh2_session_set_blocking(session: 1);

        // ... start it up. This will trade welcome banners, exchange keys,
        // and setup crypto, compression, and MAC layers
        rc = libssh2_session_handshake(session: sock);
        if rc <> 0;
            error('Failure establishing SSH session: ' + %trim(%char(rc)));
            return;
        endif;

        // Authenticate
        if libssh2_userauth_password(session:
          libssh2_from_ccsid(session: sc: 0: user:
                             trimmed_length(user: %size(user):
                                            %addr(password)): *omit):
          libssh2_from_ccsid(session: sc: 0: password:
                             trimmed_length(password: %size(password):
                                            %addr(filepath)): *omit)) <> 0;
            error('Authentication by password failed');
            return;
        endif;

        // Request a file via SFTP
        sftp_session = libssh2_sftp_init(session);

        if sftp_session = *NULL;
            error('Unable to init SFTP session');
            return;
        endif;

        sftp_handle = libssh2_sftp_open(sftp_session:
          libssh2_from_ccsid(session: sc: 0: filepath:
                             trimmed_length(filepath: %size(filepath): *null):
                                            *omit): LIBSSH2_FXF_READ: 0);
        if sftp_handle = *NULL;
            error('Unable to open file with SFTP: ' +
                  %trim(%char(libssh2_sftp_last_error(sftp_session))));
            return;
        endif;

        // Download and display the remote file
        nread = libssh2_sftp_read(sftp_handle: %addr(buf): %size(buf));
        dow nread > 0;  // loop until we fail
            print(libssh2_to_ccsid(session: sc: 0: %addr(buf): nread: *omit):
                  -1);
            libssh2_release_string_cache(session: sc);
            nread = libssh2_sftp_read(sftp_handle: %addr(buf): %size(buf));
        enddo;
     p main            e
      /eject
      **************************************************************************
      * Release all allocated resources
      **************************************************************************
      *
     p cleanout        b
      *

        if sftp_handle <> *NULL;
            libssh2_sftp_close(sftp_handle);
        endif;

        if sftp_session <> *NULL;
            libssh2_sftp_shutdown(sftp_session);
        endif;

        if session <> *NULL;
            libssh2_session_disconnect(session: libssh2_from_ccsid(session: sc:
              0: 'Normal shutdown': -1: *omit));
            libssh2_release_string_cache(session: sc);
            libssh2_session_free(session);
        endif;

        if sock <> LIBSSH2_INVALID_SOCKET;
                shutdown(sock: SHUT_RDWR);
                LIBSSH2_SOCKET_CLOSE(sock);
        endif;

        libssh2_exit();
     p cleanout        e
      /eject
      **************************************************************************
      * Print data line by line
      **************************************************************************
      *
     p print           b
     d                 pi
     d  string                         *   value options(*string)
     d  len                          10i 0 value
      *
     d recout          ds                                                       Output line buffer
     d  lineout                     120    inz(*blanks)
      *
     d i               s             10u 0
     d j               s             10u 0 inz(0)

        if len < 0;
            len = %len(%str(string));
        endif;

        for i = 0 to len - 1;
            if %str(string + i: 1) <> EBCDIC_CR;
                if %str(string + i: 1) = EBCDIC_LF;
                    write QPRINT recout;
                    lineout = *blanks;
                    j = 0;
                else;
                    if j >= %size(lineout);
                        write QPRINT recout;
                        lineout = *blanks;
                        j = 0;
                    endif;
                    j = j + 1;
                    %subst(lineout: j: 1) = %str(string + i: 1);
                endif;
            endif;
        endfor;
        if j > 0;
            write QPRINT recout;
        endif;
     p print           e
      /eject
      **************************************************************************
      * Error procedure
      **************************************************************************
      *
     p error           b
     d                 pi
     d  message                        *   value options(*string)
      *
     d errcode         ds                  qualified
     d  provided                     10u 0 inz(0)
     d  available                    10u 0
      *
     d msgkey          s              4

        // Send error as an exception to the calling level.
        qmhsndpm('CPF9898': 'QCPFMSG   QSYS':
                 %str(message): %len(%str(message)): '*ESCAPE':
                 '*          ': 1: msgkey: errcode);
     p error           e
      /eject
      **************************************************************************
      * Get the length of right-trimmed string
      **************************************************************************
      *
     p trimmed_length  b
     d                 pi            10u 0
     d  string                   999999    options(*varsize)
     d  length                       10u 0 value
     d  nextarg                        *   value
      *
     d len             s             10u 0

        if nextarg <> *null;
            len = nextarg - %addr(string);
            if length > len;
                length = len;
            endif;
        endif;
        len = %scan(X'00': string: 1: length); // Maybe zero-terminated
        if len = 0;
            len = length + 1;
        endif;
        return %checkr(' ': string: len - 1);  // Trim right
     p trimmed_length  e
