/* +----------------------------------------------------------------------+ | PHP Version 4 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2006 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 2.02 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | http://www.php.net/license/2_02.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Sara Golemon | +----------------------------------------------------------------------+ $Id: ssh2_sftp.c,v 1.4 2006/06/07 17:35:34 pollita Exp $ */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "php.h" #include "php_ssh2.h" #include "ext/standard/php_string.h" /* ************************* * Resource Housekeeping * ************************* */ void php_ssh2_sftp_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) { php_ssh2_sftp_data *data = (php_ssh2_sftp_data*)rsrc->ptr; if (!data) { return; } libssh2_sftp_shutdown(data->sftp); zend_list_delete(data->session_rsrcid); efree(data); } /* ***************** * SFTP File Ops * ***************** */ inline unsigned long php_ssh2_parse_fopen_modes(char *openmode) { unsigned long flags = 0; if (strchr(openmode, 'a')) { flags |= LIBSSH2_FXF_APPEND; } if (strchr(openmode, 'w')) { flags |= LIBSSH2_FXF_WRITE | LIBSSH2_FXF_TRUNC | LIBSSH2_FXF_CREAT; } if (strchr(openmode, 'r')) { flags |= LIBSSH2_FXF_READ; } if (strchr(openmode, '+')) { flags |= LIBSSH2_FXF_READ | LIBSSH2_FXF_WRITE; } if (strchr(openmode, 'x')) { flags |= LIBSSH2_FXF_WRITE | LIBSSH2_FXF_TRUNC | LIBSSH2_FXF_EXCL | LIBSSH2_FXF_CREAT; } return flags; } inline int php_ssh2_sftp_attr2ssb(php_stream_statbuf *ssb, LIBSSH2_SFTP_ATTRIBUTES *attrs) { memset(ssb, 0, sizeof(php_stream_statbuf)); if (attrs->flags & LIBSSH2_SFTP_ATTR_SIZE) { ssb->sb.st_size = attrs->filesize; } if (attrs->flags & LIBSSH2_SFTP_ATTR_UIDGID) { ssb->sb.st_uid = attrs->uid; ssb->sb.st_gid = attrs->gid; } if (attrs->flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) { ssb->sb.st_mode = attrs->permissions; } if (attrs->flags & LIBSSH2_SFTP_ATTR_ACMODTIME) { ssb->sb.st_atime = attrs->atime; ssb->sb.st_mtime = attrs->mtime; } return 0; } typedef struct _php_ssh2_sftp_handle_data { LIBSSH2_SFTP_HANDLE *handle; long sftp_rsrcid; } php_ssh2_sftp_handle_data; /* {{{ php_ssh2_sftp_stream_write */ static size_t php_ssh2_sftp_stream_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) { php_ssh2_sftp_handle_data *data = (php_ssh2_sftp_handle_data*)stream->abstract; return libssh2_sftp_write(data->handle, buf, count); } /* }}} */ /* {{{ php_ssh2_sftp_stream_read */ static size_t php_ssh2_sftp_stream_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) { php_ssh2_sftp_handle_data *data = (php_ssh2_sftp_handle_data*)stream->abstract; return libssh2_sftp_read(data->handle, buf, count); } /* }}} */ /* {{{ php_ssh2_sftp_stream_close */ static int php_ssh2_sftp_stream_close(php_stream *stream, int close_handle TSRMLS_DC) { php_ssh2_sftp_handle_data *data = (php_ssh2_sftp_handle_data*)stream->abstract; libssh2_sftp_close(data->handle); zend_list_delete(data->sftp_rsrcid); efree(data); return 0; } /* }}} */ /* {{{ php_ssh2_sftp_stream_seek */ static int php_ssh2_sftp_stream_seek(php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC) { php_ssh2_sftp_handle_data *data = (php_ssh2_sftp_handle_data*)stream->abstract; switch (whence) { case SEEK_END: { LIBSSH2_SFTP_ATTRIBUTES attrs; if (libssh2_sftp_fstat(data->handle, &attrs)) { return -1; } if ((attrs.flags & LIBSSH2_SFTP_ATTR_SIZE) == 0) { return -1; } offset += attrs.filesize; } case SEEK_CUR: { off_t current_offset = libssh2_sftp_tell(data->handle); if (current_offset < 0) { return -1; } offset += current_offset; } } libssh2_sftp_seek(data->handle, offset); if (newoffset) { *newoffset = offset; } return 0; } /* }}} */ /* {{{ php_ssh2_sftp_stream_fstat */ static int php_ssh2_sftp_stream_fstat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC) { php_ssh2_sftp_handle_data *data = (php_ssh2_sftp_handle_data*)stream->abstract; LIBSSH2_SFTP_ATTRIBUTES attrs; if (libssh2_sftp_fstat(data->handle, &attrs)) { return -1; } return php_ssh2_sftp_attr2ssb(ssb, &attrs); } /* }}} */ static php_stream_ops php_ssh2_sftp_stream_ops = { php_ssh2_sftp_stream_write, php_ssh2_sftp_stream_read, php_ssh2_sftp_stream_close, NULL, /* flush */ PHP_SSH2_SFTP_STREAM_NAME, php_ssh2_sftp_stream_seek, NULL, /* cast */ php_ssh2_sftp_stream_fstat, NULL, /* set_option */ }; /* {{{ php_ssh2_sftp_stream_opener */ static php_stream *php_ssh2_sftp_stream_opener( php_stream_wrapper *wrapper, char *filename, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) { php_ssh2_sftp_handle_data *data; LIBSSH2_SESSION *session = NULL; LIBSSH2_SFTP *sftp = NULL; LIBSSH2_SFTP_HANDLE *handle; php_stream *stream; int resource_id = 0, sftp_rsrcid = 0; php_url *resource; unsigned long flags; long perms = 0644; resource = php_ssh2_fopen_wraper_parse_path(filename, "sftp", context, &session, &resource_id, &sftp, &sftp_rsrcid TSRMLS_CC); if (!resource || !session || !sftp) { return NULL; } flags = php_ssh2_parse_fopen_modes(mode); handle = libssh2_sftp_open(sftp, resource->path, flags, perms); if (!handle) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to open %s on remote host", filename); php_url_free(resource); zend_list_delete(sftp_rsrcid); return NULL; } data = emalloc(sizeof(php_ssh2_sftp_handle_data)); data->handle = handle; data->sftp_rsrcid = sftp_rsrcid; stream = php_stream_alloc(&php_ssh2_sftp_stream_ops, data, 0, mode); if (!stream) { libssh2_sftp_close(handle); zend_list_delete(sftp_rsrcid); efree(data); } php_url_free(resource); return stream; } /* }}} */ /* ********************** * SFTP Directory Ops * ********************** */ /* {{{ php_ssh2_sftp_dirstream_read */ static size_t php_ssh2_sftp_dirstream_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) { php_ssh2_sftp_handle_data *data = (php_ssh2_sftp_handle_data*)stream->abstract; php_stream_dirent *ent = (php_stream_dirent*)buf; size_t bytesread = libssh2_sftp_readdir(data->handle, ent->d_name, sizeof(ent->d_name) - 1, NULL); char *basename = NULL; int basename_len = 0; if (bytesread <= 0) { return 0; } ent->d_name[bytesread] = 0; #ifdef ZEND_ENGINE_2 php_basename(ent->d_name, bytesread, NULL, 0, &basename, &basename_len TSRMLS_CC); #else /* CURSE YOU BC BREAKS! */ basename = php_basename(ent->d_name, bytesread, NULL, 0); if (basename) { basename_len = strlen(basename); } #endif if (!basename) { return 0; } if (!basename_len) { efree(basename); return 0; } bytesread = MIN(sizeof(ent->d_name) - 1, basename_len); memcpy(ent->d_name, basename, bytesread); ent->d_name[bytesread] = 0; efree(basename); /* Trim off trailing whitespace characters */ bytesread--; while (bytesread >= 0 && (ent->d_name[bytesread] == '\n' || ent->d_name[bytesread] == '\r' || ent->d_name[bytesread] == '\t' || ent->d_name[bytesread] == ' ')) { ent->d_name[bytesread--] = '\0'; } return sizeof(php_stream_dirent); } /* }}} */ /* {{{ php_ssh2_sftp_dirstream_close */ static int php_ssh2_sftp_dirstream_close(php_stream *stream, int close_handle TSRMLS_DC) { php_ssh2_sftp_handle_data *data = (php_ssh2_sftp_handle_data*)stream->abstract; libssh2_sftp_close(data->handle); zend_list_delete(data->sftp_rsrcid); efree(data); return 0; } /* }}} */ static php_stream_ops php_ssh2_sftp_dirstream_ops = { NULL, /* write */ php_ssh2_sftp_dirstream_read, php_ssh2_sftp_dirstream_close, NULL, /* flush */ PHP_SSH2_SFTP_DIRSTREAM_NAME, NULL, /* seek */ NULL, /* cast */ NULL, /* fstat */ NULL, /* set_option */ }; /* {{{ php_ssh2_sftp_dirstream_opener */ static php_stream *php_ssh2_sftp_dirstream_opener( php_stream_wrapper *wrapper, char *filename, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) { php_ssh2_sftp_handle_data *data; LIBSSH2_SESSION *session = NULL; LIBSSH2_SFTP *sftp = NULL; LIBSSH2_SFTP_HANDLE *handle; php_stream *stream; int resource_id = 0, sftp_rsrcid = 0; php_url *resource; resource = php_ssh2_fopen_wraper_parse_path(filename, "sftp", context, &session, &resource_id, &sftp, &sftp_rsrcid TSRMLS_CC); if (!resource || !session || !sftp) { return NULL; } handle = libssh2_sftp_opendir(sftp, resource->path); if (!handle) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to open %s on remote host", filename); php_url_free(resource); zend_list_delete(sftp_rsrcid); return NULL; } data = emalloc(sizeof(php_ssh2_sftp_handle_data)); data->handle = handle; data->sftp_rsrcid = sftp_rsrcid; stream = php_stream_alloc(&php_ssh2_sftp_dirstream_ops, data, 0, mode); if (!stream) { libssh2_sftp_close(handle); zend_list_delete(sftp_rsrcid); efree(data); } php_url_free(resource); return stream; } /* }}} */ /* **************** * SFTP Wrapper * **************** */ #ifdef ZEND_ENGINE_2 /* {{{ php_ssh2_sftp_urlstat */ static int php_ssh2_sftp_urlstat(php_stream_wrapper *wrapper, char *url, int flags, php_stream_statbuf *ssb, php_stream_context *context TSRMLS_DC) { LIBSSH2_SFTP_ATTRIBUTES attrs; LIBSSH2_SESSION *session = NULL; LIBSSH2_SFTP *sftp = NULL; int resource_id = 0, sftp_rsrcid = 0; php_url *resource; resource = php_ssh2_fopen_wraper_parse_path(url, "sftp", context, &session, &resource_id, &sftp, &sftp_rsrcid TSRMLS_CC); if (!resource || !session || !sftp || !resource->path) { return -1; } if (libssh2_sftp_stat_ex(sftp, resource->path, strlen(resource->path), (flags & PHP_STREAM_URL_STAT_LINK) ? LIBSSH2_SFTP_LSTAT : LIBSSH2_SFTP_STAT, &attrs)) { php_url_free(resource); zend_list_delete(sftp_rsrcid); return -1; } /* parse_path addrefs the resource, but we're not holding on to it so we have to delref it before we leave */ zend_list_delete(sftp_rsrcid); return php_ssh2_sftp_attr2ssb(ssb, &attrs); } /* }}} */ /* {{{ php_ssh2_sftp_unlink */ static int php_ssh2_sftp_unlink(php_stream_wrapper *wrapper, char *url, int options, php_stream_context *context TSRMLS_DC) { LIBSSH2_SESSION *session = NULL; LIBSSH2_SFTP *sftp = NULL; int resource_id = 0, sftp_rsrcid = 0; php_url *resource; int result; resource = php_ssh2_fopen_wraper_parse_path(url, "sftp", context, &session, &resource_id, &sftp, &sftp_rsrcid TSRMLS_CC); if (!resource || !session || !sftp || !resource->path) { return -1; } result = libssh2_sftp_unlink(sftp, resource->path); php_url_free(resource); zend_list_delete(sftp_rsrcid); return (result == 0) ? 0 : -1; } /* }}} */ /* {{{ php_ssh2_sftp_rename */ static int php_ssh2_sftp_rename(php_stream_wrapper *wrapper, char *url_from, char *url_to, int options, php_stream_context *context TSRMLS_DC) { LIBSSH2_SESSION *session = NULL; LIBSSH2_SFTP *sftp = NULL; int resource_id = 0, sftp_rsrcid = 0; php_url *resource, *resource_to; int result; if (strncmp(url_from, "ssh2.sftp://", sizeof("ssh2.sftp://") - 1) || strncmp(url_to, "ssh2.sftp://", sizeof("ssh2.sftp://") - 1)) { return -1; } resource_to = php_url_parse(url_to); if (!resource_to || !resource_to->path) { return -1; } resource = php_ssh2_fopen_wraper_parse_path(url_from, "sftp", context, &session, &resource_id, &sftp, &sftp_rsrcid TSRMLS_CC); if (!resource || !session || !sftp || !resource->path) { php_url_free(resource_to); return -1; } result = libssh2_sftp_rename(sftp, resource->path, resource_to->path); php_url_free(resource); zend_list_delete(sftp_rsrcid); return (result == 0) ? 0 : -1; } /* }}} */ /* {{{ php_ssh2_sftp_mkdir */ static int php_ssh2_sftp_mkdir(php_stream_wrapper *wrapper, char *url, int mode, int options, php_stream_context *context TSRMLS_DC) { LIBSSH2_SESSION *session = NULL; LIBSSH2_SFTP *sftp = NULL; int resource_id = 0, sftp_rsrcid = 0; php_url *resource; int result; resource = php_ssh2_fopen_wraper_parse_path(url, "sftp", context, &session, &resource_id, &sftp, &sftp_rsrcid TSRMLS_CC); if (!resource || !session || !sftp || !resource->path) { return -1; } if (options & PHP_STREAM_MKDIR_RECURSIVE) { /* Just attempt to make every directory, some will fail, but we only care about the last success/failure */ char *p = resource->path; while ((p = strchr(p + 1, '/'))) { libssh2_sftp_mkdir_ex(sftp, resource->path, p - resource->path, mode); } } result = libssh2_sftp_mkdir(sftp, resource->path, mode); php_url_free(resource); zend_list_delete(sftp_rsrcid); return (result == 0) ? 0 : -1; } /* }}} */ /* {{{ php_ssh2_sftp_rmdir */ static int php_ssh2_sftp_rmdir(php_stream_wrapper *wrapper, char *url, int options, php_stream_context *context TSRMLS_DC) { LIBSSH2_SESSION *session = NULL; LIBSSH2_SFTP *sftp = NULL; int resource_id = 0, sftp_rsrcid = 0; php_url *resource; int result; resource = php_ssh2_fopen_wraper_parse_path(url, "sftp", context, &session, &resource_id, &sftp, &sftp_rsrcid TSRMLS_CC); if (!resource || !session || !sftp || !resource->path) { return -1; } result = libssh2_sftp_rmdir(sftp, resource->path); php_url_free(resource); zend_list_delete(sftp_rsrcid); return (result == 0) ? 0 : -1; } /* }}} */ #endif static php_stream_wrapper_ops php_ssh2_sftp_wrapper_ops = { php_ssh2_sftp_stream_opener, NULL, /* close */ NULL, /* stat */ #ifdef ZEND_ENGINE_2 php_ssh2_sftp_urlstat, #else NULL, /* url_stat isn't actually functional prior to PHP5 */ #endif php_ssh2_sftp_dirstream_opener, PHP_SSH2_SFTP_WRAPPER_NAME, #ifdef ZEND_ENGINE_2 php_ssh2_sftp_unlink, php_ssh2_sftp_rename, php_ssh2_sftp_mkdir, php_ssh2_sftp_rmdir, #endif }; php_stream_wrapper php_ssh2_sftp_wrapper = { &php_ssh2_sftp_wrapper_ops, NULL, 1, 0, NULL, }; /* ***************** * Userspace API * ***************** */ /* {{{ proto resource ssh2_sftp(resource session) * Request the SFTP subsystem from an already connected SSH2 server */ PHP_FUNCTION(ssh2_sftp) { LIBSSH2_SESSION *session; LIBSSH2_SFTP *sftp; php_ssh2_sftp_data *data; zval *zsession; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zsession) == FAILURE) { RETURN_FALSE; } ZEND_FETCH_RESOURCE(session, LIBSSH2_SESSION*, &zsession, -1, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session); sftp = libssh2_sftp_init(session); if (!sftp) { char *sess_err = "Unknown"; libssh2_session_last_error(session, &sess_err, NULL, 0); php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to startup SFTP subsystem: %s", sess_err); RETURN_FALSE; } data = emalloc(sizeof(php_ssh2_sftp_data)); data->session = session; data->sftp = sftp; data->session_rsrcid = Z_LVAL_P(zsession); zend_list_addref(Z_LVAL_P(zsession)); ZEND_REGISTER_RESOURCE(return_value, data, le_ssh2_sftp); } /* }}} */ /* Much of the stuff below can be done via wrapper ops as of PHP5, but is included here for PHP 4.3 users */ /* {{{ proto bool ssh2_sftp_rename(resource sftp, string from, string to) */ PHP_FUNCTION(ssh2_sftp_rename) { php_ssh2_sftp_data *data; zval *zsftp; char *src, *dst; int src_len, dst_len; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rss", &zsftp, &src, &src_len, &dst, &dst_len) == FAILURE) { RETURN_FALSE; } ZEND_FETCH_RESOURCE(data, php_ssh2_sftp_data*, &zsftp, -1, PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp); RETURN_BOOL(!libssh2_sftp_rename_ex(data->sftp, src, src_len, dst, dst_len, LIBSSH2_SFTP_RENAME_OVERWRITE | LIBSSH2_SFTP_RENAME_ATOMIC | LIBSSH2_SFTP_RENAME_NATIVE)); } /* }}} */ /* {{{ proto bool ssh2_sftp_unlink(resource sftp, string filename) */ PHP_FUNCTION(ssh2_sftp_unlink) { php_ssh2_sftp_data *data; zval *zsftp; char *filename; int filename_len; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &zsftp, &filename, &filename_len) == FAILURE) { RETURN_FALSE; } ZEND_FETCH_RESOURCE(data, php_ssh2_sftp_data*, &zsftp, -1, PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp); RETURN_BOOL(!libssh2_sftp_unlink_ex(data->sftp, filename, filename_len)); } /* }}} */ /* {{{ proto bool ssh2_sftp_mkdir(resource sftp, string filename[, int mode[, int recursive]]) */ PHP_FUNCTION(ssh2_sftp_mkdir) { php_ssh2_sftp_data *data; zval *zsftp; char *filename; int filename_len; long mode = 0777; zend_bool recursive = 0; char *p; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs|lb", &zsftp, &filename, &filename_len, &mode, &recursive) == FAILURE) { RETURN_FALSE; } if (filename_len < 1) { RETURN_FALSE; } ZEND_FETCH_RESOURCE(data, php_ssh2_sftp_data*, &zsftp, -1, PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp); if (recursive) { /* Just attempt to make every directory, some will fail, but we only care about the last success/failure */ p = filename; while ((p = strchr(p + 1, '/'))) { libssh2_sftp_mkdir_ex(data->sftp, filename, p - filename, mode); } } RETURN_BOOL(!libssh2_sftp_mkdir_ex(data->sftp, filename, filename_len, mode)); } /* }}} */ /* {{{ proto bool ssh2_sftp_rmdir(resource sftp, string filename) */ PHP_FUNCTION(ssh2_sftp_rmdir) { php_ssh2_sftp_data *data; zval *zsftp; char *filename; int filename_len; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &zsftp, &filename, &filename_len) == FAILURE) { RETURN_FALSE; } ZEND_FETCH_RESOURCE(data, php_ssh2_sftp_data*, &zsftp, -1, PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp); RETURN_BOOL(!libssh2_sftp_rmdir_ex(data->sftp, filename, filename_len)); } /* }}} */ /* {{{ php_ssh2_sftp_stat_func * In PHP4.3 this is the only way to request stat into, in PHP >= 5 you can use the fopen wrapper approach * Both methods will return identical structures * (well, the other one will include other values set to 0 but they don't count) */ static void php_ssh2_sftp_stat_func(INTERNAL_FUNCTION_PARAMETERS, int stat_type) { php_ssh2_sftp_data *data; LIBSSH2_SFTP_ATTRIBUTES attrs; zval *zsftp; char *path; int path_len; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &zsftp, &path, &path_len) == FAILURE) { RETURN_FALSE; } ZEND_FETCH_RESOURCE(data, php_ssh2_sftp_data*, &zsftp, -1, PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp); if (libssh2_sftp_stat_ex(data->sftp, path, path_len, stat_type, &attrs)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to stat remote file"); RETURN_FALSE; } array_init(return_value); if (attrs.flags & LIBSSH2_SFTP_ATTR_SIZE) { add_index_long(return_value, 7, attrs.filesize); add_assoc_long(return_value, "size", attrs.filesize); } if (attrs.flags & LIBSSH2_SFTP_ATTR_UIDGID) { add_index_long(return_value, 4, attrs.uid); add_assoc_long(return_value, "uid", attrs.uid); add_index_long(return_value, 5, attrs.gid); add_assoc_long(return_value, "gid", attrs.gid); } if (attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) { add_index_long(return_value, 2, attrs.permissions); add_assoc_long(return_value, "mode", attrs.permissions); } if (attrs.flags & LIBSSH2_SFTP_ATTR_ACMODTIME) { add_index_long(return_value, 8, attrs.atime); add_assoc_long(return_value, "atime", attrs.atime); add_index_long(return_value, 9, attrs.mtime); add_assoc_long(return_value, "mtime", attrs.mtime); } } /* }}} */ /* {{{ proto array ssh2_sftp_stat(resource sftp, string path) */ PHP_FUNCTION(ssh2_sftp_stat) { php_ssh2_sftp_stat_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, LIBSSH2_SFTP_STAT); } /* }}} */ /* {{{ proto array ssh2_sftp_lstat(resource sftp, string path) */ PHP_FUNCTION(ssh2_sftp_lstat) { php_ssh2_sftp_stat_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, LIBSSH2_SFTP_LSTAT); } /* }}} */ /* {{{ proto bool ssh2_sftp_symlink(resource sftp, string target, string link) */ PHP_FUNCTION(ssh2_sftp_symlink) { php_ssh2_sftp_data *data; zval *zsftp; char *targ, *link; int targ_len, link_len; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rss", &zsftp, &targ, &targ_len, &link, &link_len) == FAILURE) { RETURN_FALSE; } ZEND_FETCH_RESOURCE(data, php_ssh2_sftp_data*, &zsftp, -1, PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp); RETURN_BOOL(!libssh2_sftp_symlink_ex(data->sftp, targ, targ_len, link, link_len, LIBSSH2_SFTP_SYMLINK)); } /* }}} */ /* {{{ proto string ssh2_sftp_readlink(resource sftp, string link) */ PHP_FUNCTION(ssh2_sftp_readlink) { php_ssh2_sftp_data *data; zval *zsftp; char *link; int targ_len = 0, link_len; char targ[8192]; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &zsftp, &link, &link_len) == FAILURE) { RETURN_FALSE; } ZEND_FETCH_RESOURCE(data, php_ssh2_sftp_data*, &zsftp, -1, PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp); if ((targ_len = libssh2_sftp_symlink_ex(data->sftp, link, link_len, targ, 8192, LIBSSH2_SFTP_READLINK)) < 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to read link '%s'", link); RETURN_FALSE; } RETURN_STRINGL(targ, targ_len, 1); } /* }}} */ /* {{{ proto string ssh2_sftp_realpath(resource sftp, string filename) */ PHP_FUNCTION(ssh2_sftp_realpath) { php_ssh2_sftp_data *data; zval *zsftp; char *link; int targ_len = 0, link_len; char targ[8192]; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &zsftp, &link, &link_len) == FAILURE) { RETURN_FALSE; } ZEND_FETCH_RESOURCE(data, php_ssh2_sftp_data*, &zsftp, -1, PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp); if ((targ_len = libssh2_sftp_symlink_ex(data->sftp, link, link_len, targ, 8192, LIBSSH2_SFTP_REALPATH)) < 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to resolve realpath for '%s'", link); RETURN_FALSE; } RETURN_STRINGL(targ, targ_len, 1); } /* }}} */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * indent-tabs-mode: t * End: */