Net-SSH2

 view release on metacpan or  search on metacpan

SSH2.xs  view on Meta::CPAN

#else

void
net_ss_block_directions(SSH2* ss)
CODE:
    croak("libssh2 version 1.0 or higher required for block_directions support");

#endif

#if LIBSSH2_VERSION_NUM >= 0x010209

SV *
net_ss_timeout(SSH2* ss, SV *timeout = &PL_sv_undef)
PREINIT:
    long r;
CODE:
    if (items > 1)
        libssh2_session_set_timeout(ss->session,
                                    (SvOK(timeout) ? SvUV(timeout) : 0));
    r = libssh2_session_get_timeout(ss->session);
    RETVAL = (r > 0 ? newSVuv(r) : &PL_sv_undef);
OUTPUT:
    RETVAL

#else

void
net_ss_timeout(SSH2* ss, long timeout)
CODE:
    croak("libssh2 version 1.2.9 or higher required for set_timeout support");

#endif

SSH2_BOOL
net_ss_blocking(SSH2* ss, SSH2_BOOL blocking = 0)
CODE:
    if (items > 1)
        libssh2_session_set_blocking(ss->session, blocking);
    RETVAL = libssh2_session_get_blocking(ss->session);
OUTPUT:
    RETVAL

void
net_ss_DESTROY(SSH2* ss)
CODE:
    debug("%s::DESTROY object 0x%x\n", class, ss);
    libssh2_session_free(ss->session);
    if (ss->socket)
        SvREFCNT_dec(ss->socket);
    if (ss->hostname)
        SvREFCNT_dec(ss->hostname);
    Safefree(ss);

void
net_ss_debug(SV*, IV debug)
CODE:
    net_ss_debug_out = debug & 1;  /* allow for future flags */

void
net_ss_version(...)
PPCODE:
    EXTEND(SP, 3);
    ST(0) = sv_2mortal(newSVpv(LIBSSH2_VERSION, 0));
    if (GIMME_V != G_ARRAY)
        XSRETURN(1);
#ifdef LIBSSH2_VERSION_NUM
    ST(1) = sv_2mortal(newSVuv(LIBSSH2_VERSION_NUM));
#else
    ST(1) = &PL_sv_undef;
#endif
    ST(2) = sv_2mortal(newSVpv(LIBSSH2_SSH_DEFAULT_BANNER, 0));
    XSRETURN(3);

SSH2_NERROR
net_ss_banner(SSH2* ss, SSH2_CHARP banner)
PREINIT:
    SV* full_banner;
CODE:
    full_banner = sv_2mortal(newSVpvf("SSH-2.0-%s", banner));
    RETVAL = libssh2_banner_set(ss->session, SvPVbyte_nolen(full_banner));
    save_eagain(ss->session, RETVAL);
OUTPUT:
    RETVAL

SSH2_ERROR
net_ss_error(SSH2* ss)
PREINIT:
    char* errstr;
    int errlen;
CODE:
    RETVAL = libssh2_session_last_error(ss->session, &errstr, &errlen, 0);
    if(GIMME_V == G_ARRAY) {
        SV *errcode_sv;
        if (RETVAL == LIBSSH2_ERROR_NONE)
            XSRETURN_EMPTY;
        EXTEND(SP, 3);
        ST(0) = sv_2mortal(newSViv(RETVAL));
        if ((-RETVAL > 0) && (-RETVAL < countof(xs_libssh2_error)))
            errcode_sv = newSVpvf("LIBSSH2_ERROR_%s", xs_libssh2_error[-RETVAL]);
        else
            errcode_sv = newSVpvf("LIBSSH2_ERROR_UNKNOWN(%d)", RETVAL);
        ST(1) = sv_2mortal(errcode_sv);
        ST(2) = (errstr ? sv_2mortal(newSVpvn(errstr, errlen)) : &PL_sv_undef);
        XSRETURN(3);
    }
OUTPUT:
    RETVAL

void
net_ss__set_error(SSH2 *ss, int errcode = 0, SSH2_CHARP_OR_NULL errmsg = NULL)
CODE:
    libssh2_session_set_last_error(ss->session, errcode, errmsg);

SSH2_NERROR
net_ss__method(SSH2* ss, SSH2_METHOD type, SSH2_CHARP_OR_NULL prefs = NULL)
CODE:
    /* if there are no other parameters, return the current value */
    if (items == 2) {
        const char *method = libssh2_session_methods(ss->session, (int)type);
        if (!method)
            XSRETURN_EMPTY;

SSH2.xs  view on Meta::CPAN

    ss->sv_ss = SvRV(ST(0));  /* don't keep a reference, just store it */
    SvREFCNT_dec(ss->rgsv_cb[type]);
    libssh2_session_callback_set(ss->session,
     type, callback ? cb_as_void_ptr(msg_cb[type]) : NULL);
    SvREFCNT_inc(callback);
    ss->rgsv_cb[type] = callback;
    RETVAL = 1;
OUTPUT:
    RETVAL

SSH2_NERROR
net_ss__startup(SSH2* ss, int fd, SV *socket, SV* hostname, int port)
CODE:
    RETVAL = libssh2_session_startup(ss->session, fd);
    if ((RETVAL >= 0) && SvOK(socket)) {
        if (ss->socket)
            sv_2mortal(ss->socket);
        ss->socket = newSVsv(socket);
        ss->hostname = newSVsv(hostname);
        ss->port = port;
    }
    save_eagain(ss->session, RETVAL);
OUTPUT:
    RETVAL

SV *
net_ss_hostname(SSH2* ss)
CODE:
    RETVAL = (ss->hostname ? newSVsv(ss->hostname) : &PL_sv_undef);
OUTPUT:
    RETVAL

int
net_ss_port(SSH2* ss)
CODE:
    RETVAL = ss->port;
 OUTPUT:
    RETVAL

SV *
net_ss_sock(SSH2* ss)
CODE:
    RETVAL = (ss->socket ? newSVsv(ss->socket) : &PL_sv_undef);
OUTPUT:
    RETVAL

SSH2_NERROR
net_ss_disconnect(SSH2* ss, SSH2_CHARP description = "",       \
                  int reason = SSH_DISCONNECT_BY_APPLICATION, SSH2_CHARP lang = "")
CODE:
    RETVAL = libssh2_session_disconnect_ex(ss->session, reason, description, lang);
    save_eagain(ss->session, RETVAL);
OUTPUT:
    RETVAL

void
net_ss_hostkey_hash(SSH2* ss, SSH2_HOSTKEY_HASH type)
PREINIT:
    const char* hash;
    static STRLEN rglen[] = { 16/*MD5*/, 20/*SHA1*/ };
PPCODE:
    if (type < 1 || type > countof(rglen)) {
        croak("%s::hostkey: unknown hostkey hash: %d",
              class, (int)type);
    }
    if ((hash = (const char*)libssh2_hostkey_hash(ss->session, type))) {
        PUSHs(sv_2mortal(newSVpvn(hash, rglen[type-1])));
        XSRETURN(1);
    }
    XSRETURN_EMPTY;

void
net_ss_remote_hostkey(SSH2* ss)
PREINIT:
    const char *key_pv;
    size_t key_len;
    int type_int;
PPCODE:
    if ((key_pv = libssh2_session_hostkey(ss->session, &key_len, &type_int))) {
        XPUSHs(sv_2mortal(newSVpvn(key_pv, key_len)));
        if (GIMME_V != G_ARRAY)
            XSRETURN(1);
        else {
            XPUSHs(sv_2mortal(newSViv(type_int)));
            XSRETURN(2);
        }
    }
    else
        XSRETURN_EMPTY;

SSH2_CHARP_OR_NULL
net_ss__auth_list(SSH2* ss, SV *username = &PL_sv_undef)
PREINIT:
    const char* pv_username = NULL;
    STRLEN len_username = 0;
CODE:
    if (SvOK(username))
        pv_username = SvPVbyte(username, len_username);
    RETVAL = libssh2_userauth_list(ss->session, pv_username, len_username);
OUTPUT:
    RETVAL

SSH2_RC
net_ss_auth_ok(SSH2* ss)
CODE:
    RETVAL = libssh2_userauth_authenticated(ss->session);
OUTPUT:
    RETVAL

SSH2_NERROR
net_ss_auth_password(SSH2* ss,                                  \
                     SV* username, SV* password = &PL_sv_undef, \
                     SV* callback = &PL_sv_undef)
PREINIT:
    STRLEN len_username, len_password;
    const char *pv_username, *pv_password;
    int i, ok;
CODE:
    pv_username = SvPVbyte(username, len_username);

    /* if we don't have a password, try for an unauthenticated login */
    if (!SvPOK(password)) {
        /* That's how libssh2 tells you authentication 'none' is valid */
        RETVAL = (((libssh2_userauth_list(ss->session, pv_username, len_username) == NULL) &&
                   libssh2_userauth_authenticated(ss->session)) ? 0 : -1);
    }
    else {
        if (SvOK(callback)) {
            if (!(SvROK(callback) && SvTYPE(SvRV(callback)) == SVt_PVCV))
                Perl_croak(aTHX_ "%s::auth_password: callback must be CODE ref", class);
            else {
                AV *cb_args = (AV*)sv_2mortal((SV*)newAV());
                av_push(cb_args, newSVsv(callback));
                av_push(cb_args, newSVsv(ST(0))); /*session */
                av_push(cb_args, newSVsv(username));
                set_cb_args(aTHX_ cb_args);
            }
        }

SSH2.xs  view on Meta::CPAN

    RETVAL

SSH2_PUBLICKEY*
net_ss_public_key(SSH2* ss)
CODE:
    NEW_PUBLICKEY(libssh2_publickey_init(ss->session));
OUTPUT:
    RETVAL

#undef class


MODULE = Net::SSH2		PACKAGE = Net::SSH2::Channel    PREFIX = net_ch_
PROTOTYPES: DISABLE

#define class "Net::SSH2::Channel"

void
net_ch_DESTROY(SSH2_CHANNEL* ch)
CODE:
    debug("%s::DESTROY\n", class);
    libssh2_channel_free(ch->channel);
    SvREFCNT_dec(ch->sv_ss);
    Safefree(ch);

SV *
net_ch_session(SSH2_CHANNEL* ch)
CODE:
    RETVAL = newRV_inc(ch->sv_ss);
OUTPUT:
    RETVAL

SSH2_NERROR
net_ch__setenv(SSH2_CHANNEL* ch, SV *key, SV *value)
PREINIT:
    int i, success = 0;
    const char* pv_key, * pv_value;
    STRLEN len_key, len_value;
CODE:
    pv_key = SvPVbyte(key, len_key);
    pv_value = SvPVbyte(value, len_value);
    RETVAL = libssh2_channel_setenv_ex(ch->channel,
                                       (char*)pv_key, len_key,
                                       (char*)pv_value, len_value);
    save_eagain(ch->ss->session, RETVAL);
OUTPUT:
    RETVAL

#if LIBSSH2_VERSION_NUM >= 0x010208

void
net_ch__exit_signal(SSH2_CHANNEL* ch)
PREINIT:
    char *exitsignal;
    char *errmsg;
    char *langtag;
    size_t exitsignal_len;
    size_t errmsg_len;
    size_t langtag_len;
    int retcount = 1;
PPCODE:
    if (!libssh2_channel_get_exit_signal(ch->channel,
                                         &exitsignal, &exitsignal_len,
                                         &errmsg, &errmsg_len,
                                         &langtag, &langtag_len)) {
        LIBSSH2_SESSION *session = ch->ss->session;
        libssh2_session_set_last_error(session, 0, NULL);
        if (exitsignal) {
            XPUSHs(sv_2mortal(newSVpvn(exitsignal, exitsignal_len)));
            if (GIMME_V == G_ARRAY) {
                XPUSHs(errmsg ? sv_2mortal(newSVpvn(errmsg, errmsg_len)) : &PL_sv_undef);
                XPUSHs(langtag ? sv_2mortal(newSVpvn(langtag, langtag_len)) : &PL_sv_undef);
                retcount = 3;
            }
            libssh2_free(session, exitsignal);
            if (errmsg) libssh2_free(session, errmsg);
            if (langtag) libssh2_free(session, langtag);
        }
        else
            XPUSHs(&PL_sv_no);
        XSRETURN(retcount);
    }
    else
        XSRETURN(0);

#else

void
net_ch__exit_signal(SSH2_CHANNEL* ch)
CODE:
    croak("libssh2 version 1.2.8 or higher required for exit_signal support");

#endif

SSH2_BYTES
net_ch_eof(SSH2_CHANNEL* ch)
CODE:
    RETVAL = libssh2_channel_eof(ch->channel);
    save_eagain(ch->ss->session, RETVAL);
OUTPUT:
    RETVAL

SSH2_NERROR
net_ch_send_eof(SSH2_CHANNEL* ch)
CODE:
    RETVAL = libssh2_channel_send_eof(ch->channel);
    save_eagain(ch->ss->session, RETVAL);
OUTPUT:
    RETVAL

SSH2_NERROR
net_ch_close(SSH2_CHANNEL* ch)
CODE:
    RETVAL = libssh2_channel_close(ch->channel);
    save_eagain(ch->ss->session, RETVAL);
OUTPUT:
    RETVAL

SSH2_NERROR
net_ch__wait_closed(SSH2_CHANNEL* ch)
CODE:

SSH2.xs  view on Meta::CPAN

    /*
       1. in blocking mode, write all the data.
       2. in non-blocking mode, write as much data as possible without
          blocking.
       3. if some error happens...
          a. if some data was already written, discard the error and
             report the number of bytes written.
          b. if no data was written, report the error.
    */
    pv_buffer = SvPVbyte(buffer, len_buffer);
    while (offset < len_buffer) {
        count = libssh2_channel_write_ex(ch->channel, ext,
                                         pv_buffer + offset,
                                         len_buffer - offset);
        if (count >= 0)
            offset += count;
        else if ((count != LIBSSH2_ERROR_EAGAIN) ||
                 !libssh2_session_get_blocking(ch->ss->session))
            break;
    }
    if (offset || (count == 0)) /* yes, zero is a valid value */
        RETVAL = offset;
    else {
        save_eagain(ch->ss->session, count);
        RETVAL = -1;
    }
OUTPUT:
    RETVAL

#if LIBSSH2_VERSION_NUM >= 0x010100

SSH2_BYTES
net_ch_receive_window_adjust(SSH2_CHANNEL *ch, unsigned long adjustment, SV *force = &PL_sv_undef)
PREINIT:
    unsigned int bytes;
CODE:
    RETVAL = libssh2_channel_receive_window_adjust2(ch->channel, adjustment,
                                                    SvTRUE(force), &bytes);
    if (RETVAL)
        save_eagain(ch->ss->session, RETVAL);
    else
        RETVAL = bytes;
OUTPUT:
    RETVAL

#else

void
net_ch_receive_window_adjust(SSH2_CHANNEL* ch, ...)
CODE:
    croak("libssh2 version 1.1 or higher required for receive_window_adjust support");

#endif

#if LIBSSH2_VERSION_NUM >= 0x010200

void
net_ch_window_write(SSH2_CHANNEL* ch)
PREINIT:
    unsigned long window_size_initial = 0;
PPCODE:
    XPUSHs(sv_2mortal(newSVuv(libssh2_channel_window_write_ex(ch->channel,
                                                              &window_size_initial))));
    if (GIMME_V == G_ARRAY) {
        XPUSHs(sv_2mortal(newSVuv(window_size_initial)));
        XSRETURN(2);
    }
    else
        XSRETURN(1);

void
net_ch_window_read(SSH2_CHANNEL *ch)
PREINIT:
    unsigned long read_avail = 0;
    unsigned long window_size_initial = 0;
PPCODE:
    XPUSHs(sv_2mortal(newSVuv(libssh2_channel_window_read_ex(ch->channel,
                                                             &read_avail,
                                                             &window_size_initial))));
    if (GIMME_V == G_ARRAY) {
        XPUSHs(sv_2mortal(newSVuv(read_avail)));
        XPUSHs(sv_2mortal(newSVuv(window_size_initial)));
        XSRETURN(3);
    }
    else
        XSRETURN(1);

#else

void
net_ch_window_write(SSH2_CHANNEL* ch)
CODE:
    croak("libssh2 version 1.2 or higher required for window_write support");

void
net_ch_window_read(SSH2_CHANNEL* ch)
CODE:
    croak("libssh2 version 1.2 or higher required for window_read support");

#endif

SSH2_BYTES
net_ch_flush(SSH2_CHANNEL* ch, SSH2_STREAM_ID ext = 0)
CODE:
    RETVAL = libssh2_channel_flush_ex(ch->channel, ext);
    save_eagain(ch->ss->session, RETVAL);
OUTPUT:
    RETVAL

#undef class


MODULE = Net::SSH2		PACKAGE = Net::SSH2::Listener    PREFIX = net_ls_
PROTOTYPES: DISABLE

#define class "Net::SSH2::Listener"

void
net_ls_DESTROY(SSH2_LISTENER* ls)
CODE:
    debug("%s::DESTROY\n", class);
    libssh2_channel_forward_cancel(ls->listener);
    SvREFCNT_dec(ls->sv_ss);
    Safefree(ls);

SSH2_CHANNEL*
net_ls_accept(SSH2_LISTENER* ls)
PREINIT:
    SSH2* ss;
CODE:
    ss = ls->ss;
    NEW_CHANNEL(libssh2_channel_forward_accept(ls->listener));
OUTPUT:
    RETVAL

#undef class


MODULE = Net::SSH2		PACKAGE = Net::SSH2::SFTP   PREFIX = net_sf_
PROTOTYPES: DISABLE

#define class "Net::SSH2::SFTP"

void
net_sf_DESTROY(SSH2_SFTP* sf)
CODE:
    debug("%s::DESTROY\n", class);
    libssh2_sftp_shutdown(sf->sftp);
    debug("%s::DESTROY freeing session\n", class);
    SvREFCNT_dec(sf->sv_ss);
    Safefree(sf);

SV *
net_sf_session(SSH2_SFTP* sf)
CODE:
    RETVAL = newRV_inc(sf->sv_ss);
OUTPUT:
    RETVAL

void
net_sf_error(SSH2_SFTP* sf)
PREINIT:
    unsigned long error;
    SV *errstr;
PPCODE:
    error = libssh2_sftp_last_error(sf->sftp);
    ST(0) = sv_2mortal(newSVuv(error));
    if (GIMME_V == G_ARRAY) {
        EXTEND(SP, 2);
        if ((error >= 0) && (error < countof(sftp_error)))
            errstr = newSVpvf("SSH_FX_%s", sftp_error[error]);
        else
            errstr = newSVpvf("SSH_FX_UNKNOWN(%lu)", error);
        ST(1) = sv_2mortal(errstr);
        XSRETURN(2);
    }
    else
        XSRETURN(1);

#define XLATFLAG(posix, fxf) do { \
    if (flags & posix || \
     l_flags == 0 && posix == 0 && flags == posix /* 0-valued flag */) { \
        l_flags |= fxf; \
        flags &= ~posix; \
    } \
} while(0)
    
SSH2_FILE*
net_sf_open(SSH2_SFTP* sf, SV* file, int flags = O_RDONLY, int mode = 0666)
PREINIT:
    long l_flags = 0;
    const char* pv_file;
    STRLEN len_file;
CODE:
    pv_file = SvPVbyte(file, len_file);
    
    /* map POSIX O_* to LIBSSH2_FXF_* (can't assume they're the same) */
    XLATFLAG(O_RDWR,   LIBSSH2_FXF_READ | LIBSSH2_FXF_WRITE);
    XLATFLAG(O_RDONLY, LIBSSH2_FXF_READ);
    XLATFLAG(O_WRONLY, LIBSSH2_FXF_WRITE);
    XLATFLAG(O_APPEND, LIBSSH2_FXF_APPEND);
    XLATFLAG(O_CREAT,  LIBSSH2_FXF_CREAT);
    XLATFLAG(O_TRUNC,  LIBSSH2_FXF_TRUNC);
    XLATFLAG(O_EXCL,   LIBSSH2_FXF_EXCL);
    if (flags)
        croak("%s::open: unknown flag value: %d", class, flags);

    NEW_FILE(libssh2_sftp_open_ex(sf->sftp, (char*)pv_file, len_file,
     l_flags, mode, LIBSSH2_SFTP_OPENFILE));
OUTPUT:
    RETVAL

#undef XLATFLAG

SSH2_DIR*
net_sf_opendir(SSH2_SFTP* sf, SV* dir)
PREINIT:
    const char* pv_dir;
    STRLEN len_dir;
CODE:
    pv_dir = SvPVbyte(dir, len_dir);
    NEW_DIR(libssh2_sftp_open_ex(sf->sftp, (char*)pv_dir, len_dir,
     0/*flags*/, 0/*mode*/, LIBSSH2_SFTP_OPENDIR));
OUTPUT:
    RETVAL

SSH2_NERROR
net_sf_unlink(SSH2_SFTP* sf, SV* file)
PREINIT:
    char* pv_file;
    STRLEN len_file;
CODE:
    pv_file = SvPVbyte(file, len_file);
    RETVAL = libssh2_sftp_unlink_ex(sf->sftp, (char*)pv_file, len_file);
OUTPUT:
    RETVAL

SSH2_NERROR
net_sf_rename(SSH2_SFTP* sf, SV* old, SV* new,                  \
              long flags = ( LIBSSH2_SFTP_RENAME_OVERWRITE |    \
                             LIBSSH2_SFTP_RENAME_ATOMIC    |    \
                             LIBSSH2_SFTP_RENAME_NATIVE ) )
PREINIT:
    const char* pv_old, * pv_new;
    STRLEN len_old, len_new;
CODE:
    pv_old = SvPVbyte(old, len_old);
    pv_new = SvPVbyte(new, len_new);
    RETVAL = libssh2_sftp_rename_ex(sf->sftp,
                                    (char*)pv_old, len_old, (char*)pv_new, len_new, flags);
OUTPUT:
    RETVAL

SSH2_NERROR
net_sf_mkdir(SSH2_SFTP* sf, SV* dir, int mode = 0777)
PREINIT:
    const char* pv_dir;
    STRLEN len_dir;
CODE:
    pv_dir = SvPVbyte(dir, len_dir);
    RETVAL = libssh2_sftp_mkdir_ex(sf->sftp, (char*)pv_dir, len_dir, mode);
OUTPUT:
    RETVAL

SSH2_NERROR
net_sf_rmdir(SSH2_SFTP* sf, SV* dir)
PREINIT:
    const char* pv_dir;
    STRLEN len_dir;
CODE:
    pv_dir = SvPVbyte(dir, len_dir);
    RETVAL = libssh2_sftp_rmdir_ex(sf->sftp, (char*)pv_dir, len_dir);
OUTPUT:
    RETVAL

void
net_sf_stat(SSH2_SFTP* sf, SV* path, int follow = 1)
PREINIT:
    const char* pv_path;
    STRLEN len_path;
    int error;
    LIBSSH2_SFTP_ATTRIBUTES attrs;
PPCODE:
    pv_path = SvPVbyte(path, len_path);
    error = libssh2_sftp_stat_ex(sf->sftp, (char*)pv_path, len_path,
                                  (follow ? LIBSSH2_SFTP_STAT : LIBSSH2_SFTP_LSTAT),
                                  &attrs);
    if (error < 0)
        XSRETURN_EMPTY;
    XSRETURN_STAT_ATTRS(SvREFCNT_inc(path));

SSH2_NERROR
net_sf_setstat(SSH2_SFTP* sf, SV* path, ...)
PREINIT:
    const char* pv_path;
    STRLEN len_path;
    LIBSSH2_SFTP_ATTRIBUTES attrs;
    int i;
CODE:
    pv_path = SvPVbyte(path, len_path);
    Zero(&attrs, 1, LIBSSH2_SFTP_ATTRIBUTES);

    /* read key/value pairs; cf. hv_from_attrs */
    for (i = 2; i < items; i += 2) {
        const char* key = SvPVbyte_nolen(ST(i));
        if (i + 1 == items)
            croak("%s::setstat: key without value", class);
        if (0);  /* prime the chain */
        XLATATTR("size",  filesize,    SIZE)
        XLATATTR("uid",   uid,         UIDGID)
        XLATATTR("gid",   gid,         UIDGID)
        XLATATTR("mode",  permissions, PERMISSIONS)
        XLATATTR("atime", atime,       ACMODTIME)
        XLATATTR("mtime", mtime,       ACMODTIME)
        else
            croak("%s::setstat: unknown attribute: %s", class, key);
    }
    RETVAL = libssh2_sftp_stat_ex(sf->sftp, (char*)pv_path, len_path,
                                  LIBSSH2_SFTP_SETSTAT, &attrs);
OUTPUT:
    RETVAL

SSH2_NERROR
net_sf_symlink(SSH2_SFTP* sf, SV* path, SV* target)
PREINIT:
    char *pv_path, *pv_target;
    STRLEN len_path, len_target;
CODE:
    pv_path = SvPVbyte(path, len_path);
    pv_target = SvPVbyte(target, len_target);
    RETVAL = libssh2_sftp_symlink_ex(sf->sftp,
                                     pv_path, len_path,
                                     pv_target, len_target,
                                     LIBSSH2_SFTP_SYMLINK);
OUTPUT:
    RETVAL

SV *
net_sf_readlink(SSH2_SFTP* sf, SV* path)
PREINIT:
    const char* pv_path;
    char* pv_link;
    STRLEN len_path;

SSH2.xs  view on Meta::CPAN

CODE:
    debug("%s::DESTROY\n", class);
    libssh2_sftp_close_handle(fi->handle);
    SvREFCNT_dec(fi->sv_sf);
    Safefree(fi);

SSH2_BYTES
net_fi_read(SSH2_FILE* fi, SV* buffer, size_t size)
PREINIT:
    char* pv_buffer;
    STRLEN len_buffer;
CODE:
    sv_force_normal(buffer);
    sv_setpvn_mg(buffer, "", 0);
    SvPVbyte_force(buffer, len_buffer);
    pv_buffer = sv_grow(buffer, size + 1);
    RETVAL = libssh2_sftp_read(fi->handle, pv_buffer, size);
    if (RETVAL < 0)
        SvOK_off(buffer);
    else {
        SvPOK_only(buffer);
        pv_buffer[RETVAL] = '\0';
        SvCUR_set(buffer, RETVAL);
    }
    SvSETMAGIC(buffer);
OUTPUT:
    RETVAL

SV *
net_fi_getc(SSH2_FILE* fi)
PREINIT:
    char buffer[2];
    int count;
CODE:
    count = libssh2_sftp_read(fi->handle, buffer, 1);
    if (count == 1) {
        buffer[count] = '\0';
        RETVAL = newSVpvn(buffer, count);
    }
    else
        RETVAL = &PL_sv_undef;
OUTPUT:
    RETVAL

SSH2_BYTES
net_fi_write(SSH2_FILE* fi, SV* buffer)
PREINIT:
    const char* pv_buffer;
    STRLEN len_buffer;
CODE:
    sv_utf8_downgrade(buffer, 0);
    pv_buffer = SvPVbyte(buffer, len_buffer);
    RETVAL = libssh2_sftp_write(fi->handle, pv_buffer, len_buffer);
OUTPUT:
    RETVAL

void
net_fi_stat(SSH2_FILE* fi)
PREINIT:
    LIBSSH2_SFTP_ATTRIBUTES attrs;
PPCODE:
    if (libssh2_sftp_fstat(fi->handle, &attrs))
        XSRETURN_EMPTY;
    XSRETURN_STAT_ATTRS(NULL/*name*/);

SSH2_NERROR
net_fi_setstat(SSH2_FILE* fi, ...)
PREINIT:
    LIBSSH2_SFTP_ATTRIBUTES attrs;
    int i;
CODE:
    Zero(&attrs, 1, LIBSSH2_SFTP_ATTRIBUTES);

    /* read key/value pairs; cf. hv_from_attrs */
    for (i = 1; i < items; i += 2) {
        const char* key = SvPVbyte_nolen(ST(i));
        if (i + 1 == items)
            croak("%s::setstat: key without value", class);
        if (0);  /* prime the chain */
        XLATATTR("size",  filesize,    SIZE)
        XLATATTR("uid",   uid,         UIDGID)
        XLATATTR("gid",   gid,         UIDGID)
        XLATATTR("mode",  permissions, PERMISSIONS)
        XLATATTR("atime", atime,       ACMODTIME)
        XLATATTR("mtime", mtime,       ACMODTIME)
        else
            croak("%s::setstat: unknown attribute: %s", class, key);
    }
    RETVAL = libssh2_sftp_fsetstat(fi->handle, &attrs);
OUTPUT:
    RETVAL

int
net_fi_seek(SSH2_FILE* fi, size_t offset)
CODE:
    libssh2_sftp_seek64(fi->handle, offset);
    RETVAL = 1;
OUTPUT:
    RETVAL

SSH2_BYTES64
net_fi_tell(SSH2_FILE* fi)
CODE:
    RETVAL = libssh2_sftp_tell64(fi->handle);
OUTPUT:
    RETVAL
        
#undef class

MODULE = Net::SSH2		PACKAGE = Net::SSH2::Dir   PREFIX = net_di_
PROTOTYPES: DISABLE

#define class "Net::SSH2::Dir"

void
net_di_DESTROY(SSH2_DIR* di)
CODE:
    debug("%s::DESTROY\n", class);
    libssh2_sftp_close_handle(di->handle);
    SvREFCNT_dec(di->sv_sf);
    Safefree(di);

void
net_di_read(SSH2_DIR* di)
PREINIT:
    SV* buffer;
    char* pv_buffer;
    int count;
    LIBSSH2_SFTP_ATTRIBUTES attrs;
PPCODE:
    buffer = newSV(MAXPATHLEN + 1);
    SvPOK_on(buffer);
    pv_buffer = SvPVX(buffer);

    count = libssh2_sftp_readdir(di->handle, pv_buffer, MAXPATHLEN, &attrs);

    if (count <= 0) {
        SvREFCNT_dec(buffer);
        XSRETURN_EMPTY;
    }
    pv_buffer[count] = '\0';
    SvCUR_set(buffer, count);
    XSRETURN_STAT_ATTRS(buffer);

#undef class


MODULE = Net::SSH2		PACKAGE = Net::SSH2::PublicKey   PREFIX = net_pk_
PROTOTYPES: DISABLE

#define class "Net::SSH2::PublicKey"

void
net_pk_DESTROY(SSH2_PUBLICKEY* pk)
CODE:
    debug("%s::DESTROY\n", class);
    libssh2_publickey_shutdown(pk->pkey);
    SvREFCNT_dec(pk->sv_ss);
    Safefree(pk);

SSH2_NERROR
net_pk_add(SSH2_PUBLICKEY* pk, SV* name, SV* blob, int overwrite, ...)
PREINIT:
    const char* pv_name, * pv_blob;
    STRLEN len_name, len_blob;
    unsigned long num_attrs, i;
    libssh2_publickey_attribute *attrs;
CODE:
    pv_name = SvPVbyte(name, len_name);
    pv_blob = SvPVbyte(blob, len_blob);

    num_attrs = items - 4;
    New(0, attrs, num_attrs, libssh2_publickey_attribute);
    if (!attrs)
        Perl_croak(aTHX_ "Out of memory!");

    for (i = 0; i < num_attrs; ++i) {
        HV* hv;
        SV** tmp;
        STRLEN len_tmp;

        if (!SvROK(ST(i + 4)) || SvTYPE(SvRV(ST(i + 4))) != SVt_PVHV)
            croak("%s::add: attribute %lu is not hash", class, i);
        hv = (HV*)SvRV(ST(i + 4));

        if (!(tmp = hv_fetch(hv, "name", 4, 0/*lval*/)) || !*tmp)
            croak("%s::add: attribute %lu missing name", class, i);
        attrs[i].name = SvPVbyte(*tmp, len_tmp);
        attrs[i].name_len = len_tmp;

        if ((tmp = hv_fetch(hv, "value", 5, 0/*lval*/)) && *tmp) {
            attrs[i].value = SvPVbyte(*tmp, len_tmp);
            attrs[i].value_len = len_tmp;
        } else
            attrs[i].value_len = 0;

        if ((tmp = hv_fetch(hv, "mandatory", 9, 0/*lval*/)) && *tmp)
            attrs[i].mandatory = (char)SvIV(*tmp);
        else
            attrs[i].mandatory = 0;
    }

    RETVAL = libssh2_publickey_add_ex(pk->pkey,
                                      (const unsigned char *)pv_name, len_name,
                                      (const unsigned char *)pv_blob, len_blob, overwrite, num_attrs, attrs);
    Safefree(attrs);
OUTPUT:
    RETVAL
    
SSH2_NERROR
net_pk_remove(SSH2_PUBLICKEY* pk, SV* name, SV* blob)
PREINIT:
    const char* pv_name, * pv_blob;
    STRLEN len_name, len_blob;
CODE:
    pv_name = SvPVbyte(name, len_name);
    pv_blob = SvPVbyte(blob, len_blob);
    RETVAL = libssh2_publickey_remove_ex(pk->pkey,
                                         (const unsigned char *)pv_name, len_name,
                                         (const unsigned char *)pv_blob, len_blob);
OUTPUT:
    RETVAL

void
net_pk_fetch(SSH2_PUBLICKEY* pk)
PREINIT:
    unsigned long keys, i, j;
    libssh2_publickey_list* list = NULL;
PPCODE:
    if (!libssh2_publickey_list_fetch(pk->pkey, &keys, &list) || !list)
        XSRETURN_EMPTY;

    if (GIMME_V == G_ARRAY) {
        EXTEND(SP, keys);

        for (i = 0; i < keys; ++i) {
            HV* hv = newHV();
            AV* av = newAV();

            hv_store(hv, "name", 4,
             newSVpvn((char*)list[i].name, list[i].name_len), 0/*hash*/);
            hv_store(hv, "blob", 4,
             newSVpvn((char*)list[i].blob, list[i].blob_len), 0/*hash*/);

            hv_store(hv, "attr", 4, newRV_noinc((SV*)av), 0/*hash*/);
            av_extend(av, list[i].num_attrs - 1);
            for (j = 0; j < list[i].num_attrs; ++j) {
                HV* attr = newHV();
                hv_store(attr, "name", 4, newSVpvn(list[i].attrs[j].name,
                 list[i].attrs[j].name_len), 0/*hash*/);
                hv_store(attr, "value", 5, newSVpvn(list[i].attrs[j].value,
                 list[i].attrs[j].value_len), 0/*hash*/);
                hv_store(attr, "mandatory", 9,
                 newSViv(list[i].attrs[j].mandatory), 0/*hash*/);
                av_store(av, j, newRV_noinc((SV*)attr));
            }
            
            ST(i) = sv_2mortal(newRV_noinc((SV*)hv));
        }
    }

    libssh2_publickey_list_free(pk->pkey, list);

    if (GIMME_V == G_ARRAY)
        XSRETURN(keys);
    XSRETURN_UV(keys);

#undef class

MODULE = Net::SSH2		PACKAGE = Net::SSH2::KnownHosts   PREFIX = net_kh_
PROTOTYPES: DISABLE

#define class "Net::SSH2::KnownHosts"

#if LIBSSH2_VERSION_NUM >= 0x010200

void
net_kh_DESTROY(SSH2_KNOWNHOSTS *kh)
CODE:
    debug("%s::DESTROY\n", class);
    libssh2_knownhost_free(kh->knownhosts);
    SvREFCNT_dec(kh->sv_ss);
    Safefree(kh);

SSH2_BYTES
net_kh_readfile(SSH2_KNOWNHOSTS *kh, SSH2_CHARP filename)
CODE:
    RETVAL = libssh2_knownhost_readfile(kh->knownhosts, filename, LIBSSH2_KNOWNHOST_FILE_OPENSSH);
OUTPUT:



( run in 1.516 second using v1.01-cache-2.11-cpan-71847e10f99 )