diff --git a/.gitignore b/.gitignore
index ac9c931..b91513d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -43,6 +43,7 @@ __pycache__
/external/mongoose
/external/openssl
/external/python
+/external/libssh
/external/libssh-win-static/include/libssh
/external/libssh-win-static/src
/external/libssh-win-static/lib
@@ -99,3 +100,4 @@ xcuserdata
profile
*.moved-aside
/server/share/tmp
+/build.sh
diff --git a/build.bat.in b/build.bat.in
index 3de6757..6738eb4 100644
--- a/build.bat.in
+++ b/build.bat.in
@@ -2,10 +2,12 @@
rem ============================================
rem 请调整以下路径,以适配您自己的的系统环境。
-rem 注意:必须使用Pyhont 3.4,32位版本!!
+rem 注意:必须使用Pyhont 3.7,32位版本!!
+rem 可以从这里下载:
+rem https://www.python.org/ftp/python/3.7.0/python-3.7.0.exe
rem ============================================
+
SET PYEXEC=C:\Program Files\Python\python.exe
-
"%PYEXEC%" -B build/build.py %1 %2 %3 %4 %5
diff --git a/build/.idea/encodings.xml b/build/.idea/encodings.xml
index 97626ba..d574609 100644
--- a/build/.idea/encodings.xml
+++ b/build/.idea/encodings.xml
@@ -1,6 +1,7 @@
+
\ No newline at end of file
diff --git a/build/build.py b/build/build.py
index d05680b..241bd29 100644
--- a/build/build.py
+++ b/build/build.py
@@ -128,6 +128,7 @@ def do_opt(opt):
elif 'external' == opt['name']:
script = 'build-external.py'
+ arg = '%s %s' % (ctx.target_path, opt['bits'])
elif 'server' == opt['name']:
script = 'build-server.py'
diff --git a/build/builder/build-external.py b/build/builder/build-external.py
index 61ba1ae..240fff7 100644
--- a/build/builder/build-external.py
+++ b/build/builder/build-external.py
@@ -41,15 +41,12 @@ class BuilderBase:
def _build_mongoose(self, file_name):
cc.e("this is a pure-virtual function.")
- # def build_openssl(self):
- # file_name = 'openssl-{}.zip'.format(env.ver_openssl)
- # _alt_ver = '_'.join(env.ver_openssl.split('.'))
- # if not utils.download_file('openssl source tarball', 'https://github.com/openssl/openssl/archive/OpenSSL_{}.zip'.format(_alt_ver), PATH_DOWNLOAD, file_name):
- # return
- # self._build_openssl(file_name)
- #
- # def _build_openssl(self, file_name):
- # cc.e("this is a pure-virtual function.")
+ def build_openssl(self):
+ file_name = 'openssl-{}.zip'.format(env.ver_ossl)
+ self._build_openssl(file_name)
+
+ def _build_openssl(self, file_name):
+ cc.e("this is a pure-virtual function.")
def build_libuv(self):
file_name = 'libuv-{}.zip'.format(env.ver_libuv)
@@ -102,12 +99,13 @@ class BuilderWin(BuilderBase):
super().__init__()
def _init_path(self):
- # self.OPENSSL_PATH_SRC = os.path.join(PATH_EXTERNAL, 'openssl')
+ self.OPENSSL_PATH_SRC = os.path.join(PATH_EXTERNAL, 'openssl')
self.JSONCPP_PATH_SRC = os.path.join(PATH_EXTERNAL, 'jsoncpp')
self.MONGOOSE_PATH_SRC = os.path.join(PATH_EXTERNAL, 'mongoose')
self.MBEDTLS_PATH_SRC = os.path.join(PATH_EXTERNAL, 'mbedtls')
self.LIBUV_PATH_SRC = os.path.join(PATH_EXTERNAL, 'libuv')
- self.LIBSSH_PATH_SRC = os.path.join(PATH_EXTERNAL, 'libssh-win-static')
+ # self.LIBSSH_PATH_SRC = os.path.join(PATH_EXTERNAL, 'libssh-win-static')
+ self.LIBSSH_PATH_SRC = os.path.join(PATH_EXTERNAL, 'libssh')
# self._prepare_python_header()
@@ -119,68 +117,84 @@ class BuilderWin(BuilderBase):
return
cc.v('')
- if os.path.exists(os.path.join(env.path_py_inc, 'Python.h')):
- cc.e('can not locate python development include path, make sure miniconda installed.')
+ # if os.path.exists(os.path.join(env.path_py_inc, 'Python.h')):
+ # cc.e('can not locate python development include path, make sure miniconda installed.')
+ # return
+ # cc.v('')
+ # utils.copy_ex(env.path_py_inc, os.path.join(PATH_EXTERNAL, 'python', 'include'))
+
+ _header_path = None
+ for p in sys.path:
+ if os.path.exists(os.path.join(p, 'include', 'Python.h')):
+ _header_path = os.path.join(p, 'include')
+ if _header_path is None:
+ cc.e('\ncan not locate python development include path in:')
+ for p in sys.path:
+ cc.e(' ', p)
+ raise RuntimeError()
+
+ utils.copy_ex(_header_path, os.path.join(PATH_EXTERNAL, 'python', 'include'))
+
+ def _build_openssl(self, file_name):
+ cc.n('build openssl static library from source code... ', end='')
+
+ _alt_ver = '_'.join(env.ver_ossl.split('.'))
+ if not utils.download_file('openssl source tarball', 'https://github.com/openssl/openssl/archive/OpenSSL_{}.zip'.format(_alt_ver), PATH_DOWNLOAD, file_name):
+ return
+
+ _chk_output = [
+ os.path.join(self.OPENSSL_PATH_SRC, 'out32', 'libeay32.lib'),
+ os.path.join(self.OPENSSL_PATH_SRC, 'out32', 'ssleay32.lib'),
+ os.path.join(self.OPENSSL_PATH_SRC, 'inc32', 'openssl', 'opensslconf.h'),
+ ]
+
+ need_build = False
+ for f in _chk_output:
+ if not os.path.exists(f):
+ need_build = True
+ break
+
+ if not need_build:
+ cc.w('already exists, skip.')
return
cc.v('')
- # _header_path = None
- # for p in sys.path:
- # if os.path.exists(os.path.join(p, 'include', 'pyctype.h')):
- # _header_path = os.path.join(p, 'include')
- # if _header_path is None:
- # cc.e('\ncan not locate python development include path in:')
- # for p in sys.path:
- # cc.e(' ', p)
- # raise RuntimeError()
+ cc.n('prepare openssl source code...')
+ _alt_ver = '_'.join(env.ver_ossl.split('.'))
+ if not os.path.exists(self.OPENSSL_PATH_SRC):
+ utils.unzip(os.path.join(PATH_DOWNLOAD, file_name), PATH_EXTERNAL)
+ os.rename(os.path.join(PATH_EXTERNAL, 'openssl-OpenSSL_{}'.format(_alt_ver)), self.OPENSSL_PATH_SRC)
+ if not os.path.exists(self.OPENSSL_PATH_SRC):
+ raise RuntimeError('can not prepare openssl source code.')
+ else:
+ cc.w('already exists, skip.')
- # utils.copy_ex(_header_path, os.path.join(PATH_EXTERNAL, 'python', 'include'))
- utils.copy_ex(env.path_py_inc, os.path.join(PATH_EXTERNAL, 'python', 'include'))
+ os.chdir(self.OPENSSL_PATH_SRC)
+ os.system('""{}" Configure VC-WIN32"'.format(env.perl))
+ os.system(r'ms\do_nasm')
+ os.system(r'"{}\VC\bin\vcvars32.bat" && nmake -f ms\nt.mak'.format(env.visual_studio_path))
- # def _build_openssl(self, file_name):
- # cc.n('build openssl static library from source code... ', end='')
- # _chk_output = [
- # os.path.join(self.OPENSSL_PATH_SRC, 'out32', 'libeay32.lib'),
- # os.path.join(self.OPENSSL_PATH_SRC, 'out32', 'ssleay32.lib'),
- # os.path.join(self.OPENSSL_PATH_SRC, 'inc32', 'openssl', 'opensslconf.h'),
- # ]
- #
- # need_build = False
- # for f in _chk_output:
- # if not os.path.exists(f):
- # need_build = True
- # break
- #
- # if not need_build:
- # cc.w('already exists, skip.')
- # return
- # cc.v('')
- #
- # cc.n('prepare openssl source code...')
- # _alt_ver = '_'.join(env.ver_openssl.split('.'))
- # if not os.path.exists(self.OPENSSL_PATH_SRC):
- # utils.unzip(os.path.join(PATH_DOWNLOAD, file_name), PATH_EXTERNAL)
- # os.rename(os.path.join(PATH_EXTERNAL, 'openssl-OpenSSL_{}'.format(_alt_ver)), self.OPENSSL_PATH_SRC)
- # if not os.path.exists(self.OPENSSL_PATH_SRC):
- # raise RuntimeError('can not prepare openssl source code.')
- # else:
- # cc.w('already exists, skip.')
- #
- # os.chdir(self.OPENSSL_PATH_SRC)
- # os.system('""{}" Configure VC-WIN32"'.format(env.perl))
- # os.system(r'ms\do_nasm')
- # os.system(r'"{}\VC\bin\vcvars32.bat" && nmake -f ms\nt.mak'.format(env.visual_studio_path))
- #
- # for f in _chk_output:
- # if not os.path.exists(f):
- # raise RuntimeError('build openssl static library from source code failed.')
+ for f in _chk_output:
+ if not os.path.exists(f):
+ raise RuntimeError('build openssl static library from source code failed.')
def _build_libssh(self, file_name):
cc.n('build libssh static library from source code... ', end='')
- out_file = os.path.join(self.LIBSSH_PATH_SRC, 'lib', 'libsshMT.lib')
+
+ if not os.path.exists(self.LIBSSH_PATH_SRC):
+ cc.v('')
+ utils.unzip(os.path.join(PATH_DOWNLOAD, file_name), PATH_EXTERNAL)
+ os.rename(os.path.join(PATH_EXTERNAL, 'libssh-{}'.format(env.ver_libssh)), self.LIBSSH_PATH_SRC)
+
+ cc.n('fix libssh source code... ', end='')
+ utils.ensure_file_exists(os.path.join(PATH_EXTERNAL, 'fix-external', 'libssh', 'src', 'sftp.c'))
+ utils.copy_file(os.path.join(PATH_EXTERNAL, 'fix-external', 'libssh', 'src'), os.path.join(self.LIBSSH_PATH_SRC, 'src'), 'sftp.c')
+
+ out_file_lib = os.path.join(self.LIBSSH_PATH_SRC, 'lib', ctx.target_path, 'ssh.lib')
+ out_file_dll = os.path.join(self.LIBSSH_PATH_SRC, 'lib', ctx.target_path, 'ssh.dll')
need_build = False
- if not os.path.exists(out_file):
+ if not (os.path.exists(out_file_lib) and (os.path.exists(out_file_dll))):
need_build = True
if not need_build:
@@ -188,30 +202,43 @@ class BuilderWin(BuilderBase):
return
cc.v('')
- cc.n('prepare libssh source code... ', end='')
- _include = os.path.join(self.LIBSSH_PATH_SRC, 'include', 'libssh')
- _src = os.path.join(self.LIBSSH_PATH_SRC, 'src')
+ # cc.n('prepare libssh source code... ', end='')
+ # _include = os.path.join(self.LIBSSH_PATH_SRC, 'include', 'libssh')
+ # _src = os.path.join(self.LIBSSH_PATH_SRC, 'src')
+ #
+ # if not os.path.exists(_include) or not os.path.exists(_src):
+ # utils.unzip(os.path.join(PATH_DOWNLOAD, file_name), PATH_EXTERNAL)
+ # # os.rename(os.path.join(PATH_EXTERNAL, 'openssl-OpenSSL_{}'.format(_alt_ver)), self.OPENSSL_PATH_SRC)
+ #
+ # _unzipped_path = os.path.join(PATH_EXTERNAL, 'libssh-{}'.format(env.ver_libssh))
+ #
+ # utils.copy_ex(os.path.join(_unzipped_path, 'include', 'libssh'), _include)
+ # utils.copy_ex(os.path.join(_unzipped_path, 'src'), _src)
+ #
+ # utils.remove(_unzipped_path)
+ #
+ # if not os.path.exists(_include) or not os.path.exists(_src):
+ # raise RuntimeError('\ncan not prepare libssh source code.')
+ # else:
+ # cc.w('already exists, skip.')
- if not os.path.exists(_include) or not os.path.exists(_src):
- utils.unzip(os.path.join(PATH_DOWNLOAD, file_name), PATH_EXTERNAL)
- # os.rename(os.path.join(PATH_EXTERNAL, 'openssl-OpenSSL_{}'.format(_alt_ver)), self.OPENSSL_PATH_SRC)
-
- _unzipped_path = os.path.join(PATH_EXTERNAL, 'libssh-{}'.format(env.ver_libssh))
-
- utils.copy_ex(os.path.join(_unzipped_path, 'include', 'libssh'), _include)
- utils.copy_ex(os.path.join(_unzipped_path, 'src'), _src)
-
- utils.remove(_unzipped_path)
-
- if not os.path.exists(_include) or not os.path.exists(_src):
- raise RuntimeError('\ncan not prepare libssh source code.')
- else:
- cc.w('already exists, skip.')
+ cc.w('\nOnce the libssh.sln generated, press Enter to continue or Q to quit...', end='')
+ try:
+ x = env.input()
+ except EOFError:
+ x = 'q'
+ if x == 'q':
+ return
cc.i('build libssh...')
- sln_file = os.path.join(self.LIBSSH_PATH_SRC, 'libssh.vs2015.sln')
- utils.msvc_build(sln_file, 'libssh', ctx.target_path, ctx.bits_path, False)
- utils.ensure_file_exists(out_file)
+ sln_file = os.path.join(self.LIBSSH_PATH_SRC, 'build', 'libssh.sln')
+ utils.msvc_build(sln_file, 'ssh_shared', ctx.target_path, 'win32', False)
+ utils.ensure_file_exists(os.path.join(self.LIBSSH_PATH_SRC, 'build', 'src', ctx.target_path, 'ssh.lib'))
+ utils.ensure_file_exists(os.path.join(self.LIBSSH_PATH_SRC, 'build', 'src', ctx.target_path, 'ssh.dll'))
+ utils.copy_file(os.path.join(self.LIBSSH_PATH_SRC, 'build', 'src', ctx.target_path), os.path.join(self.LIBSSH_PATH_SRC, 'lib', ctx.target_path), 'ssh.lib')
+ utils.copy_file(os.path.join(self.LIBSSH_PATH_SRC, 'build', 'src', ctx.target_path), os.path.join(self.LIBSSH_PATH_SRC, 'lib', ctx.target_path), 'ssh.dll')
+ utils.ensure_file_exists(out_file_lib)
+ utils.ensure_file_exists(out_file_dll)
def _build_jsoncpp(self, file_name):
cc.n('prepare jsoncpp source code... ', end='')
@@ -245,8 +272,8 @@ class BuilderWin(BuilderBase):
# fix source file
utils.ensure_file_exists(os.path.join(PATH_EXTERNAL, 'fix-external', 'mbedtls', 'include', 'mbedtls', 'config.h'))
utils.copy_file(os.path.join(PATH_EXTERNAL, 'fix-external', 'mbedtls', 'include', 'mbedtls'), os.path.join(self.MBEDTLS_PATH_SRC, 'include', 'mbedtls'), 'config.h')
- utils.ensure_file_exists(os.path.join(PATH_EXTERNAL, 'fix-external', 'mbedtls', 'library', 'rsa.c'))
- utils.copy_file(os.path.join(PATH_EXTERNAL, 'fix-external', 'mbedtls', 'library'), os.path.join(self.MBEDTLS_PATH_SRC, 'library'), 'rsa.c')
+ # utils.ensure_file_exists(os.path.join(PATH_EXTERNAL, 'fix-external', 'mbedtls', 'library', 'rsa.c'))
+ # utils.copy_file(os.path.join(PATH_EXTERNAL, 'fix-external', 'mbedtls', 'library'), os.path.join(self.MBEDTLS_PATH_SRC, 'library'), 'rsa.c')
def _build_libuv(self, file_name):
cc.n('prepare libuv source code... ', end='')
@@ -272,7 +299,7 @@ class BuilderLinux(BuilderBase):
def _init_path(self):
self.PATH_TMP = os.path.join(PATH_EXTERNAL, 'linux', 'tmp')
self.PATH_RELEASE = os.path.join(PATH_EXTERNAL, 'linux', 'release')
- # self.OPENSSL_PATH_SRC = os.path.join(self.PATH_TMP, 'openssl-{}'.format(env.ver_openssl))
+ # self.OPENSSL_PATH_SRC = os.path.join(self.PATH_TMP, 'openssl-{}'.format(env.ver_ossl))
self.LIBUV_PATH_SRC = os.path.join(self.PATH_TMP, 'libuv-{}'.format(env.ver_libuv))
self.MBEDTLS_PATH_SRC = os.path.join(self.PATH_TMP, 'mbedtls-mbedtls-{}'.format(env.ver_mbedtls))
self.LIBSSH_PATH_SRC = os.path.join(self.PATH_TMP, 'libssh-{}'.format(env.ver_libssh))
@@ -322,8 +349,8 @@ class BuilderLinux(BuilderBase):
else:
cc.w('already exists, skip.')
- # def _build_openssl(self, file_name):
- # pass # we do not need build openssl anymore, because first time run build.sh we built Python, it include openssl.
+ def _build_openssl(self, file_name):
+ pass # we do not need build openssl anymore, because first time run build.sh we built Python, it include openssl.
def _build_libuv(self, file_name):
if not os.path.exists(self.LIBUV_PATH_SRC):
@@ -570,8 +597,8 @@ class BuilderMacOS(BuilderBase):
else:
cc.w('already exists, skip.')
- # def _build_openssl(self, file_name):
- # # we do not need build openssl anymore, because first time run build.sh we built Python, it include openssl.
+ def _build_openssl(self, file_name):
+ pass # we do not need build openssl anymore, because first time run build.sh we built Python, it include openssl.
#
# if not os.path.exists(self.OPENSSL_PATH_SRC):
# os.system('tar -zxvf "{}/{}" -C "{}"'.format(PATH_DOWNLOAD, file_name, self.PATH_TMP))
@@ -797,7 +824,7 @@ def main():
builder.build_jsoncpp()
builder.build_mongoose()
- # builder.build_openssl()
+ builder.build_openssl()
builder.build_libuv()
builder.build_mbedtls()
builder.build_libssh()
diff --git a/build/builder/build-pysrt.py b/build/builder/build-pysrt.py
index 8051f5d..5625ffc 100644
--- a/build/builder/build-pysrt.py
+++ b/build/builder/build-pysrt.py
@@ -12,12 +12,13 @@ from core.env import env
ctx = BuildContext()
-MODULES_WIN = ['_bz2', '_ctypes', '_hashlib', '_lzma', '_overlapped', '_socket', '_sqlite3', '_ssl', 'select', 'sqlite3', 'unicodedata']
+MODULES_WIN = ['_asyncio', '_bz2', '_ctypes', '_hashlib', '_lzma', '_overlapped', '_socket', '_sqlite3', '_ssl', 'select', 'sqlite3',
+ 'libcrypto-1_1', 'libssl-1_1', 'unicodedata']
PY_LIB_REMOVE_WIN = ['ctypes/test', 'curses', 'dbm', 'distutils', 'email/test', 'ensurepip', 'idlelib', 'lib2to3',
'lib-dynload', 'pydoc_data', 'site-packages', 'sqlite3/test', 'test', 'tkinter', 'turtledemo',
'unittest', 'venv', 'wsgiref', 'dis.py', 'doctest.py', 'pdb.py', 'py_compile.py', 'pydoc.py',
'this.py', 'wave.py', 'webbrowser.py', 'zipapp.py']
-PY_LIB_REMOVE_LINUX = ['ctypes/test', 'curses', 'config-3.7m-x86_64-linux-gnu', 'dbm', 'distutils', 'ensurepip', 'idlelib', 'lib2to3',
+PY_LIB_REMOVE_LINUX = ['ctypes/test', 'curses', 'dbm', 'distutils', 'ensurepip', 'idlelib', 'lib2to3',
'lib-dynload', 'pydoc_data', 'site-packages', 'sqlite3/test', 'test', 'tkinter', 'turtledemo', 'unittest', 'venv',
'wsgiref', 'dis.py', 'doctest.py', 'pdb.py', 'py_compile.py', 'pydoc_data', 'pydoc.py', 'this.py', 'wave.py',
'webbrowser.py', 'zipapp.py']
@@ -150,23 +151,24 @@ class PYSWin(PYSBase):
utils.makedirs(self.base_path)
cc.v('copy python core dll...')
- _win_system_path = os.path.join(os.getenv('SystemRoot'), 'system32')
- if ctx.bits == BITS_32 and ctx.host_os_is_win_x64:
- _win_system_path = os.path.join(os.getenv('SystemRoot'), 'SysWOW64')
+ _exec_path = os.path.dirname(env.py_exec)
+ # _win_system_path = os.path.join(os.getenv('SystemRoot'), 'system32')
+ # if ctx.bits == BITS_32 and ctx.host_os_is_win_x64:
+ # _win_system_path = os.path.join(os.getenv('SystemRoot'), 'SysWOW64')
- if not os.path.exists(_win_system_path):
- raise RuntimeError('can not locate windows system folder at:', _win_system_path)
+ if not os.path.exists(_exec_path):
+ raise RuntimeError('can not locate python folder at:', _exec_path)
pydll = self._get_py_dll_name()
- shutil.copy(os.path.join(_win_system_path, pydll), os.path.join(self.base_path, pydll))
+ shutil.copy(os.path.join(_exec_path, pydll), os.path.join(self.base_path, pydll))
if ctx.py_ver == '34':
msvcrdll = 'msvcr100.dll'
elif ctx.py_ver == '37':
- msvcrdll = 'msvcr140.dll'
+ msvcrdll = 'vcruntime140.dll'
else:
raise RuntimeError('unknown msvc runtime for this python version.')
- shutil.copy(os.path.join(_win_system_path, msvcrdll), os.path.join(self.base_path, msvcrdll))
+ shutil.copy(os.path.join(_exec_path, msvcrdll), os.path.join(self.base_path, msvcrdll))
super()._copy_modules()
diff --git a/build/builder/build-server.py b/build/builder/build-server.py
index e7d835c..8605b25 100644
--- a/build/builder/build-server.py
+++ b/build/builder/build-server.py
@@ -49,6 +49,7 @@ class BuilderWin(BuilderBase):
utils.remove(out_file)
utils.msvc_build(sln_file, 'tpssh', ctx.target_path, ctx.bits_path, False)
utils.ensure_file_exists(out_file)
+ utils.copy_file(os.path.join(env.root_path, 'external', 'libssh', 'lib', ctx.target_path), os.path.join(env.root_path, 'out', 'server', ctx.bits_path, ctx.target_path), 'ssh.dll')
cc.n('build TELNET protocol ...')
sln_file = os.path.join(env.root_path, 'server', 'tp_core', 'protocol', 'telnet', 'tptelnet.vs2015.sln')
diff --git a/build/builder/core/context.py b/build/builder/core/context.py
index 9237f3f..0f21b12 100644
--- a/build/builder/core/context.py
+++ b/build/builder/core/context.py
@@ -59,6 +59,7 @@ class BuildContext(object):
if self.host_os == 'windows':
self.host_os_is_win_x64 = 'PROGRAMFILES(X86)' in os.environ
+ self.dist_path = ''
self.make_dist_path()
def make_dist_path(self):
diff --git a/build/builder/core/env.py b/build/builder/core/env.py
index f572a66..4739f82 100644
--- a/build/builder/core/env.py
+++ b/build/builder/core/env.py
@@ -24,11 +24,15 @@ class Env(object):
self.root_path = os.path.abspath(os.path.join(_this_path, '..', '..', '..'))
self.build_path = os.path.abspath(os.path.join(_this_path, '..', '..'))
self.builder_path = os.path.join(self.build_path, 'builder')
- self.win32_tools_path = os.path.join(self.build_path, 'tools', 'win32')
self.is_py2 = sys.version_info[0] == 2
self.is_py3 = sys.version_info[0] == 3
+ if self.is_py2:
+ self.input = raw_input
+ else:
+ self.input = input
+
self.py_ver = platform.python_version_tuple()
self.py_ver_str = '%s%s' % (self.py_ver[0], self.py_ver[1])
self.py_ver_dot = '%s.%s' % (self.py_ver[0], self.py_ver[1])
@@ -53,19 +57,9 @@ class Env(object):
self.is_win = True
self.plat = 'windows'
self.is_win_x64 = 'PROGRAMFILES(X86)' in os.environ
- self.path_miniconda = os.path.abspath(os.path.dirname(self.py_exec))
- self.path_ossl_inc = os.path.join(self.path_miniconda, 'Library', 'include')
- self.path_ossl_lib = os.path.join(self.path_miniconda, 'Library', 'lib')
- self.path_py_inc = os.path.join(self.path_miniconda, 'include')
- self.path_py_lib = os.path.join(self.path_miniconda, 'libs')
elif _os == 'linux':
self.is_linux = True
self.plat = 'linux'
- self.path_miniconda = os.path.abspath(os.path.join(os.path.dirname(self.py_exec), '..'))
- self.path_ossl_inc = os.path.join(self.path_miniconda, 'include')
- self.path_ossl_lib = os.path.join(self.path_miniconda, 'lib')
- self.path_py_inc = os.path.join(self.path_miniconda, 'include', 'python{}m'.format(self.py_ver_dot))
- self.path_py_lib = os.path.join(self.path_miniconda, 'lib')
elif _os == 'darwin':
self.is_macos = True
self.plat = 'macos'
@@ -77,16 +71,12 @@ class Env(object):
if not self._load_version():
return False
- if not os.path.exists(os.path.join(self.path_miniconda, 'conda-meta')):
- cc.e('can not find conda, please install miniconda from https://conda.io/miniconda.html and try again.')
- return False
-
return True
def _load_config(self, warn_miss_tool):
_cfg_file = os.path.join(self.root_path, 'config.ini')
if not os.path.exists(_cfg_file):
- cc.e('can not load configuration.\n\nplease copy `config.ini.in` into `config.ini` and modify it to fit your condition and try again.')
+ cc.e('can not load configuration.\n\nplease copy `config.ini.in` to `config.ini` and modify it to fit your condition and try again.')
return False
_cfg = configparser.ConfigParser()
diff --git a/external/fix-external/libssh/src/session.c b/external/fix-external/libssh/src/session.c
deleted file mode 100644
index f046408..0000000
--- a/external/fix-external/libssh/src/session.c
+++ /dev/null
@@ -1,953 +0,0 @@
-/*
- * session.c - non-networking functions
- *
- * This file is part of the SSH Library
- *
- * Copyright (c) 2005-2013 by Aris Adamantiadis
- *
- * The SSH Library is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2.1 of the License, or (at your
- * option) any later version.
- *
- * The SSH Library is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
- * License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with the SSH Library; see the file COPYING. If not, write to
- * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
- * MA 02111-1307, USA.
- */
-
-#include "config.h"
-
-#include
-#include
-
-#include "libssh/priv.h"
-#include "libssh/libssh.h"
-#include "libssh/crypto.h"
-#include "libssh/server.h"
-#include "libssh/socket.h"
-#ifdef WITH_SSH1
-#include "libssh/ssh1.h"
-#endif /* WITH_SSH1 */
-#include "libssh/ssh2.h"
-#include "libssh/agent.h"
-#include "libssh/packet.h"
-#include "libssh/session.h"
-#include "libssh/misc.h"
-#include "libssh/buffer.h"
-#include "libssh/poll.h"
-
-#define FIRST_CHANNEL 42 // why not ? it helps to find bugs.
-
-/**
- * @defgroup libssh_session The SSH session functions.
- * @ingroup libssh
- *
- * Functions that manage a session.
- *
- * @{
- */
-
-/**
- * @brief Create a new ssh session.
- *
- * @returns A new ssh_session pointer, NULL on error.
- */
-ssh_session ssh_new(void) {
- ssh_session session;
- char *id = NULL;
- int rc;
-
- session = malloc(sizeof (struct ssh_session_struct));
- if (session == NULL) {
- return NULL;
- }
- ZERO_STRUCTP(session);
-
- session->next_crypto = crypto_new();
- if (session->next_crypto == NULL) {
- goto err;
- }
-
- session->socket = ssh_socket_new(session);
- if (session->socket == NULL) {
- goto err;
- }
-
- session->out_buffer = ssh_buffer_new();
- if (session->out_buffer == NULL) {
- goto err;
- }
-
- session->in_buffer=ssh_buffer_new();
- if (session->in_buffer == NULL) {
- goto err;
- }
-
- session->alive = 0;
- session->auth_methods = 0;
- ssh_set_blocking(session, 1);
- session->maxchannel = FIRST_CHANNEL;
-
-#ifndef _WIN32
- session->agent = agent_new(session);
- if (session->agent == NULL) {
- goto err;
- }
-#endif /* _WIN32 */
-
- /* OPTIONS */
- session->opts.StrictHostKeyChecking = 1;
- session->opts.port = 0;
- session->opts.fd = -1;
- session->opts.ssh2 = 1;
- session->opts.compressionlevel=7;
-#ifdef WITH_SSH1
- session->opts.ssh1 = 1;
-#else
- session->opts.ssh1 = 0;
-#endif
-
- session->opts.identity = ssh_list_new();
- if (session->opts.identity == NULL) {
- goto err;
- }
-
- id = strdup("%d/id_ed25519");
- if (id == NULL) {
- goto err;
- }
- rc = ssh_list_append(session->opts.identity, id);
- if (rc == SSH_ERROR) {
- goto err;
- }
-
-#ifdef HAVE_ECC
- id = strdup("%d/id_ecdsa");
- if (id == NULL) {
- goto err;
- }
- rc = ssh_list_append(session->opts.identity, id);
- if (rc == SSH_ERROR) {
- goto err;
- }
-#endif
-
- id = strdup("%d/id_rsa");
- if (id == NULL) {
- goto err;
- }
- rc = ssh_list_append(session->opts.identity, id);
- if (rc == SSH_ERROR) {
- goto err;
- }
-
- id = strdup("%d/id_dsa");
- if (id == NULL) {
- goto err;
- }
- rc = ssh_list_append(session->opts.identity, id);
- if (rc == SSH_ERROR) {
- goto err;
- }
-
- id = strdup("%d/identity");
- if (id == NULL) {
- goto err;
- }
- rc = ssh_list_append(session->opts.identity, id);
- if (rc == SSH_ERROR) {
- goto err;
- }
-
- return session;
-
-err:
- free(id);
- ssh_free(session);
- return NULL;
-}
-
-/**
- * @brief Deallocate a SSH session handle.
- *
- * @param[in] session The SSH session to free.
- *
- * @see ssh_disconnect()
- * @see ssh_new()
- */
-void ssh_free(ssh_session session) {
- int i;
- struct ssh_iterator *it;
-
- if (session == NULL) {
- return;
- }
-
- /*
- * Delete all channels
- *
- * This needs the first thing we clean up cause if there is still an open
- * channel we call ssh_channel_close() first. So we need a working socket
- * and poll context for it.
- */
- for (it = ssh_list_get_iterator(session->channels);
- it != NULL;
- it = ssh_list_get_iterator(session->channels)) {
- ssh_channel_do_free(ssh_iterator_value(ssh_channel,it));
- ssh_list_remove(session->channels, it);
- }
- ssh_list_free(session->channels);
- session->channels = NULL;
-
-#ifdef WITH_PCAP
- if (session->pcap_ctx) {
- ssh_pcap_context_free(session->pcap_ctx);
- session->pcap_ctx = NULL;
- }
-#endif
-
- ssh_socket_free(session->socket);
- session->socket = NULL;
-
- if (session->default_poll_ctx) {
- ssh_poll_ctx_free(session->default_poll_ctx);
- }
-
- ssh_buffer_free(session->in_buffer);
- ssh_buffer_free(session->out_buffer);
- session->in_buffer = session->out_buffer = NULL;
-
- if (session->in_hashbuf != NULL) {
- ssh_buffer_free(session->in_hashbuf);
- }
- if (session->out_hashbuf != NULL) {
- ssh_buffer_free(session->out_hashbuf);
- }
-
- crypto_free(session->current_crypto);
- crypto_free(session->next_crypto);
-
-#ifndef _WIN32
- agent_free(session->agent);
-#endif /* _WIN32 */
-
- ssh_key_free(session->srv.dsa_key);
- session->srv.dsa_key = NULL;
- ssh_key_free(session->srv.rsa_key);
- session->srv.rsa_key = NULL;
- ssh_key_free(session->srv.ecdsa_key);
- session->srv.ecdsa_key = NULL;
- ssh_key_free(session->srv.ed25519_key);
- session->srv.ed25519_key = NULL;
-
- if (session->ssh_message_list) {
- ssh_message msg;
-
- for (msg = ssh_list_pop_head(ssh_message, session->ssh_message_list);
- msg != NULL;
- msg = ssh_list_pop_head(ssh_message, session->ssh_message_list)) {
- ssh_message_free(msg);
- }
- ssh_list_free(session->ssh_message_list);
- }
-
- if (session->packet_callbacks) {
- ssh_list_free(session->packet_callbacks);
- }
-
- /* options */
- if (session->opts.identity) {
- char *id;
-
- for (id = ssh_list_pop_head(char *, session->opts.identity);
- id != NULL;
- id = ssh_list_pop_head(char *, session->opts.identity)) {
- SAFE_FREE(id);
- }
- ssh_list_free(session->opts.identity);
- }
-
-#ifndef _WIN32
- ssh_agent_state_free (session->agent_state);
-#endif
- session->agent_state = NULL;
-
- SAFE_FREE(session->auth_auto_state);
- SAFE_FREE(session->serverbanner);
- SAFE_FREE(session->clientbanner);
- SAFE_FREE(session->banner);
-
- SAFE_FREE(session->opts.bindaddr);
- SAFE_FREE(session->opts.custombanner);
- SAFE_FREE(session->opts.username);
- SAFE_FREE(session->opts.host);
- SAFE_FREE(session->opts.sshdir);
- SAFE_FREE(session->opts.knownhosts);
- SAFE_FREE(session->opts.ProxyCommand);
- SAFE_FREE(session->opts.gss_server_identity);
- SAFE_FREE(session->opts.gss_client_identity);
-
- for (i = 0; i < 10; i++) {
- if (session->opts.wanted_methods[i]) {
- SAFE_FREE(session->opts.wanted_methods[i]);
- }
- }
-
- /* burn connection, it could contain sensitive data */
- BURN_BUFFER(session, sizeof(struct ssh_session_struct));
- SAFE_FREE(session);
-}
-
-/**
- * @brief get the client banner
- *
- * @param[in] session The SSH session
- *
- * @return Returns the client banner string or NULL.
- */
-const char* ssh_get_clientbanner(ssh_session session) {
- if (session == NULL) {
- return NULL;
- }
-
- return session->clientbanner;
-}
-
-/**
- * @brief get the server banner
- *
- * @param[in] session The SSH session
- *
- * @return Returns the server banner string or NULL.
- */
-const char* ssh_get_serverbanner(ssh_session session) {
- if(!session) {
- return NULL;
- }
- return session->serverbanner;
-}
-
-/**
- * @brief get the name of the current key exchange algorithm.
- *
- * @param[in] session The SSH session
- *
- * @return Returns the key exchange algorithm string or NULL.
- */
-const char* ssh_get_kex_algo(ssh_session session) {
- if ((session == NULL) ||
- (session->current_crypto == NULL)) {
- return NULL;
- }
-
- switch (session->current_crypto->kex_type) {
- case SSH_KEX_DH_GROUP1_SHA1:
- return "diffie-hellman-group1-sha1";
- case SSH_KEX_DH_GROUP14_SHA1:
- return "diffie-hellman-group14-sha1";
- case SSH_KEX_ECDH_SHA2_NISTP256:
- return "ecdh-sha2-nistp256";
- case SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG:
- return "curve25519-sha256@libssh.org";
- default:
- break;
- }
-
- return NULL;
-}
-
-/**
- * @brief get the name of the input cipher for the given session.
- *
- * @param[in] session The SSH session.
- *
- * @return Returns cipher name or NULL.
- */
-const char* ssh_get_cipher_in(ssh_session session) {
- if ((session != NULL) &&
- (session->current_crypto != NULL) &&
- (session->current_crypto->in_cipher != NULL)) {
- return session->current_crypto->in_cipher->name;
- }
- return NULL;
-}
-
-/**
- * @brief get the name of the output cipher for the given session.
- *
- * @param[in] session The SSH session.
- *
- * @return Returns cipher name or NULL.
- */
-const char* ssh_get_cipher_out(ssh_session session) {
- if ((session != NULL) &&
- (session->current_crypto != NULL) &&
- (session->current_crypto->out_cipher != NULL)) {
- return session->current_crypto->out_cipher->name;
- }
- return NULL;
-}
-
-/**
- * @brief get the name of the input HMAC algorithm for the given session.
- *
- * @param[in] session The SSH session.
- *
- * @return Returns HMAC algorithm name or NULL if unknown.
- */
-const char* ssh_get_hmac_in(ssh_session session) {
- if ((session != NULL) &&
- (session->current_crypto != NULL)) {
- return ssh_hmac_type_to_string(session->current_crypto->in_hmac);
- }
- return NULL;
-}
-
-/**
- * @brief get the name of the output HMAC algorithm for the given session.
- *
- * @param[in] session The SSH session.
- *
- * @return Returns HMAC algorithm name or NULL if unknown.
- */
-const char* ssh_get_hmac_out(ssh_session session) {
- if ((session != NULL) &&
- (session->current_crypto != NULL)) {
- return ssh_hmac_type_to_string(session->current_crypto->out_hmac);
- }
- return NULL;
-}
-
-/**
- * @brief Disconnect impolitely from a remote host by closing the socket.
- *
- * Suitable if you forked and want to destroy this session.
- *
- * @param[in] session The SSH session to disconnect.
- */
-void ssh_silent_disconnect(ssh_session session) {
- if (session == NULL) {
- return;
- }
-
- ssh_socket_close(session->socket);
- session->alive = 0;
- ssh_disconnect(session);
-}
-
-/**
- * @brief Set the session in blocking/nonblocking mode.
- *
- * @param[in] session The ssh session to change.
- *
- * @param[in] blocking Zero for nonblocking mode.
- */
-void ssh_set_blocking(ssh_session session, int blocking) {
- if (session == NULL) {
- return;
- }
- session->flags &= ~SSH_SESSION_FLAG_BLOCKING;
- session->flags |= blocking ? SSH_SESSION_FLAG_BLOCKING : 0;
-}
-
-/**
- * @brief Return the blocking mode of libssh
- * @param[in] session The SSH session
- * @returns 0 if the session is nonblocking,
- * @returns 1 if the functions may block.
- */
-int ssh_is_blocking(ssh_session session){
- return (session->flags&SSH_SESSION_FLAG_BLOCKING) ? 1 : 0;
-}
-
-/* Waits until the output socket is empty */
-static int ssh_flush_termination(void *c){
- ssh_session session = c;
- if (ssh_socket_buffered_write_bytes(session->socket) == 0 ||
- session->session_state == SSH_SESSION_STATE_ERROR)
- return 1;
- else
- return 0;
-}
-
-/**
- * @brief Blocking flush of the outgoing buffer
- * @param[in] session The SSH session
- * @param[in] timeout Set an upper limit on the time for which this function
- * will block, in milliseconds. Specifying -1
- * means an infinite timeout. This parameter is passed to
- * the poll() function.
- * @returns SSH_OK on success, SSH_AGAIN if timeout occurred,
- * SSH_ERROR otherwise.
- */
-
-int ssh_blocking_flush(ssh_session session, int timeout){
- int rc;
- if (session == NULL) {
- return SSH_ERROR;
- }
-
- rc = ssh_handle_packets_termination(session, timeout,
- ssh_flush_termination, session);
- if (rc == SSH_ERROR) {
- return rc;
- }
- if (!ssh_flush_termination(session)) {
- rc = SSH_AGAIN;
- }
-
- return rc;
-}
-
-/**
- * @brief Check if we are connected.
- *
- * @param[in] session The session to check if it is connected.
- *
- * @return 1 if we are connected, 0 if not.
- */
-int ssh_is_connected(ssh_session session) {
- if (session == NULL) {
- return 0;
- }
-
- return session->alive;
-}
-
-/**
- * @brief Get the fd of a connection.
- *
- * In case you'd need the file descriptor of the connection to the server/client.
- *
- * @param[in] session The ssh session to use.
- *
- * @return The file descriptor of the connection, or -1 if it is
- * not connected
- */
-socket_t ssh_get_fd(ssh_session session) {
- if (session == NULL) {
- return -1;
- }
-
- return ssh_socket_get_fd_in(session->socket);
-}
-
-/**
- * @brief Tell the session it has data to read on the file descriptor without
- * blocking.
- *
- * @param[in] session The ssh session to use.
- */
-void ssh_set_fd_toread(ssh_session session) {
- if (session == NULL) {
- return;
- }
-
- ssh_socket_set_read_wontblock(session->socket);
-}
-
-/**
- * @brief Tell the session it may write to the file descriptor without blocking.
- *
- * @param[in] session The ssh session to use.
- */
-void ssh_set_fd_towrite(ssh_session session) {
- if (session == NULL) {
- return;
- }
-
- ssh_socket_set_write_wontblock(session->socket);
-}
-
-/**
- * @brief Tell the session it has an exception to catch on the file descriptor.
- *
- * \param[in] session The ssh session to use.
- */
-void ssh_set_fd_except(ssh_session session) {
- if (session == NULL) {
- return;
- }
-
- ssh_socket_set_except(session->socket);
-}
-
-/**
- * @internal
- *
- * @brief Poll the current session for an event and call the appropriate
- * callbacks. This function will not loop until the timeout is expired.
- *
- * This will block until one event happens.
- *
- * @param[in] session The session handle to use.
- *
- * @param[in] timeout Set an upper limit on the time for which this function
- * will block, in milliseconds. Specifying SSH_TIMEOUT_INFINITE
- * (-1) means an infinite timeout.
- * Specifying SSH_TIMEOUT_USER means to use the timeout
- * specified in options. 0 means poll will return immediately.
- * This parameter is passed to the poll() function.
- *
- * @return SSH_OK on success, SSH_ERROR otherwise.
- */
-int ssh_handle_packets(ssh_session session, int timeout) {
- ssh_poll_handle spoll_in,spoll_out;
- ssh_poll_ctx ctx;
- int tm = timeout;
- int rc;
-
- if (session == NULL || session->socket == NULL) {
- return SSH_ERROR;
- }
-
- spoll_in = ssh_socket_get_poll_handle_in(session->socket);
- spoll_out = ssh_socket_get_poll_handle_out(session->socket);
- ssh_poll_add_events(spoll_in, POLLIN);
- ctx = ssh_poll_get_ctx(spoll_in);
-
- if (!ctx) {
- ctx = ssh_poll_get_default_ctx(session);
- ssh_poll_ctx_add(ctx, spoll_in);
- if (spoll_in != spoll_out) {
- ssh_poll_ctx_add(ctx, spoll_out);
- }
- }
-
- if (timeout == SSH_TIMEOUT_USER) {
- if (ssh_is_blocking(session))
- tm = ssh_make_milliseconds(session->opts.timeout,
- session->opts.timeout_usec);
- else
- tm = 0;
- }
- rc = ssh_poll_ctx_dopoll(ctx, tm);
- if (rc == SSH_ERROR) {
- session->session_state = SSH_SESSION_STATE_ERROR;
- }
-
- return rc;
-}
-
-/**
- * @internal
- *
- * @brief Poll the current session for an event and call the appropriate
- * callbacks.
- *
- * This will block until termination function returns true, or timeout expired.
- *
- * @param[in] session The session handle to use.
- *
- * @param[in] timeout Set an upper limit on the time for which this function
- * will block, in milliseconds. Specifying SSH_TIMEOUT_INFINITE
- * (-1) means an infinite timeout.
- * Specifying SSH_TIMEOUT_USER means to use the timeout
- * specified in options. 0 means poll will return immediately.
- * SSH_TIMEOUT_DEFAULT uses blocking parameters of the session.
- * This parameter is passed to the poll() function.
- *
- * @param[in] fct Termination function to be used to determine if it is
- * possible to stop polling.
- * @param[in] user User parameter to be passed to fct termination function.
- * @return SSH_OK on success, SSH_ERROR otherwise.
- */
-int ssh_handle_packets_termination(ssh_session session,
- int timeout,
- ssh_termination_function fct,
- void *user)
-{
- struct ssh_timestamp ts;
- int ret = SSH_OK;
- int tm;
-
- if (timeout == SSH_TIMEOUT_USER) {
- if (ssh_is_blocking(session)) {
- timeout = ssh_make_milliseconds(session->opts.timeout,
- session->opts.timeout_usec);
- } else {
- timeout = SSH_TIMEOUT_NONBLOCKING;
- }
- } else if (timeout == SSH_TIMEOUT_DEFAULT) {
- if (ssh_is_blocking(session)) {
- // Apex.
- // timeout = SSH_TIMEOUT_INFINITE;
- timeout = ssh_make_milliseconds(session->opts.timeout,
- session->opts.timeout_usec);
- } else {
- timeout = SSH_TIMEOUT_NONBLOCKING;
- }
- }
-
- /* avoid unnecessary syscall for the SSH_TIMEOUT_NONBLOCKING case */
- if (timeout != SSH_TIMEOUT_NONBLOCKING) {
- ssh_timestamp_init(&ts);
- }
-
- tm = timeout;
- while(!fct(user)) {
- ret = ssh_handle_packets(session, tm);
- if (ret == SSH_ERROR) {
- break;
- }
- if (ssh_timeout_elapsed(&ts,timeout)) {
- ret = fct(user) ? SSH_OK : SSH_AGAIN;
- break;
- }
-
- tm = ssh_timeout_update(&ts, timeout);
- }
-
- return ret;
-}
-
-/**
- * @brief Get session status
- *
- * @param session The ssh session to use.
- *
- * @returns A bitmask including SSH_CLOSED, SSH_READ_PENDING, SSH_WRITE_PENDING
- * or SSH_CLOSED_ERROR which respectively means the session is closed,
- * has data to read on the connection socket and session was closed
- * due to an error.
- */
-int ssh_get_status(ssh_session session) {
- int socketstate;
- int r = 0;
-
- if (session == NULL) {
- return 0;
- }
-
- socketstate = ssh_socket_get_status(session->socket);
-
- if (session->session_state == SSH_SESSION_STATE_DISCONNECTED) {
- r |= SSH_CLOSED;
- }
- if (socketstate & SSH_READ_PENDING) {
- r |= SSH_READ_PENDING;
- }
- if (socketstate & SSH_WRITE_PENDING) {
- r |= SSH_WRITE_PENDING;
- }
- if ((session->session_state == SSH_SESSION_STATE_DISCONNECTED &&
- (socketstate & SSH_CLOSED_ERROR)) ||
- session->session_state == SSH_SESSION_STATE_ERROR) {
- r |= SSH_CLOSED_ERROR;
- }
-
- return r;
-}
-
-/**
- * @brief Get poll flags for an external mainloop
- *
- * @param session The ssh session to use.
- *
- * @returns A bitmask including SSH_READ_PENDING or SSH_WRITE_PENDING.
- * For SSH_READ_PENDING, your invocation of poll() should include
- * POLLIN. For SSH_WRITE_PENDING, your invocation of poll() should
- * include POLLOUT.
- */
-int ssh_get_poll_flags(ssh_session session)
-{
- if (session == NULL) {
- return 0;
- }
-
- return ssh_socket_get_poll_flags (session->socket);
-}
-
-/**
- * @brief Get the disconnect message from the server.
- *
- * @param[in] session The ssh session to use.
- *
- * @return The message sent by the server along with the
- * disconnect, or NULL in which case the reason of the
- * disconnect may be found with ssh_get_error.
- *
- * @see ssh_get_error()
- */
-const char *ssh_get_disconnect_message(ssh_session session) {
- if (session == NULL) {
- return NULL;
- }
-
- if (session->session_state != SSH_SESSION_STATE_DISCONNECTED) {
- ssh_set_error(session, SSH_REQUEST_DENIED,
- "Connection not closed yet");
- } else if(!session->discon_msg) {
- ssh_set_error(session, SSH_FATAL,
- "Connection correctly closed but no disconnect message");
- } else {
- return session->discon_msg;
- }
-
- return NULL;
-}
-
-/**
- * @brief Get the protocol version of the session.
- *
- * @param session The ssh session to use.
- *
- * @return 1 or 2, for ssh1 or ssh2, < 0 on error.
- */
-int ssh_get_version(ssh_session session) {
- if (session == NULL) {
- return -1;
- }
-
- return session->version;
-}
-
-/**
- * @internal
- * @brief Callback to be called when the socket received an exception code.
- * @param user is a pointer to session
- */
-void ssh_socket_exception_callback(int code, int errno_code, void *user){
- ssh_session session=(ssh_session)user;
-
- SSH_LOG(SSH_LOG_RARE,"Socket exception callback: %d (%d)",code, errno_code);
- session->session_state = SSH_SESSION_STATE_ERROR;
- if (errno_code == 0 && code == SSH_SOCKET_EXCEPTION_EOF) {
- ssh_set_error(session, SSH_FATAL, "Socket error: disconnected");
- } else {
- ssh_set_error(session, SSH_FATAL, "Socket error: %s", strerror(errno_code));
- }
-
- session->ssh_connection_callback(session);
-}
-
-/**
- * @brief Send a message that should be ignored
- *
- * @param[in] session The SSH session
- * @param[in] data Data to be sent
- *
- * @return SSH_OK on success, SSH_ERROR otherwise.
- */
-int ssh_send_ignore (ssh_session session, const char *data) {
-#ifdef WITH_SSH1
- const int type = session->version == 1 ? SSH_MSG_IGNORE : SSH2_MSG_IGNORE;
-#else /* WITH_SSH1 */
- const int type = SSH2_MSG_IGNORE;
-#endif /* WITH_SSH1 */
- int rc;
-
- if (ssh_socket_is_open(session->socket)) {
- rc = ssh_buffer_pack(session->out_buffer,
- "bs",
- type,
- data);
- if (rc != SSH_OK){
- ssh_set_error_oom(session);
- goto error;
- }
- packet_send(session);
- ssh_handle_packets(session, 0);
- }
-
- return SSH_OK;
-
-error:
- ssh_buffer_reinit(session->out_buffer);
- return SSH_ERROR;
-}
-
-/**
- * @brief Send a debug message
- *
- * @param[in] session The SSH session
- * @param[in] message Data to be sent
- * @param[in] always_display Message SHOULD be displayed by the server. It
- * SHOULD NOT be displayed unless debugging
- * information has been explicitly requested.
- *
- * @return SSH_OK on success, SSH_ERROR otherwise.
- */
-int ssh_send_debug (ssh_session session, const char *message, int always_display) {
- int rc;
-
- if (ssh_socket_is_open(session->socket)) {
-#ifdef WITH_SSH1
- if (session->version == 1) {
- rc = ssh_buffer_pack(session->out_buffer,
- "bs",
- SSH_MSG_DEBUG,
- message);
- } else
-#endif /* WITH_SSH1 */
- {
- rc = ssh_buffer_pack(session->out_buffer,
- "bbsd",
- SSH2_MSG_DEBUG,
- always_display != 0 ? 1 : 0,
- message,
- 0); /* empty language tag */
- }
- if (rc != SSH_OK) {
- ssh_set_error_oom(session);
- goto error;
- }
- packet_send(session);
- ssh_handle_packets(session, 0);
- }
-
- return SSH_OK;
-
-error:
- ssh_buffer_reinit(session->out_buffer);
- return SSH_ERROR;
-}
-
- /**
- * @brief Set the session data counters.
- *
- * This functions sets the counter structures to be used to calculate data
- * which comes in and goes out through the session at different levels.
- *
- * @code
- * struct ssh_counter_struct scounter = {
- * .in_bytes = 0,
- * .out_bytes = 0,
- * .in_packets = 0,
- * .out_packets = 0
- * };
- *
- * struct ssh_counter_struct rcounter = {
- * .in_bytes = 0,
- * .out_bytes = 0,
- * .in_packets = 0,
- * .out_packets = 0
- * };
- *
- * ssh_set_counters(session, &scounter, &rcounter);
- * @endcode
- *
- * @param[in] session The SSH session.
- *
- * @param[in] scounter Counter for byte data handled by the session sockets.
- *
- * @param[in] rcounter Counter for byte and packet data handled by the session,
- * prior compression and SSH overhead.
- */
-void ssh_set_counters(ssh_session session, ssh_counter scounter,
- ssh_counter rcounter) {
- if (session != NULL) {
- session->socket_counter = scounter;
- session->raw_counter = rcounter;
- }
-}
-
-/** @} */
-
-/* vim: set ts=4 sw=4 et cindent: */
diff --git a/external/fix-external/libssh/src/sftp.c b/external/fix-external/libssh/src/sftp.c
new file mode 100644
index 0000000..be1fa47
--- /dev/null
+++ b/external/fix-external/libssh/src/sftp.c
@@ -0,0 +1,3368 @@
+/*
+ * sftp.c - Secure FTP functions
+ *
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2005-2008 by Aris Adamantiadis
+ * Copyright (c) 2008-2009 by Andreas Schneider
+ *
+ * The SSH Library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ *
+ * The SSH Library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the SSH Library; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/* This file contains code written by Nick Zitzmann */
+
+#include "config.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#ifndef _WIN32
+#include
+#include
+#endif
+
+#include "libssh/priv.h"
+#include "libssh/ssh2.h"
+#include "libssh/sftp.h"
+#include "libssh/buffer.h"
+#include "libssh/channels.h"
+#include "libssh/session.h"
+#include "libssh/misc.h"
+
+#ifdef WITH_SFTP
+
+struct sftp_ext_struct {
+ unsigned int count;
+ char **name;
+ char **data;
+};
+
+/* functions */
+static int sftp_enqueue(sftp_session session, sftp_message msg);
+static void sftp_message_free(sftp_message msg);
+static void sftp_set_error(sftp_session sftp, int errnum);
+static void status_msg_free(sftp_status_message status);
+
+static sftp_ext sftp_ext_new(void) {
+ sftp_ext ext;
+
+ ext = calloc(1, sizeof(struct sftp_ext_struct));
+ if (ext == NULL) {
+ return NULL;
+ }
+
+ return ext;
+}
+
+static void sftp_ext_free(sftp_ext ext) {
+ unsigned int i;
+
+ if (ext == NULL) {
+ return;
+ }
+
+ if (ext->count) {
+ for (i = 0; i < ext->count; i++) {
+ SAFE_FREE(ext->name[i]);
+ SAFE_FREE(ext->data[i]);
+ }
+ SAFE_FREE(ext->name);
+ SAFE_FREE(ext->data);
+ }
+
+ SAFE_FREE(ext);
+}
+
+sftp_session sftp_new(ssh_session session){
+ sftp_session sftp;
+
+ if (session == NULL) {
+ return NULL;
+ }
+
+ sftp = calloc(1, sizeof(struct sftp_session_struct));
+ if (sftp == NULL) {
+ ssh_set_error_oom(session);
+
+ return NULL;
+ }
+
+ sftp->ext = sftp_ext_new();
+ if (sftp->ext == NULL) {
+ ssh_set_error_oom(session);
+ SAFE_FREE(sftp);
+
+ return NULL;
+ }
+
+ sftp->session = session;
+ sftp->channel = ssh_channel_new(session);
+ if (sftp->channel == NULL) {
+ sftp_ext_free(sftp->ext);
+ SAFE_FREE(sftp);
+
+ return NULL;
+ }
+
+ if (ssh_channel_open_session(sftp->channel)) {
+ ssh_channel_free(sftp->channel);
+ sftp_ext_free(sftp->ext);
+ SAFE_FREE(sftp);
+
+ return NULL;
+ }
+
+ if (ssh_channel_request_sftp(sftp->channel)) {
+ sftp_free(sftp);
+
+ return NULL;
+ }
+
+ return sftp;
+}
+
+sftp_session sftp_new_channel(ssh_session session, ssh_channel channel){
+ sftp_session sftp;
+
+ if (session == NULL) {
+ return NULL;
+ }
+
+ sftp = calloc(1, sizeof(struct sftp_session_struct));
+ if (sftp == NULL) {
+ ssh_set_error_oom(session);
+
+ return NULL;
+ }
+
+ sftp->ext = sftp_ext_new();
+ if (sftp->ext == NULL) {
+ ssh_set_error_oom(session);
+ SAFE_FREE(sftp);
+
+ return NULL;
+ }
+
+ sftp->session = session;
+ sftp->channel = channel;
+
+ return sftp;
+}
+
+#ifdef WITH_SERVER
+sftp_session sftp_server_new(ssh_session session, ssh_channel chan){
+ sftp_session sftp = NULL;
+
+ sftp = calloc(1, sizeof(struct sftp_session_struct));
+ if (sftp == NULL) {
+ ssh_set_error_oom(session);
+ return NULL;
+ }
+
+ sftp->session = session;
+ sftp->channel = chan;
+
+ return sftp;
+}
+
+int sftp_server_init(sftp_session sftp){
+ ssh_session session = sftp->session;
+ sftp_packet packet = NULL;
+ ssh_buffer reply = NULL;
+ uint32_t version;
+
+ packet = sftp_packet_read(sftp);
+ if (packet == NULL) {
+ return -1;
+ }
+
+ if (packet->type != SSH_FXP_INIT) {
+ ssh_set_error(session, SSH_FATAL,
+ "Packet read of type %d instead of SSH_FXP_INIT",
+ packet->type);
+
+ sftp_packet_free(packet);
+ return -1;
+ }
+
+ SSH_LOG(SSH_LOG_PACKET, "Received SSH_FXP_INIT");
+
+ ssh_buffer_get_u32(packet->payload, &version);
+ version = ntohl(version);
+ SSH_LOG(SSH_LOG_PACKET, "Client version: %d", version);
+ sftp->client_version = version;
+
+ sftp_packet_free(packet);
+
+ reply = ssh_buffer_new();
+ if (reply == NULL) {
+ ssh_set_error_oom(session);
+ return -1;
+ }
+
+ if (ssh_buffer_add_u32(reply, ntohl(LIBSFTP_VERSION)) < 0) {
+ ssh_set_error_oom(session);
+ ssh_buffer_free(reply);
+ return -1;
+ }
+
+ if (sftp_packet_write(sftp, SSH_FXP_VERSION, reply) < 0) {
+ ssh_buffer_free(reply);
+ return -1;
+ }
+ ssh_buffer_free(reply);
+
+ SSH_LOG(SSH_LOG_RARE, "Server version sent");
+
+ if (version > LIBSFTP_VERSION) {
+ sftp->version = LIBSFTP_VERSION;
+ } else {
+ sftp->version=version;
+ }
+
+ return 0;
+}
+#endif /* WITH_SERVER */
+
+void sftp_free(sftp_session sftp){
+ sftp_request_queue ptr;
+
+ if (sftp == NULL) {
+ return;
+ }
+
+ ssh_channel_send_eof(sftp->channel);
+ ptr = sftp->queue;
+ while(ptr) {
+ sftp_request_queue old;
+ sftp_message_free(ptr->message);
+ old = ptr->next;
+ SAFE_FREE(ptr);
+ ptr = old;
+ }
+
+ ssh_channel_free(sftp->channel);
+
+ SAFE_FREE(sftp->handles);
+
+ sftp_ext_free(sftp->ext);
+ ZERO_STRUCTP(sftp);
+
+ SAFE_FREE(sftp);
+}
+
+int sftp_packet_write(sftp_session sftp, uint8_t type, ssh_buffer payload){
+ int size;
+
+ if (ssh_buffer_prepend_data(payload, &type, sizeof(uint8_t)) < 0) {
+ ssh_set_error_oom(sftp->session);
+ return -1;
+ }
+
+ size = htonl(ssh_buffer_get_len(payload));
+ if (ssh_buffer_prepend_data(payload, &size, sizeof(uint32_t)) < 0) {
+ ssh_set_error_oom(sftp->session);
+ return -1;
+ }
+
+ size = ssh_channel_write(sftp->channel, ssh_buffer_get(payload),
+ ssh_buffer_get_len(payload));
+ if (size < 0) {
+ return -1;
+ } else if((uint32_t) size != ssh_buffer_get_len(payload)) {
+ SSH_LOG(SSH_LOG_PACKET,
+ "Had to write %d bytes, wrote only %d",
+ ssh_buffer_get_len(payload),
+ size);
+ }
+
+ return size;
+}
+
+sftp_packet sftp_packet_read(sftp_session sftp) {
+ unsigned char buffer[MAX_BUF_SIZE];
+ sftp_packet packet = NULL;
+ uint32_t tmp;
+ size_t size;
+ int r, s, is_eof;
+
+ packet = calloc(1, sizeof(struct sftp_packet_struct));
+ if (packet == NULL) {
+ ssh_set_error_oom(sftp->session);
+ return NULL;
+ }
+ packet->sftp = sftp;
+ packet->payload = ssh_buffer_new();
+ if (packet->payload == NULL) {
+ ssh_set_error_oom(sftp->session);
+ SAFE_FREE(packet);
+ return NULL;
+ }
+
+ r = ssh_buffer_allocate_size(packet->payload, 4);
+ if (r < 0) {
+ ssh_set_error_oom(sftp->session);
+ goto error;
+ }
+ r=0;
+ do {
+ // read from channel until 4 bytes have been read or an error occurs
+ s=ssh_channel_read(sftp->channel, buffer+r, 4-r, 0);
+ if (s < 0) {
+ goto error;
+ } else if (s == 0) {
+ is_eof = ssh_channel_is_eof(sftp->channel);
+ if (is_eof) {
+ goto error;
+ }
+ } else {
+ r += s;
+ }
+ } while (r<4);
+ ssh_buffer_add_data(packet->payload, buffer, r);
+ if (ssh_buffer_get_u32(packet->payload, &tmp) != sizeof(uint32_t)) {
+ ssh_set_error(sftp->session, SSH_FATAL, "Short sftp packet!");
+ goto error;
+ }
+
+ do {
+ r = ssh_channel_read(sftp->channel, buffer, 1, 0);
+ if (r < 0) {
+ goto error;
+ } else if (s == 0) {
+ is_eof = ssh_channel_is_eof(sftp->channel);
+ if (is_eof) {
+ goto error;
+ }
+ }
+ } while (r < 1);
+ ssh_buffer_add_data(packet->payload, buffer, r);
+ ssh_buffer_get_u8(packet->payload, &packet->type);
+
+ size = ntohl(tmp);
+ if (size == 0 || size > UINT32_MAX) {
+ return packet;
+ }
+ size--;
+
+ r = ssh_buffer_allocate_size(packet->payload, size);
+ if (r < 0) {
+ ssh_set_error_oom(sftp->session);
+ goto error;
+ }
+ while (size > 0 && size < UINT_MAX) {
+ r=ssh_channel_read(sftp->channel,buffer,
+ sizeof(buffer)>size ? size:sizeof(buffer),0);
+
+ if (r < 0) {
+ /* TODO: check if there are cases where an error needs to be set here */
+ goto error;
+ } else if (r == 0) {
+ /* Retry the reading unless the remote was closed */
+ is_eof = ssh_channel_is_eof(sftp->channel);
+ if (is_eof) {
+ goto error;
+ }
+ } else if (ssh_buffer_add_data(packet->payload, buffer, r) == SSH_ERROR) {
+ ssh_set_error_oom(sftp->session);
+ goto error;
+ }
+ size -= r;
+ }
+
+ return packet;
+error:
+ ssh_buffer_free(packet->payload);
+ SAFE_FREE(packet);
+ return NULL;
+}
+
+static void sftp_set_error(sftp_session sftp, int errnum) {
+ if (sftp != NULL) {
+ sftp->errnum = errnum;
+ }
+}
+
+/* Get the last sftp error */
+int sftp_get_error(sftp_session sftp) {
+ if (sftp == NULL) {
+ return -1;
+ }
+
+ return sftp->errnum;
+}
+
+static sftp_message sftp_message_new(sftp_session sftp){
+ sftp_message msg = NULL;
+
+ msg = calloc(1, sizeof(struct sftp_message_struct));
+ if (msg == NULL) {
+ ssh_set_error_oom(sftp->session);
+ return NULL;
+ }
+
+ msg->payload = ssh_buffer_new();
+ if (msg->payload == NULL) {
+ ssh_set_error_oom(sftp->session);
+ SAFE_FREE(msg);
+ return NULL;
+ }
+ msg->sftp = sftp;
+
+ return msg;
+}
+
+static void sftp_message_free(sftp_message msg) {
+ if (msg == NULL) {
+ return;
+ }
+
+ ssh_buffer_free(msg->payload);
+ SAFE_FREE(msg);
+}
+
+static sftp_message sftp_get_message(sftp_packet packet) {
+ sftp_session sftp = packet->sftp;
+ sftp_message msg = NULL;
+ int rc;
+
+ msg = sftp_message_new(sftp);
+ if (msg == NULL) {
+ return NULL;
+ }
+
+ msg->sftp = packet->sftp;
+ msg->packet_type = packet->type;
+
+ if ((packet->type != SSH_FXP_STATUS) && (packet->type!=SSH_FXP_HANDLE) &&
+ (packet->type != SSH_FXP_DATA) && (packet->type != SSH_FXP_ATTRS) &&
+ (packet->type != SSH_FXP_NAME) && (packet->type != SSH_FXP_EXTENDED_REPLY)) {
+ ssh_set_error(packet->sftp->session, SSH_FATAL,
+ "Unknown packet type %d", packet->type);
+ sftp_message_free(msg);
+ return NULL;
+ }
+
+ rc = ssh_buffer_unpack(packet->payload, "d", &msg->id);
+ if (rc != SSH_OK) {
+ ssh_set_error(packet->sftp->session, SSH_FATAL,
+ "Invalid packet %d: no ID", packet->type);
+ sftp_message_free(msg);
+ return NULL;
+ }
+
+ SSH_LOG(SSH_LOG_PACKET,
+ "Packet with id %d type %d",
+ msg->id,
+ msg->packet_type);
+
+ if (ssh_buffer_add_data(msg->payload, ssh_buffer_get(packet->payload),
+ ssh_buffer_get_len(packet->payload)) < 0) {
+ ssh_set_error_oom(sftp->session);
+ sftp_message_free(msg);
+ return NULL;
+ }
+
+ return msg;
+}
+
+static int sftp_read_and_dispatch(sftp_session sftp) {
+ sftp_packet packet = NULL;
+ sftp_message msg = NULL;
+
+ packet = sftp_packet_read(sftp);
+ if (packet == NULL) {
+ return -1; /* something nasty happened reading the packet */
+ }
+
+ msg = sftp_get_message(packet);
+ sftp_packet_free(packet);
+ if (msg == NULL) {
+ return -1;
+ }
+
+ if (sftp_enqueue(sftp, msg) < 0) {
+ sftp_message_free(msg);
+ return -1;
+ }
+
+ return 0;
+}
+
+void sftp_packet_free(sftp_packet packet) {
+ if (packet == NULL) {
+ return;
+ }
+
+ ssh_buffer_free(packet->payload);
+ free(packet);
+}
+
+/* Initialize the sftp session with the server. */
+int sftp_init(sftp_session sftp) {
+ sftp_packet packet = NULL;
+ ssh_buffer buffer = NULL;
+ char *ext_name = NULL;
+ char *ext_data = NULL;
+ uint32_t version;
+ int rc;
+
+ buffer = ssh_buffer_new();
+ if (buffer == NULL) {
+ ssh_set_error_oom(sftp->session);
+ return -1;
+ }
+
+ rc = ssh_buffer_pack(buffer, "d", LIBSFTP_VERSION);
+ if (rc != SSH_OK) {
+ ssh_set_error_oom(sftp->session);
+ ssh_buffer_free(buffer);
+ return -1;
+ }
+ if (sftp_packet_write(sftp, SSH_FXP_INIT, buffer) < 0) {
+ ssh_buffer_free(buffer);
+ return -1;
+ }
+ ssh_buffer_free(buffer);
+
+ packet = sftp_packet_read(sftp);
+ if (packet == NULL) {
+ return -1;
+ }
+
+ if (packet->type != SSH_FXP_VERSION) {
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Received a %d messages instead of SSH_FXP_VERSION", packet->type);
+ sftp_packet_free(packet);
+ return -1;
+ }
+
+ /* TODO: are we sure there are 4 bytes ready? */
+ rc = ssh_buffer_unpack(packet->payload, "d", &version);
+ if (rc != SSH_OK){
+ return -1;
+ }
+ SSH_LOG(SSH_LOG_RARE,
+ "SFTP server version %d",
+ version);
+ rc = ssh_buffer_unpack(packet->payload, "s", &ext_name);
+ while (rc == SSH_OK) {
+ int count = sftp->ext->count;
+ char **tmp;
+
+ rc = ssh_buffer_unpack(packet->payload, "s", &ext_data);
+ if (rc == SSH_ERROR) {
+ break;
+ }
+
+ SSH_LOG(SSH_LOG_RARE,
+ "SFTP server extension: %s, version: %s",
+ ext_name, ext_data);
+
+ count++;
+ tmp = realloc(sftp->ext->name, count * sizeof(char *));
+ if (tmp == NULL) {
+ ssh_set_error_oom(sftp->session);
+ SAFE_FREE(ext_name);
+ SAFE_FREE(ext_data);
+ return -1;
+ }
+ tmp[count - 1] = ext_name;
+ sftp->ext->name = tmp;
+
+ tmp = realloc(sftp->ext->data, count * sizeof(char *));
+ if (tmp == NULL) {
+ ssh_set_error_oom(sftp->session);
+ SAFE_FREE(ext_name);
+ SAFE_FREE(ext_data);
+ return -1;
+ }
+ tmp[count - 1] = ext_data;
+ sftp->ext->data = tmp;
+
+ sftp->ext->count = count;
+
+ rc = ssh_buffer_unpack(packet->payload, "s", &ext_name);
+ }
+
+ sftp_packet_free(packet);
+
+ sftp->version = sftp->server_version = version;
+
+
+ return 0;
+}
+
+unsigned int sftp_extensions_get_count(sftp_session sftp) {
+ if (sftp == NULL || sftp->ext == NULL) {
+ return 0;
+ }
+
+ return sftp->ext->count;
+}
+
+const char *sftp_extensions_get_name(sftp_session sftp, unsigned int idx) {
+ if (sftp == NULL)
+ return NULL;
+ if (sftp->ext == NULL || sftp->ext->name == NULL) {
+ ssh_set_error_invalid(sftp->session);
+ return NULL;
+ }
+
+ if (idx > sftp->ext->count) {
+ ssh_set_error_invalid(sftp->session);
+ return NULL;
+ }
+
+ return sftp->ext->name[idx];
+}
+
+const char *sftp_extensions_get_data(sftp_session sftp, unsigned int idx) {
+ if (sftp == NULL)
+ return NULL;
+ if (sftp->ext == NULL || sftp->ext->name == NULL) {
+ ssh_set_error_invalid(sftp->session);
+ return NULL;
+ }
+
+ if (idx > sftp->ext->count) {
+ ssh_set_error_invalid(sftp->session);
+ return NULL;
+ }
+
+ return sftp->ext->data[idx];
+}
+
+int sftp_extension_supported(sftp_session sftp, const char *name,
+ const char *data) {
+ int i, n;
+
+ if (sftp == NULL || name == NULL || data == NULL) {
+ return 0;
+ }
+
+ n = sftp_extensions_get_count(sftp);
+ for (i = 0; i < n; i++) {
+ const char *ext_name = sftp_extensions_get_name(sftp, i);
+ const char *ext_data = sftp_extensions_get_data(sftp, i);
+
+ if (ext_name != NULL && ext_data != NULL &&
+ strcmp(ext_name, name) == 0 &&
+ strcmp(ext_data, data) == 0) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static sftp_request_queue request_queue_new(sftp_message msg) {
+ sftp_request_queue queue = NULL;
+
+ queue = calloc(1, sizeof(struct sftp_request_queue_struct));
+ if (queue == NULL) {
+ ssh_set_error_oom(msg->sftp->session);
+ return NULL;
+ }
+
+ queue->message = msg;
+
+ return queue;
+}
+
+static void request_queue_free(sftp_request_queue queue) {
+ if (queue == NULL) {
+ return;
+ }
+
+ ZERO_STRUCTP(queue);
+ SAFE_FREE(queue);
+}
+
+static int sftp_enqueue(sftp_session sftp, sftp_message msg) {
+ sftp_request_queue queue = NULL;
+ sftp_request_queue ptr;
+
+ queue = request_queue_new(msg);
+ if (queue == NULL) {
+ return -1;
+ }
+
+ SSH_LOG(SSH_LOG_PACKET,
+ "Queued msg id %d type %d",
+ msg->id, msg->packet_type);
+
+ if(sftp->queue == NULL) {
+ sftp->queue = queue;
+ } else {
+ ptr = sftp->queue;
+ while(ptr->next) {
+ ptr=ptr->next; /* find end of linked list */
+ }
+ ptr->next = queue; /* add it on bottom */
+ }
+
+ return 0;
+}
+
+/*
+ * Pulls of a message from the queue based on the ID.
+ * Returns NULL if no message has been found.
+ */
+static sftp_message sftp_dequeue(sftp_session sftp, uint32_t id){
+ sftp_request_queue prev = NULL;
+ sftp_request_queue queue;
+ sftp_message msg;
+
+ if(sftp->queue == NULL) {
+ return NULL;
+ }
+
+ queue = sftp->queue;
+ while (queue) {
+ if(queue->message->id == id) {
+ /* remove from queue */
+ if (prev == NULL) {
+ sftp->queue = queue->next;
+ } else {
+ prev->next = queue->next;
+ }
+ msg = queue->message;
+ request_queue_free(queue);
+ SSH_LOG(SSH_LOG_PACKET,
+ "Dequeued msg id %d type %d",
+ msg->id,
+ msg->packet_type);
+ return msg;
+ }
+ prev = queue;
+ queue = queue->next;
+ }
+
+ return NULL;
+}
+
+/*
+ * Assigns a new SFTP ID for new requests and assures there is no collision
+ * between them.
+ * Returns a new ID ready to use in a request
+ */
+static inline uint32_t sftp_get_new_id(sftp_session session) {
+ return ++session->id_counter;
+}
+
+static sftp_status_message parse_status_msg(sftp_message msg){
+ sftp_status_message status;
+ int rc;
+
+ if (msg->packet_type != SSH_FXP_STATUS) {
+ ssh_set_error(msg->sftp->session, SSH_FATAL,
+ "Not a ssh_fxp_status message passed in!");
+ return NULL;
+ }
+
+ status = calloc(1, sizeof(struct sftp_status_message_struct));
+ if (status == NULL) {
+ ssh_set_error_oom(msg->sftp->session);
+ return NULL;
+ }
+
+ status->id = msg->id;
+ rc = ssh_buffer_unpack(msg->payload, "d",
+ &status->status);
+ if (rc != SSH_OK){
+ SAFE_FREE(status);
+ ssh_set_error(msg->sftp->session, SSH_FATAL,
+ "Invalid SSH_FXP_STATUS message");
+ return NULL;
+ }
+ rc = ssh_buffer_unpack(msg->payload, "ss",
+ &status->errormsg,
+ &status->langmsg);
+
+ if(rc != SSH_OK && msg->sftp->version >=3){
+ /* These are mandatory from version 3 */
+ SAFE_FREE(status);
+ ssh_set_error(msg->sftp->session, SSH_FATAL,
+ "Invalid SSH_FXP_STATUS message");
+ return NULL;
+ }
+ if (status->errormsg == NULL)
+ status->errormsg = strdup("No error message in packet");
+ if (status->langmsg == NULL)
+ status->langmsg = strdup("");
+ if (status->errormsg == NULL || status->langmsg == NULL) {
+ ssh_set_error_oom(msg->sftp->session);
+ status_msg_free(status);
+ return NULL;
+ }
+
+ return status;
+}
+
+static void status_msg_free(sftp_status_message status){
+ if (status == NULL) {
+ return;
+ }
+
+ SAFE_FREE(status->errormsg);
+ SAFE_FREE(status->langmsg);
+ SAFE_FREE(status);
+}
+
+static sftp_file parse_handle_msg(sftp_message msg){
+ sftp_file file;
+
+ if(msg->packet_type != SSH_FXP_HANDLE) {
+ ssh_set_error(msg->sftp->session, SSH_FATAL,
+ "Not a ssh_fxp_handle message passed in!");
+ return NULL;
+ }
+
+ file = calloc(1, sizeof(struct sftp_file_struct));
+ if (file == NULL) {
+ ssh_set_error_oom(msg->sftp->session);
+ return NULL;
+ }
+
+ file->handle = ssh_buffer_get_ssh_string(msg->payload);
+ if (file->handle == NULL) {
+ ssh_set_error(msg->sftp->session, SSH_FATAL,
+ "Invalid SSH_FXP_HANDLE message");
+ SAFE_FREE(file);
+ return NULL;
+ }
+
+ file->sftp = msg->sftp;
+ file->offset = 0;
+ file->eof = 0;
+
+ return file;
+}
+
+/* Open a directory */
+sftp_dir sftp_opendir(sftp_session sftp, const char *path){
+ sftp_message msg = NULL;
+ sftp_file file = NULL;
+ sftp_dir dir = NULL;
+ sftp_status_message status;
+ ssh_string path_s;
+ ssh_buffer payload;
+ uint32_t id;
+ int rc;
+
+ payload = ssh_buffer_new();
+ if (payload == NULL) {
+ ssh_set_error_oom(sftp->session);
+ return NULL;
+ }
+
+ path_s = ssh_string_from_char(path);
+ if (path_s == NULL) {
+ ssh_set_error_oom(sftp->session);
+ ssh_buffer_free(payload);
+ return NULL;
+ }
+
+ id = sftp_get_new_id(sftp);
+ rc = ssh_buffer_allocate_size(payload,
+ sizeof(uint32_t) * 2 + ssh_string_len(path_s));
+ if (rc < 0) {
+ ssh_set_error_oom(sftp->session);
+ ssh_buffer_free(payload);
+ ssh_string_free(path_s);
+ return NULL;
+ }
+ if (ssh_buffer_add_u32(payload, htonl(id)) < 0 ||
+ ssh_buffer_add_ssh_string(payload, path_s) < 0) {
+ ssh_set_error_oom(sftp->session);
+ ssh_buffer_free(payload);
+ ssh_string_free(path_s);
+ return NULL;
+ }
+ ssh_string_free(path_s);
+
+ if (sftp_packet_write(sftp, SSH_FXP_OPENDIR, payload) < 0) {
+ ssh_buffer_free(payload);
+ return NULL;
+ }
+ ssh_buffer_free(payload);
+
+ while (msg == NULL) {
+ if (sftp_read_and_dispatch(sftp) < 0) {
+ /* something nasty has happened */
+ return NULL;
+ }
+ msg = sftp_dequeue(sftp, id);
+ }
+
+ switch (msg->packet_type) {
+ case SSH_FXP_STATUS:
+ status = parse_status_msg(msg);
+ sftp_message_free(msg);
+ if (status == NULL) {
+ return NULL;
+ }
+ sftp_set_error(sftp, status->status);
+ ssh_set_error(sftp->session, SSH_REQUEST_DENIED,
+ "SFTP server: %s", status->errormsg);
+ status_msg_free(status);
+ return NULL;
+ case SSH_FXP_HANDLE:
+ file = parse_handle_msg(msg);
+ sftp_message_free(msg);
+ if (file != NULL) {
+ dir = calloc(1, sizeof(struct sftp_dir_struct));
+ if (dir == NULL) {
+ ssh_set_error_oom(sftp->session);
+ free(file);
+ return NULL;
+ }
+
+ dir->sftp = sftp;
+ dir->name = strdup(path);
+ if (dir->name == NULL) {
+ SAFE_FREE(dir);
+ SAFE_FREE(file);
+ return NULL;
+ }
+ dir->handle = file->handle;
+ SAFE_FREE(file);
+ }
+ return dir;
+ default:
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Received message %d during opendir!", msg->packet_type);
+ sftp_message_free(msg);
+ }
+
+ return NULL;
+}
+
+/*
+ * Parse the attributes from a payload from some messages. It is coded on
+ * baselines from the protocol version 4.
+ * This code is more or less dead but maybe we need it in future.
+ */
+static sftp_attributes sftp_parse_attr_4(sftp_session sftp, ssh_buffer buf,
+ int expectnames) {
+ sftp_attributes attr;
+ ssh_string owner = NULL;
+ ssh_string group = NULL;
+ uint32_t flags = 0;
+ int ok = 0;
+
+ /* unused member variable */
+ (void) expectnames;
+
+ attr = calloc(1, sizeof(struct sftp_attributes_struct));
+ if (attr == NULL) {
+ ssh_set_error_oom(sftp->session);
+ return NULL;
+ }
+
+ /* This isn't really a loop, but it is like a try..catch.. */
+ do {
+ if (ssh_buffer_get_u32(buf, &flags) != 4) {
+ break;
+ }
+
+ flags = ntohl(flags);
+ attr->flags = flags;
+
+ if (flags & SSH_FILEXFER_ATTR_SIZE) {
+ if (ssh_buffer_get_u64(buf, &attr->size) != 8) {
+ break;
+ }
+ attr->size = ntohll(attr->size);
+ }
+
+ if (flags & SSH_FILEXFER_ATTR_OWNERGROUP) {
+ owner = ssh_buffer_get_ssh_string(buf);
+ if (owner == NULL) {
+ break;
+ }
+ attr->owner = ssh_string_to_char(owner);
+ ssh_string_free(owner);
+ if (attr->owner == NULL) {
+ break;
+ }
+
+ group = ssh_buffer_get_ssh_string(buf);
+ if (group == NULL) {
+ break;
+ }
+ attr->group = ssh_string_to_char(group);
+ ssh_string_free(group);
+ if (attr->group == NULL) {
+ break;
+ }
+ }
+
+ if (flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
+ if (ssh_buffer_get_u32(buf, &attr->permissions) != 4) {
+ break;
+ }
+ attr->permissions = ntohl(attr->permissions);
+
+ /* FIXME on windows! */
+ switch (attr->permissions & SSH_S_IFMT) {
+ case SSH_S_IFSOCK:
+ case SSH_S_IFBLK:
+ case SSH_S_IFCHR:
+ case SSH_S_IFIFO:
+ attr->type = SSH_FILEXFER_TYPE_SPECIAL;
+ break;
+ case SSH_S_IFLNK:
+ attr->type = SSH_FILEXFER_TYPE_SYMLINK;
+ break;
+ case SSH_S_IFREG:
+ attr->type = SSH_FILEXFER_TYPE_REGULAR;
+ break;
+ case SSH_S_IFDIR:
+ attr->type = SSH_FILEXFER_TYPE_DIRECTORY;
+ break;
+ default:
+ attr->type = SSH_FILEXFER_TYPE_UNKNOWN;
+ break;
+ }
+ }
+
+ if (flags & SSH_FILEXFER_ATTR_ACCESSTIME) {
+ if (ssh_buffer_get_u64(buf, &attr->atime64) != 8) {
+ break;
+ }
+ attr->atime64 = ntohll(attr->atime64);
+ }
+
+ if (flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) {
+ if (ssh_buffer_get_u32(buf, &attr->atime_nseconds) != 4) {
+ break;
+ }
+ attr->atime_nseconds = ntohl(attr->atime_nseconds);
+ }
+
+ if (flags & SSH_FILEXFER_ATTR_CREATETIME) {
+ if (ssh_buffer_get_u64(buf, &attr->createtime) != 8) {
+ break;
+ }
+ attr->createtime = ntohll(attr->createtime);
+ }
+
+ if (flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) {
+ if (ssh_buffer_get_u32(buf, &attr->createtime_nseconds) != 4) {
+ break;
+ }
+ attr->createtime_nseconds = ntohl(attr->createtime_nseconds);
+ }
+
+ if (flags & SSH_FILEXFER_ATTR_MODIFYTIME) {
+ if (ssh_buffer_get_u64(buf, &attr->mtime64) != 8) {
+ break;
+ }
+ attr->mtime64 = ntohll(attr->mtime64);
+ }
+
+ if (flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) {
+ if (ssh_buffer_get_u32(buf, &attr->mtime_nseconds) != 4) {
+ break;
+ }
+ attr->mtime_nseconds = ntohl(attr->mtime_nseconds);
+ }
+
+ if (flags & SSH_FILEXFER_ATTR_ACL) {
+ if ((attr->acl = ssh_buffer_get_ssh_string(buf)) == NULL) {
+ break;
+ }
+ }
+
+ if (flags & SSH_FILEXFER_ATTR_EXTENDED) {
+ if (ssh_buffer_get_u32(buf,&attr->extended_count) != 4) {
+ break;
+ }
+ attr->extended_count = ntohl(attr->extended_count);
+
+ while(attr->extended_count &&
+ (attr->extended_type = ssh_buffer_get_ssh_string(buf)) &&
+ (attr->extended_data = ssh_buffer_get_ssh_string(buf))){
+ attr->extended_count--;
+ }
+
+ if (attr->extended_count) {
+ break;
+ }
+ }
+ ok = 1;
+ } while (0);
+
+ if (ok == 0) {
+ /* break issued somewhere */
+ ssh_string_free(attr->acl);
+ ssh_string_free(attr->extended_type);
+ ssh_string_free(attr->extended_data);
+ SAFE_FREE(attr->owner);
+ SAFE_FREE(attr->group);
+ SAFE_FREE(attr);
+
+ ssh_set_error(sftp->session, SSH_FATAL, "Invalid ATTR structure");
+
+ return NULL;
+ }
+
+ return attr;
+}
+
+enum sftp_longname_field_e {
+ SFTP_LONGNAME_PERM = 0,
+ SFTP_LONGNAME_FIXME,
+ SFTP_LONGNAME_OWNER,
+ SFTP_LONGNAME_GROUP,
+ SFTP_LONGNAME_SIZE,
+ SFTP_LONGNAME_DATE,
+ SFTP_LONGNAME_TIME,
+ SFTP_LONGNAME_NAME,
+};
+
+static char *sftp_parse_longname(const char *longname,
+ enum sftp_longname_field_e longname_field) {
+ const char *p, *q;
+ size_t len, field = 0;
+
+ p = longname;
+ /* Find the beginning of the field which is specified by sftp_longanme_field_e. */
+ while(field != longname_field) {
+ if(isspace(*p)) {
+ field++;
+ p++;
+ while(*p && isspace(*p)) {
+ p++;
+ }
+ } else {
+ p++;
+ }
+ }
+
+ q = p;
+ while (! isspace(*q)) {
+ q++;
+ }
+
+ len = q - p;
+
+ return strndup(p, len);
+}
+
+/* sftp version 0-3 code. It is different from the v4 */
+/* maybe a paste of the draft is better than the code */
+/*
+ uint32 flags
+ uint64 size present only if flag SSH_FILEXFER_ATTR_SIZE
+ uint32 uid present only if flag SSH_FILEXFER_ATTR_UIDGID
+ uint32 gid present only if flag SSH_FILEXFER_ATTR_UIDGID
+ uint32 permissions present only if flag SSH_FILEXFER_ATTR_PERMISSIONS
+ uint32 atime present only if flag SSH_FILEXFER_ACMODTIME
+ uint32 mtime present only if flag SSH_FILEXFER_ACMODTIME
+ uint32 extended_count present only if flag SSH_FILEXFER_ATTR_EXTENDED
+ string extended_type
+ string extended_data
+ ... more extended data (extended_type - extended_data pairs),
+ so that number of pairs equals extended_count */
+static sftp_attributes sftp_parse_attr_3(sftp_session sftp, ssh_buffer buf,
+ int expectname) {
+ sftp_attributes attr;
+ int rc;
+
+ attr = calloc(1, sizeof(struct sftp_attributes_struct));
+ if (attr == NULL) {
+ ssh_set_error_oom(sftp->session);
+ return NULL;
+ }
+
+ if (expectname) {
+ rc = ssh_buffer_unpack(buf, "ss",
+ &attr->name,
+ &attr->longname);
+ if (rc != SSH_OK){
+ goto error;
+ }
+ SSH_LOG(SSH_LOG_RARE, "Name: %s", attr->name);
+
+ /* Set owner and group if we talk to openssh and have the longname */
+ if (ssh_get_openssh_version(sftp->session)) {
+ attr->owner = sftp_parse_longname(attr->longname, SFTP_LONGNAME_OWNER);
+ if (attr->owner == NULL) {
+ goto error;
+ }
+
+ attr->group = sftp_parse_longname(attr->longname, SFTP_LONGNAME_GROUP);
+ if (attr->group == NULL) {
+ goto error;
+ }
+ }
+ }
+
+ rc = ssh_buffer_unpack(buf, "d", &attr->flags);
+ if (rc != SSH_OK){
+ goto error;
+ }
+ SSH_LOG(SSH_LOG_RARE,
+ "Flags: %.8lx\n", (long unsigned int) attr->flags);
+
+ if (attr->flags & SSH_FILEXFER_ATTR_SIZE) {
+ rc = ssh_buffer_unpack(buf, "q", &attr->size);
+ if(rc != SSH_OK) {
+ goto error;
+ }
+ SSH_LOG(SSH_LOG_RARE,
+ "Size: %llu\n",
+ (long long unsigned int) attr->size);
+ }
+
+ if (attr->flags & SSH_FILEXFER_ATTR_UIDGID) {
+ rc = ssh_buffer_unpack(buf, "dd",
+ &attr->uid,
+ &attr->gid);
+ if (rc != SSH_OK){
+ goto error;
+ }
+ }
+
+ if (attr->flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
+ rc = ssh_buffer_unpack(buf, "d", &attr->permissions);
+ if (rc != SSH_OK){
+ goto error;
+ }
+
+ switch (attr->permissions & SSH_S_IFMT) {
+ case SSH_S_IFSOCK:
+ case SSH_S_IFBLK:
+ case SSH_S_IFCHR:
+ case SSH_S_IFIFO:
+ attr->type = SSH_FILEXFER_TYPE_SPECIAL;
+ break;
+ case SSH_S_IFLNK:
+ attr->type = SSH_FILEXFER_TYPE_SYMLINK;
+ break;
+ case SSH_S_IFREG:
+ attr->type = SSH_FILEXFER_TYPE_REGULAR;
+ break;
+ case SSH_S_IFDIR:
+ attr->type = SSH_FILEXFER_TYPE_DIRECTORY;
+ break;
+ default:
+ attr->type = SSH_FILEXFER_TYPE_UNKNOWN;
+ break;
+ }
+ }
+
+ if (attr->flags & SSH_FILEXFER_ATTR_ACMODTIME) {
+ rc = ssh_buffer_unpack(buf, "dd",
+ &attr->atime,
+ &attr->mtime);
+ if (rc != SSH_OK){
+ goto error;
+ }
+ }
+
+ if (attr->flags & SSH_FILEXFER_ATTR_EXTENDED) {
+ rc = ssh_buffer_unpack(buf, "d", &attr->extended_count);
+ if (rc != SSH_OK){
+ goto error;
+ }
+
+ if (attr->extended_count > 0){
+ rc = ssh_buffer_unpack(buf, "ss",
+ &attr->extended_type,
+ &attr->extended_data);
+ if (rc != SSH_OK){
+ goto error;
+ }
+ attr->extended_count--;
+ }
+ /* just ignore the remaining extensions */
+
+ while (attr->extended_count > 0){
+ ssh_string tmp1,tmp2;
+ rc = ssh_buffer_unpack(buf, "SS", &tmp1, &tmp2);
+ if (rc != SSH_OK){
+ goto error;
+ }
+ SAFE_FREE(tmp1);
+ SAFE_FREE(tmp2);
+ attr->extended_count--;
+ }
+ }
+
+ return attr;
+
+ error:
+ ssh_string_free(attr->extended_type);
+ ssh_string_free(attr->extended_data);
+ SAFE_FREE(attr->name);
+ SAFE_FREE(attr->longname);
+ SAFE_FREE(attr->owner);
+ SAFE_FREE(attr->group);
+ SAFE_FREE(attr);
+ ssh_set_error(sftp->session, SSH_FATAL, "Invalid ATTR structure");
+
+ return NULL;
+}
+
+/* FIXME is this really needed as a public function? */
+int buffer_add_attributes(ssh_buffer buffer, sftp_attributes attr) {
+ uint32_t flags = (attr ? attr->flags : 0);
+ int rc;
+
+ flags &= (SSH_FILEXFER_ATTR_SIZE | SSH_FILEXFER_ATTR_UIDGID |
+ SSH_FILEXFER_ATTR_PERMISSIONS | SSH_FILEXFER_ATTR_ACMODTIME);
+
+ rc = ssh_buffer_pack(buffer, "d", flags);
+ if (rc != SSH_OK) {
+ return -1;
+ }
+
+ if (attr != NULL) {
+ if (flags & SSH_FILEXFER_ATTR_SIZE) {
+ rc = ssh_buffer_pack(buffer, "q", attr->size);
+ if (rc != SSH_OK) {
+ return -1;
+ }
+ }
+
+ if (flags & SSH_FILEXFER_ATTR_UIDGID) {
+ rc = ssh_buffer_pack(buffer, "dd", attr->uid, attr->gid);
+ if (rc != SSH_OK) {
+ return -1;
+ }
+ }
+
+ if (flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
+ rc = ssh_buffer_pack(buffer, "d", attr->permissions);
+ if (rc != SSH_OK) {
+ return -1;
+ }
+ }
+
+ if (flags & SSH_FILEXFER_ATTR_ACMODTIME) {
+ rc = ssh_buffer_pack(buffer, "dd", attr->atime, attr->mtime);
+ if (rc != SSH_OK) {
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+
+
+sftp_attributes sftp_parse_attr(sftp_session session, ssh_buffer buf,
+ int expectname) {
+ switch(session->version) {
+ case 4:
+ return sftp_parse_attr_4(session, buf, expectname);
+ case 3:
+ case 2:
+ case 1:
+ case 0:
+ return sftp_parse_attr_3(session, buf, expectname);
+ default:
+ ssh_set_error(session->session, SSH_FATAL,
+ "Version %d unsupported by client", session->server_version);
+ return NULL;
+ }
+
+ return NULL;
+}
+
+/* Get the version of the SFTP protocol supported by the server */
+int sftp_server_version(sftp_session sftp) {
+ return sftp->server_version;
+}
+
+/* Get a single file attributes structure of a directory. */
+sftp_attributes sftp_readdir(sftp_session sftp, sftp_dir dir) {
+ sftp_message msg = NULL;
+ sftp_status_message status;
+ sftp_attributes attr;
+ ssh_buffer payload;
+ uint32_t id;
+ int rc;
+
+ if (dir->buffer == NULL) {
+ payload = ssh_buffer_new();
+ if (payload == NULL) {
+ ssh_set_error_oom(sftp->session);
+ return NULL;
+ }
+
+ rc = ssh_buffer_allocate_size(payload,
+ sizeof(uint32_t) * 2 +
+ ssh_string_len(dir->handle));
+ if (rc < 0) {
+ ssh_set_error_oom(sftp->session);
+ ssh_buffer_free(payload);
+ return NULL;
+ }
+ id = sftp_get_new_id(sftp);
+ if (ssh_buffer_add_u32(payload, htonl(id)) < 0 ||
+ ssh_buffer_add_ssh_string(payload, dir->handle) < 0) {
+ ssh_set_error_oom(sftp->session);
+ ssh_buffer_free(payload);
+ return NULL;
+ }
+
+ if (sftp_packet_write(sftp, SSH_FXP_READDIR, payload) < 0) {
+ ssh_buffer_free(payload);
+ return NULL;
+ }
+ ssh_buffer_free(payload);
+
+ SSH_LOG(SSH_LOG_PACKET,
+ "Sent a ssh_fxp_readdir with id %d", id);
+
+ while (msg == NULL) {
+ if (sftp_read_and_dispatch(sftp) < 0) {
+ /* something nasty has happened */
+ return NULL;
+ }
+ msg = sftp_dequeue(sftp, id);
+ }
+
+ switch (msg->packet_type){
+ case SSH_FXP_STATUS:
+ status = parse_status_msg(msg);
+ sftp_message_free(msg);
+ if (status == NULL) {
+ return NULL;
+ }
+ sftp_set_error(sftp, status->status);
+ switch (status->status) {
+ case SSH_FX_EOF:
+ dir->eof = 1;
+ status_msg_free(status);
+ return NULL;
+ default:
+ break;
+ }
+
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Unknown error status: %d", status->status);
+ status_msg_free(status);
+
+ return NULL;
+ case SSH_FXP_NAME:
+ ssh_buffer_get_u32(msg->payload, &dir->count);
+ dir->count = ntohl(dir->count);
+ dir->buffer = msg->payload;
+ msg->payload = NULL;
+ sftp_message_free(msg);
+ break;
+ default:
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Unsupported message back %d", msg->packet_type);
+ sftp_message_free(msg);
+
+ return NULL;
+ }
+ }
+
+ /* now dir->buffer contains a buffer and dir->count != 0 */
+ if (dir->count == 0) {
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Count of files sent by the server is zero, which is invalid, or "
+ "libsftp bug");
+ return NULL;
+ }
+
+ SSH_LOG(SSH_LOG_RARE, "Count is %d", dir->count);
+
+ attr = sftp_parse_attr(sftp, dir->buffer, 1);
+ if (attr == NULL) {
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Couldn't parse the SFTP attributes");
+ return NULL;
+ }
+
+ dir->count--;
+ if (dir->count == 0) {
+ ssh_buffer_free(dir->buffer);
+ dir->buffer = NULL;
+ }
+
+ return attr;
+}
+
+/* Tell if the directory has reached EOF (End Of File). */
+int sftp_dir_eof(sftp_dir dir) {
+ return dir->eof;
+}
+
+/* Free a SFTP_ATTRIBUTE handle */
+void sftp_attributes_free(sftp_attributes file){
+ if (file == NULL) {
+ return;
+ }
+
+ ssh_string_free(file->acl);
+ ssh_string_free(file->extended_data);
+ ssh_string_free(file->extended_type);
+
+ SAFE_FREE(file->name);
+ SAFE_FREE(file->longname);
+ SAFE_FREE(file->group);
+ SAFE_FREE(file->owner);
+
+ SAFE_FREE(file);
+}
+
+static int sftp_handle_close(sftp_session sftp, ssh_string handle) {
+ sftp_status_message status;
+ sftp_message msg = NULL;
+ ssh_buffer buffer = NULL;
+ uint32_t id;
+ int rc;
+
+ buffer = ssh_buffer_new();
+ if (buffer == NULL) {
+ ssh_set_error_oom(sftp->session);
+ return -1;
+ }
+
+ rc = ssh_buffer_allocate_size(buffer,
+ sizeof(uint32_t) * 2 +
+ ssh_string_len(handle));
+ if (rc < 0) {
+ ssh_set_error_oom(sftp->session);
+ ssh_buffer_free(buffer);
+ return -1;
+ }
+
+ id = sftp_get_new_id(sftp);
+ if (ssh_buffer_add_u32(buffer, htonl(id)) < 0 ||
+ ssh_buffer_add_ssh_string(buffer, handle) < 0) {
+ ssh_set_error_oom(sftp->session);
+ ssh_buffer_free(buffer);
+ return -1;
+ }
+ if (sftp_packet_write(sftp, SSH_FXP_CLOSE ,buffer) < 0) {
+ ssh_buffer_free(buffer);
+ return -1;
+ }
+ ssh_buffer_free(buffer);
+
+ while (msg == NULL) {
+ if (sftp_read_and_dispatch(sftp) < 0) {
+ /* something nasty has happened */
+ return -1;
+ }
+ msg = sftp_dequeue(sftp,id);
+ }
+
+ switch (msg->packet_type) {
+ case SSH_FXP_STATUS:
+ status = parse_status_msg(msg);
+ sftp_message_free(msg);
+ if(status == NULL) {
+ return -1;
+ }
+ sftp_set_error(sftp, status->status);
+ switch (status->status) {
+ case SSH_FX_OK:
+ status_msg_free(status);
+ return 0;
+ break;
+ default:
+ break;
+ }
+ ssh_set_error(sftp->session, SSH_REQUEST_DENIED,
+ "SFTP server: %s", status->errormsg);
+ status_msg_free(status);
+ return -1;
+ default:
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Received message %d during sftp_handle_close!", msg->packet_type);
+ sftp_message_free(msg);
+ }
+
+ return -1;
+}
+
+/* Close an open file handle. */
+int sftp_close(sftp_file file){
+ int err = SSH_NO_ERROR;
+
+ SAFE_FREE(file->name);
+ if (file->handle){
+ err = sftp_handle_close(file->sftp,file->handle);
+ ssh_string_free(file->handle);
+ }
+ /* FIXME: check server response and implement errno */
+ SAFE_FREE(file);
+
+ return err;
+}
+
+/* Close an open directory. */
+int sftp_closedir(sftp_dir dir){
+ int err = SSH_NO_ERROR;
+
+ SAFE_FREE(dir->name);
+ if (dir->handle) {
+ err = sftp_handle_close(dir->sftp, dir->handle);
+ ssh_string_free(dir->handle);
+ }
+ /* FIXME: check server response and implement errno */
+ ssh_buffer_free(dir->buffer);
+ SAFE_FREE(dir);
+
+ return err;
+}
+
+/* Open a file on the server. */
+sftp_file sftp_open(sftp_session sftp, const char *file, int flags,
+ mode_t mode) {
+ sftp_message msg = NULL;
+ sftp_status_message status;
+ struct sftp_attributes_struct attr;
+ sftp_file handle;
+ ssh_string filename;
+ ssh_buffer buffer;
+ sftp_attributes stat_data;
+ uint32_t sftp_flags = 0;
+ uint32_t id;
+ int rc;
+
+ buffer = ssh_buffer_new();
+ if (buffer == NULL) {
+ ssh_set_error_oom(sftp->session);
+ return NULL;
+ }
+
+ filename = ssh_string_from_char(file);
+ if (filename == NULL) {
+ ssh_set_error_oom(sftp->session);
+ ssh_buffer_free(buffer);
+ return NULL;
+ }
+
+ ZERO_STRUCT(attr);
+ attr.permissions = mode;
+ attr.flags = SSH_FILEXFER_ATTR_PERMISSIONS;
+
+ if ((flags & O_RDWR) == O_RDWR) {
+ sftp_flags |= (SSH_FXF_WRITE | SSH_FXF_READ);
+ } else if ((flags & O_WRONLY) == O_WRONLY) {
+ sftp_flags |= SSH_FXF_WRITE;
+ } else {
+ sftp_flags |= SSH_FXF_READ;
+ }
+ if ((flags & O_CREAT) == O_CREAT)
+ sftp_flags |= SSH_FXF_CREAT;
+ if ((flags & O_TRUNC) == O_TRUNC)
+ sftp_flags |= SSH_FXF_TRUNC;
+ if ((flags & O_EXCL) == O_EXCL)
+ sftp_flags |= SSH_FXF_EXCL;
+ if ((flags & O_APPEND) == O_APPEND) {
+ sftp_flags |= SSH_FXF_APPEND;
+ }
+ SSH_LOG(SSH_LOG_PACKET,"Opening file %s with sftp flags %x",file,sftp_flags);
+ rc = ssh_buffer_allocate_size(buffer,
+ sizeof(uint32_t) * 4 +
+ ssh_string_len(filename));
+ if (rc < 0) {
+ ssh_set_error_oom(sftp->session);
+ ssh_buffer_free(buffer);
+ ssh_string_free(filename);
+ return NULL;
+ }
+ id = sftp_get_new_id(sftp);
+ if (ssh_buffer_add_u32(buffer, htonl(id)) < 0 ||
+ ssh_buffer_add_ssh_string(buffer, filename) < 0) {
+ ssh_set_error_oom(sftp->session);
+ ssh_buffer_free(buffer);
+ ssh_string_free(filename);
+ return NULL;
+ }
+ ssh_string_free(filename);
+
+ if (ssh_buffer_add_u32(buffer, htonl(sftp_flags)) < 0 ||
+ buffer_add_attributes(buffer, &attr) < 0) {
+ ssh_set_error_oom(sftp->session);
+ ssh_buffer_free(buffer);
+ return NULL;
+ }
+ if (sftp_packet_write(sftp, SSH_FXP_OPEN, buffer) < 0) {
+ ssh_buffer_free(buffer);
+ return NULL;
+ }
+ ssh_buffer_free(buffer);
+
+ while (msg == NULL) {
+ if (sftp_read_and_dispatch(sftp) < 0) {
+ /* something nasty has happened */
+ return NULL;
+ }
+ msg = sftp_dequeue(sftp, id);
+ }
+
+ switch (msg->packet_type) {
+ case SSH_FXP_STATUS:
+ status = parse_status_msg(msg);
+ sftp_message_free(msg);
+ if (status == NULL) {
+ return NULL;
+ }
+ sftp_set_error(sftp, status->status);
+ ssh_set_error(sftp->session, SSH_REQUEST_DENIED,
+ "SFTP server: %s", status->errormsg);
+ status_msg_free(status);
+
+ return NULL;
+ case SSH_FXP_HANDLE:
+ handle = parse_handle_msg(msg);
+ sftp_message_free(msg);
+ if ((flags & O_APPEND) == O_APPEND) {
+ stat_data = sftp_stat(sftp, file);
+ if (stat_data == NULL) {
+ sftp_close(handle);
+ return NULL;
+ }
+ if ((stat_data->flags & SSH_FILEXFER_ATTR_SIZE) != SSH_FILEXFER_ATTR_SIZE) {
+ ssh_set_error(sftp->session,
+ SSH_FATAL,
+ "Cannot open in append mode. Unknown file size.");
+ sftp_close(handle);
+ return NULL;
+ }
+
+ handle->offset = stat_data->size;
+ }
+ return handle;
+ default:
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Received message %d during open!", msg->packet_type);
+ sftp_message_free(msg);
+ }
+
+ return NULL;
+}
+
+void sftp_file_set_nonblocking(sftp_file handle){
+ handle->nonblocking=1;
+}
+void sftp_file_set_blocking(sftp_file handle){
+ handle->nonblocking=0;
+}
+
+/* Read from a file using an opened sftp file handle. */
+ssize_t sftp_read(sftp_file handle, void *buf, size_t count) {
+ sftp_session sftp = handle->sftp;
+ sftp_message msg = NULL;
+ sftp_status_message status;
+ ssh_string datastring;
+ size_t datalen;
+ ssh_buffer buffer;
+ int id;
+ int rc;
+
+ if (handle->eof) {
+ return 0;
+ }
+
+ buffer = ssh_buffer_new();
+ if (buffer == NULL) {
+ ssh_set_error_oom(sftp->session);
+ return -1;
+ }
+
+ id = sftp_get_new_id(handle->sftp);
+
+ rc = ssh_buffer_pack(buffer,
+ "dSqd",
+ id,
+ handle->handle,
+ handle->offset,
+ count);
+ if (rc != SSH_OK){
+ ssh_set_error_oom(sftp->session);
+ ssh_buffer_free(buffer);
+ return -1;
+ }
+ if (sftp_packet_write(handle->sftp, SSH_FXP_READ, buffer) < 0) {
+ ssh_buffer_free(buffer);
+ return -1;
+ }
+ ssh_buffer_free(buffer);
+
+ while (msg == NULL) {
+ if (handle->nonblocking) {
+ if (ssh_channel_poll(handle->sftp->channel, 0) == 0) {
+ /* we cannot block */
+ return 0;
+ }
+ }
+ if (sftp_read_and_dispatch(handle->sftp) < 0) {
+ /* something nasty has happened */
+ return -1;
+ }
+ msg = sftp_dequeue(handle->sftp, id);
+ }
+
+ switch (msg->packet_type) {
+ case SSH_FXP_STATUS:
+ status = parse_status_msg(msg);
+ sftp_message_free(msg);
+ if (status == NULL) {
+ return -1;
+ }
+ sftp_set_error(sftp, status->status);
+ switch (status->status) {
+ case SSH_FX_EOF:
+ handle->eof = 1;
+ status_msg_free(status);
+ return 0;
+ default:
+ break;
+ }
+ ssh_set_error(sftp->session,SSH_REQUEST_DENIED,
+ "SFTP server: %s", status->errormsg);
+ status_msg_free(status);
+ return -1;
+ case SSH_FXP_DATA:
+ datastring = ssh_buffer_get_ssh_string(msg->payload);
+ sftp_message_free(msg);
+ if (datastring == NULL) {
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Received invalid DATA packet from sftp server");
+ return -1;
+ }
+
+ datalen = ssh_string_len(datastring);
+ if (datalen > count) {
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Received a too big DATA packet from sftp server: "
+ "%" PRIdS " and asked for %" PRIdS,
+ datalen, count);
+ ssh_string_free(datastring);
+ return -1;
+ }
+ handle->offset += (uint64_t)datalen;
+ memcpy(buf, ssh_string_data(datastring), datalen);
+ ssh_string_free(datastring);
+ return datalen;
+ default:
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Received message %d during read!", msg->packet_type);
+ sftp_message_free(msg);
+ return -1;
+ }
+
+ return -1; /* not reached */
+}
+
+/* Start an asynchronous read from a file using an opened sftp file handle. */
+int sftp_async_read_begin(sftp_file file, uint32_t len){
+ sftp_session sftp = file->sftp;
+ ssh_buffer buffer;
+ uint32_t id;
+ int rc;
+
+ buffer = ssh_buffer_new();
+ if (buffer == NULL) {
+ ssh_set_error_oom(sftp->session);
+ return -1;
+ }
+
+ id = sftp_get_new_id(sftp);
+
+ rc = ssh_buffer_pack(buffer,
+ "dSqd",
+ id,
+ file->handle,
+ file->offset,
+ len);
+ if (rc != SSH_OK) {
+ ssh_set_error_oom(sftp->session);
+ ssh_buffer_free(buffer);
+ return -1;
+ }
+ if (sftp_packet_write(sftp, SSH_FXP_READ, buffer) < 0) {
+ ssh_buffer_free(buffer);
+ return -1;
+ }
+ ssh_buffer_free(buffer);
+
+ file->offset += len; /* assume we'll read len bytes */
+
+ return id;
+}
+
+/* Wait for an asynchronous read to complete and save the data. */
+int sftp_async_read(sftp_file file, void *data, uint32_t size, uint32_t id){
+ sftp_session sftp;
+ sftp_message msg = NULL;
+ sftp_status_message status;
+ ssh_string datastring;
+ int err = SSH_OK;
+ uint32_t len;
+
+ if (file == NULL) {
+ return SSH_ERROR;
+ }
+ sftp = file->sftp;
+
+ if (file->eof) {
+ return 0;
+ }
+
+ /* handle an existing request */
+ while (msg == NULL) {
+ if (file->nonblocking){
+ if (ssh_channel_poll(sftp->channel, 0) == 0) {
+ /* we cannot block */
+ return SSH_AGAIN;
+ }
+ }
+
+ if (sftp_read_and_dispatch(sftp) < 0) {
+ /* something nasty has happened */
+ return SSH_ERROR;
+ }
+
+ msg = sftp_dequeue(sftp,id);
+ }
+
+ switch (msg->packet_type) {
+ case SSH_FXP_STATUS:
+ status = parse_status_msg(msg);
+ sftp_message_free(msg);
+ if (status == NULL) {
+ return -1;
+ }
+ sftp_set_error(sftp, status->status);
+ if (status->status != SSH_FX_EOF) {
+ ssh_set_error(sftp->session, SSH_REQUEST_DENIED,
+ "SFTP server : %s", status->errormsg);
+ err = SSH_ERROR;
+ } else {
+ file->eof = 1;
+ }
+ status_msg_free(status);
+ return err;
+ case SSH_FXP_DATA:
+ datastring = ssh_buffer_get_ssh_string(msg->payload);
+ sftp_message_free(msg);
+ if (datastring == NULL) {
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Received invalid DATA packet from sftp server");
+ return SSH_ERROR;
+ }
+ if (ssh_string_len(datastring) > size) {
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Received a too big DATA packet from sftp server: "
+ "%" PRIdS " and asked for %u",
+ ssh_string_len(datastring), size);
+ ssh_string_free(datastring);
+ return SSH_ERROR;
+ }
+ len = ssh_string_len(datastring);
+ /* Update the offset with the correct value */
+ file->offset = file->offset - (size - len);
+ memcpy(data, ssh_string_data(datastring), len);
+ ssh_string_free(datastring);
+ return len;
+ default:
+ ssh_set_error(sftp->session,SSH_FATAL,"Received message %d during read!",msg->packet_type);
+ sftp_message_free(msg);
+ return SSH_ERROR;
+ }
+
+ return SSH_ERROR;
+}
+
+ssize_t sftp_write(sftp_file file, const void *buf, size_t count) {
+ sftp_session sftp = file->sftp;
+ sftp_message msg = NULL;
+ sftp_status_message status;
+ ssh_buffer buffer;
+ uint32_t id;
+ int len;
+ int packetlen;
+ int rc;
+
+ buffer = ssh_buffer_new();
+ if (buffer == NULL) {
+ ssh_set_error_oom(sftp->session);
+ return -1;
+ }
+
+ id = sftp_get_new_id(file->sftp);
+
+ rc = ssh_buffer_pack(buffer,
+ "dSqdP",
+ id,
+ file->handle,
+ file->offset,
+ count, /* len of datastring */
+ (size_t)count, buf);
+ if (rc != SSH_OK){
+ ssh_set_error_oom(sftp->session);
+ ssh_buffer_free(buffer);
+ return -1;
+ }
+ packetlen=ssh_buffer_get_len(buffer);
+ len = sftp_packet_write(file->sftp, SSH_FXP_WRITE, buffer);
+ ssh_buffer_free(buffer);
+ if (len < 0) {
+ return -1;
+ } else if (len != packetlen) {
+ SSH_LOG(SSH_LOG_PACKET,
+ "Could not write as much data as expected");
+ }
+
+ while (msg == NULL) {
+ if (sftp_read_and_dispatch(file->sftp) < 0) {
+ /* something nasty has happened */
+ return -1;
+ }
+ msg = sftp_dequeue(file->sftp, id);
+ }
+
+ switch (msg->packet_type) {
+ case SSH_FXP_STATUS:
+ status = parse_status_msg(msg);
+ sftp_message_free(msg);
+ if (status == NULL) {
+ return -1;
+ }
+ sftp_set_error(sftp, status->status);
+ switch (status->status) {
+ case SSH_FX_OK:
+ file->offset += count;
+ status_msg_free(status);
+ return count;
+ default:
+ break;
+ }
+ ssh_set_error(sftp->session, SSH_REQUEST_DENIED,
+ "SFTP server: %s", status->errormsg);
+ file->offset += count;
+ status_msg_free(status);
+ return -1;
+ default:
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Received message %d during write!", msg->packet_type);
+ sftp_message_free(msg);
+ return -1;
+ }
+
+ return -1; /* not reached */
+}
+
+/* Seek to a specific location in a file. */
+int sftp_seek(sftp_file file, uint32_t new_offset) {
+ if (file == NULL) {
+ return -1;
+ }
+
+ file->offset = new_offset;
+ file->eof = 0;
+
+ return 0;
+}
+
+int sftp_seek64(sftp_file file, uint64_t new_offset) {
+ if (file == NULL) {
+ return -1;
+ }
+
+ file->offset = new_offset;
+ file->eof = 0;
+
+ return 0;
+}
+
+/* Report current byte position in file. */
+unsigned long sftp_tell(sftp_file file) {
+ return (unsigned long)file->offset;
+}
+/* Report current byte position in file. */
+uint64_t sftp_tell64(sftp_file file) {
+ return (uint64_t) file->offset;
+}
+
+/* Rewinds the position of the file pointer to the beginning of the file.*/
+void sftp_rewind(sftp_file file) {
+ file->offset = 0;
+ file->eof = 0;
+}
+
+/* code written by Nick */
+int sftp_unlink(sftp_session sftp, const char *file) {
+ sftp_status_message status = NULL;
+ sftp_message msg = NULL;
+ ssh_buffer buffer;
+ uint32_t id;
+ int rc;
+
+ buffer = ssh_buffer_new();
+ if (buffer == NULL) {
+ ssh_set_error_oom(sftp->session);
+ return -1;
+ }
+
+ id = sftp_get_new_id(sftp);
+
+ rc = ssh_buffer_pack(buffer,
+ "ds",
+ id,
+ file);
+ if (rc != SSH_OK) {
+ ssh_set_error_oom(sftp->session);
+ ssh_buffer_free(buffer);
+ return -1;
+ }
+
+ if (sftp_packet_write(sftp, SSH_FXP_REMOVE, buffer) < 0) {
+ ssh_buffer_free(buffer);
+ return -1;
+ }
+ ssh_buffer_free(buffer);
+
+ while (msg == NULL) {
+ if (sftp_read_and_dispatch(sftp)) {
+ return -1;
+ }
+ msg = sftp_dequeue(sftp, id);
+ }
+
+ if (msg->packet_type == SSH_FXP_STATUS) {
+ /* by specification, this command's only supposed to return SSH_FXP_STATUS */
+ status = parse_status_msg(msg);
+ sftp_message_free(msg);
+ if (status == NULL) {
+ return -1;
+ }
+ sftp_set_error(sftp, status->status);
+ switch (status->status) {
+ case SSH_FX_OK:
+ status_msg_free(status);
+ return 0;
+ default:
+ break;
+ }
+
+ /*
+ * The status should be SSH_FX_OK if the command was successful, if it
+ * didn't, then there was an error
+ */
+ ssh_set_error(sftp->session, SSH_REQUEST_DENIED,
+ "SFTP server: %s", status->errormsg);
+ status_msg_free(status);
+ return -1;
+ } else {
+ ssh_set_error(sftp->session,SSH_FATAL,
+ "Received message %d when attempting to remove file", msg->packet_type);
+ sftp_message_free(msg);
+ }
+
+ return -1;
+}
+
+/* code written by Nick */
+int sftp_rmdir(sftp_session sftp, const char *directory) {
+ sftp_status_message status = NULL;
+ sftp_message msg = NULL;
+ ssh_buffer buffer;
+ uint32_t id;
+ int rc;
+
+ buffer = ssh_buffer_new();
+ if (buffer == NULL) {
+ ssh_set_error_oom(sftp->session);
+ return -1;
+ }
+
+ id = sftp_get_new_id(sftp);
+
+ rc = ssh_buffer_pack(buffer,
+ "ds",
+ id,
+ directory);
+ if (rc != SSH_OK) {
+ ssh_set_error_oom(sftp->session);
+ ssh_buffer_free(buffer);
+ return -1;
+ }
+ if (sftp_packet_write(sftp, SSH_FXP_RMDIR, buffer) < 0) {
+ ssh_buffer_free(buffer);
+ return -1;
+ }
+ ssh_buffer_free(buffer);
+
+ while (msg == NULL) {
+ if (sftp_read_and_dispatch(sftp) < 0) {
+ return -1;
+ }
+ msg = sftp_dequeue(sftp, id);
+ }
+
+ /* By specification, this command returns SSH_FXP_STATUS */
+ if (msg->packet_type == SSH_FXP_STATUS) {
+ status = parse_status_msg(msg);
+ sftp_message_free(msg);
+ if (status == NULL) {
+ return -1;
+ }
+ sftp_set_error(sftp, status->status);
+ switch (status->status) {
+ case SSH_FX_OK:
+ status_msg_free(status);
+ return 0;
+ break;
+ default:
+ break;
+ }
+ ssh_set_error(sftp->session, SSH_REQUEST_DENIED,
+ "SFTP server: %s", status->errormsg);
+ status_msg_free(status);
+ return -1;
+ } else {
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Received message %d when attempting to remove directory",
+ msg->packet_type);
+ sftp_message_free(msg);
+ }
+
+ return -1;
+}
+
+/* Code written by Nick */
+int sftp_mkdir(sftp_session sftp, const char *directory, mode_t mode) {
+ sftp_status_message status = NULL;
+ sftp_message msg = NULL;
+ sftp_attributes errno_attr = NULL;
+ struct sftp_attributes_struct attr;
+ ssh_buffer buffer;
+ ssh_string path;
+ uint32_t id;
+ int rc;
+
+ buffer = ssh_buffer_new();
+ if (buffer == NULL) {
+ ssh_set_error_oom(sftp->session);
+ return -1;
+ }
+
+ path = ssh_string_from_char(directory);
+ if (path == NULL) {
+ ssh_set_error_oom(sftp->session);
+ ssh_buffer_free(buffer);
+ return -1;
+ }
+
+ ZERO_STRUCT(attr);
+ attr.permissions = mode;
+ attr.flags = SSH_FILEXFER_ATTR_PERMISSIONS;
+
+ rc = ssh_buffer_allocate_size(buffer,
+ sizeof(uint32_t) * 2 +
+ ssh_string_len(path));
+ if (rc < 0) {
+ ssh_set_error_oom(sftp->session);
+ ssh_buffer_free(buffer);
+ ssh_string_free(path);
+ return -1;
+ }
+
+ id = sftp_get_new_id(sftp);
+ if (ssh_buffer_add_u32(buffer, htonl(id)) < 0 ||
+ ssh_buffer_add_ssh_string(buffer, path) < 0 ||
+ buffer_add_attributes(buffer, &attr) < 0 ||
+ sftp_packet_write(sftp, SSH_FXP_MKDIR, buffer) < 0) {
+ ssh_buffer_free(buffer);
+ ssh_string_free(path);
+ return -1;
+ }
+ ssh_buffer_free(buffer);
+ ssh_string_free(path);
+
+ while (msg == NULL) {
+ if (sftp_read_and_dispatch(sftp) < 0) {
+ return -1;
+ }
+ msg = sftp_dequeue(sftp, id);
+ }
+
+ /* By specification, this command only returns SSH_FXP_STATUS */
+ if (msg->packet_type == SSH_FXP_STATUS) {
+ status = parse_status_msg(msg);
+ sftp_message_free(msg);
+ if (status == NULL) {
+ return -1;
+ }
+ sftp_set_error(sftp, status->status);
+ switch (status->status) {
+ case SSH_FX_FAILURE:
+ /*
+ * mkdir always returns a failure, even if the path already exists.
+ * To be POSIX conform and to be able to map it to EEXIST a stat
+ * call is needed here.
+ */
+ errno_attr = sftp_lstat(sftp, directory);
+ if (errno_attr != NULL) {
+ SAFE_FREE(errno_attr);
+ sftp_set_error(sftp, SSH_FX_FILE_ALREADY_EXISTS);
+ }
+ break;
+ case SSH_FX_OK:
+ status_msg_free(status);
+ return 0;
+ break;
+ default:
+ break;
+ }
+ /*
+ * The status should be SSH_FX_OK if the command was successful, if it
+ * didn't, then there was an error
+ */
+ ssh_set_error(sftp->session, SSH_REQUEST_DENIED,
+ "SFTP server: %s", status->errormsg);
+ status_msg_free(status);
+ return -1;
+ } else {
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Received message %d when attempting to make directory",
+ msg->packet_type);
+ sftp_message_free(msg);
+ }
+
+ return -1;
+}
+
+/* code written by nick */
+int sftp_rename(sftp_session sftp, const char *original, const char *newname) {
+ sftp_status_message status = NULL;
+ sftp_message msg = NULL;
+ ssh_buffer buffer;
+ uint32_t id;
+ int rc;
+
+ buffer = ssh_buffer_new();
+ if (buffer == NULL) {
+ ssh_set_error_oom(sftp->session);
+ return -1;
+ }
+
+ id = sftp_get_new_id(sftp);
+
+ rc = ssh_buffer_pack(buffer,
+ "dss",
+ id,
+ original,
+ newname);
+ if (rc != SSH_OK) {
+ ssh_set_error_oom(sftp->session);
+ ssh_buffer_free(buffer);
+ return -1;
+ }
+
+ if (sftp->version >= 4){
+ /* POSIX rename atomically replaces newpath, we should do the same
+ * only available on >=v4 */
+ ssh_buffer_add_u32(buffer, SSH_FXF_RENAME_OVERWRITE);
+ }
+
+ if (sftp_packet_write(sftp, SSH_FXP_RENAME, buffer) < 0) {
+ ssh_buffer_free(buffer);
+ return -1;
+ }
+ ssh_buffer_free(buffer);
+
+ while (msg == NULL) {
+ if (sftp_read_and_dispatch(sftp) < 0) {
+ return -1;
+ }
+ msg = sftp_dequeue(sftp, id);
+ }
+
+ /* By specification, this command only returns SSH_FXP_STATUS */
+ if (msg->packet_type == SSH_FXP_STATUS) {
+ status = parse_status_msg(msg);
+ sftp_message_free(msg);
+ if (status == NULL) {
+ return -1;
+ }
+ sftp_set_error(sftp, status->status);
+ switch (status->status) {
+ case SSH_FX_OK:
+ status_msg_free(status);
+ return 0;
+ default:
+ break;
+ }
+ /*
+ * Status should be SSH_FX_OK if the command was successful, if it didn't,
+ * then there was an error
+ */
+ ssh_set_error(sftp->session, SSH_REQUEST_DENIED,
+ "SFTP server: %s", status->errormsg);
+ status_msg_free(status);
+ return -1;
+ } else {
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Received message %d when attempting to rename",
+ msg->packet_type);
+ sftp_message_free(msg);
+ }
+
+ return -1;
+}
+
+/* Code written by Nick */
+/* Set file attributes on a file, directory or symbolic link. */
+int sftp_setstat(sftp_session sftp, const char *file, sftp_attributes attr) {
+ uint32_t id;
+ ssh_buffer buffer;
+ ssh_string path;
+ sftp_message msg = NULL;
+ sftp_status_message status = NULL;
+ int rc;
+
+ buffer = ssh_buffer_new();
+ if (buffer == NULL) {
+ ssh_set_error_oom(sftp->session);
+ return -1;
+ }
+
+ path = ssh_string_from_char(file);
+ if (path == NULL) {
+ ssh_set_error_oom(sftp->session);
+ ssh_buffer_free(buffer);
+ return -1;
+ }
+
+ rc = ssh_buffer_allocate_size(buffer,
+ sizeof(uint32_t) * 2 +
+ ssh_string_len(path));
+ if (rc < 0) {
+ ssh_set_error_oom(sftp->session);
+ ssh_buffer_free(buffer);
+ ssh_string_free(path);
+ return -1;
+ }
+
+ id = sftp_get_new_id(sftp);
+ if (ssh_buffer_add_u32(buffer, htonl(id)) < 0 ||
+ ssh_buffer_add_ssh_string(buffer, path) < 0 ||
+ buffer_add_attributes(buffer, attr) < 0) {
+ ssh_set_error_oom(sftp->session);
+ ssh_buffer_free(buffer);
+ ssh_string_free(path);
+ return -1;
+ }
+ if (sftp_packet_write(sftp, SSH_FXP_SETSTAT, buffer) < 0) {
+ ssh_buffer_free(buffer);
+ ssh_string_free(path);
+ return -1;
+ }
+ ssh_buffer_free(buffer);
+ ssh_string_free(path);
+
+ while (msg == NULL) {
+ if (sftp_read_and_dispatch(sftp) < 0) {
+ return -1;
+ }
+ msg = sftp_dequeue(sftp, id);
+ }
+
+ /* By specification, this command only returns SSH_FXP_STATUS */
+ if (msg->packet_type == SSH_FXP_STATUS) {
+ status = parse_status_msg(msg);
+ sftp_message_free(msg);
+ if (status == NULL) {
+ return -1;
+ }
+ sftp_set_error(sftp, status->status);
+ switch (status->status) {
+ case SSH_FX_OK:
+ status_msg_free(status);
+ return 0;
+ default:
+ break;
+ }
+ /*
+ * The status should be SSH_FX_OK if the command was successful, if it
+ * didn't, then there was an error
+ */
+ ssh_set_error(sftp->session, SSH_REQUEST_DENIED,
+ "SFTP server: %s", status->errormsg);
+ status_msg_free(status);
+ return -1;
+ } else {
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Received message %d when attempting to set stats", msg->packet_type);
+ sftp_message_free(msg);
+ }
+
+ return -1;
+}
+
+/* Change the file owner and group */
+int sftp_chown(sftp_session sftp, const char *file, uid_t owner, gid_t group) {
+ struct sftp_attributes_struct attr;
+ ZERO_STRUCT(attr);
+
+ attr.uid = owner;
+ attr.gid = group;
+
+ attr.flags = SSH_FILEXFER_ATTR_UIDGID;
+
+ return sftp_setstat(sftp, file, &attr);
+}
+
+/* Change permissions of a file */
+int sftp_chmod(sftp_session sftp, const char *file, mode_t mode) {
+ struct sftp_attributes_struct attr;
+ ZERO_STRUCT(attr);
+ attr.permissions = mode;
+ attr.flags = SSH_FILEXFER_ATTR_PERMISSIONS;
+
+ return sftp_setstat(sftp, file, &attr);
+}
+
+/* Change the last modification and access time of a file. */
+int sftp_utimes(sftp_session sftp, const char *file,
+ const struct timeval *times) {
+ struct sftp_attributes_struct attr;
+ ZERO_STRUCT(attr);
+
+ attr.atime = times[0].tv_sec;
+ attr.atime_nseconds = times[0].tv_usec;
+
+ attr.mtime = times[1].tv_sec;
+ attr.mtime_nseconds = times[1].tv_usec;
+
+ attr.flags |= SSH_FILEXFER_ATTR_ACCESSTIME | SSH_FILEXFER_ATTR_MODIFYTIME |
+ SSH_FILEXFER_ATTR_SUBSECOND_TIMES;
+
+ return sftp_setstat(sftp, file, &attr);
+}
+
+int sftp_symlink(sftp_session sftp, const char *target, const char *dest) {
+ sftp_status_message status = NULL;
+ sftp_message msg = NULL;
+ ssh_buffer buffer;
+ uint32_t id;
+ int rc;
+
+ if (sftp == NULL)
+ return -1;
+ if (target == NULL || dest == NULL) {
+ ssh_set_error_invalid(sftp->session);
+ return -1;
+ }
+
+ buffer = ssh_buffer_new();
+ if (buffer == NULL) {
+ ssh_set_error_oom(sftp->session);
+ return -1;
+ }
+
+ id = sftp_get_new_id(sftp);
+
+ /* TODO check for version number if they ever fix it. */
+ if (ssh_get_openssh_version(sftp->session)) {
+ rc = ssh_buffer_pack(buffer,
+ "dss",
+ id,
+ target,
+ dest);
+ } else {
+ rc = ssh_buffer_pack(buffer,
+ "dss",
+ id,
+ dest,
+ target);
+ }
+ if (rc != SSH_OK){
+ ssh_set_error_oom(sftp->session);
+ ssh_buffer_free(buffer);
+ return -1;
+ }
+
+ if (sftp_packet_write(sftp, SSH_FXP_SYMLINK, buffer) < 0) {
+ ssh_buffer_free(buffer);
+ return -1;
+ }
+ ssh_buffer_free(buffer);
+
+ while (msg == NULL) {
+ if (sftp_read_and_dispatch(sftp) < 0) {
+ return -1;
+ }
+ msg = sftp_dequeue(sftp, id);
+ }
+
+ /* By specification, this command only returns SSH_FXP_STATUS */
+ if (msg->packet_type == SSH_FXP_STATUS) {
+ status = parse_status_msg(msg);
+ sftp_message_free(msg);
+ if (status == NULL) {
+ return -1;
+ }
+ sftp_set_error(sftp, status->status);
+ switch (status->status) {
+ case SSH_FX_OK:
+ status_msg_free(status);
+ return 0;
+ default:
+ break;
+ }
+ /*
+ * The status should be SSH_FX_OK if the command was successful, if it
+ * didn't, then there was an error
+ */
+ ssh_set_error(sftp->session, SSH_REQUEST_DENIED,
+ "SFTP server: %s", status->errormsg);
+ status_msg_free(status);
+ return -1;
+ } else {
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Received message %d when attempting to set stats", msg->packet_type);
+ sftp_message_free(msg);
+ }
+
+ return -1;
+}
+
+char *sftp_readlink(sftp_session sftp, const char *path) {
+ sftp_status_message status = NULL;
+ sftp_message msg = NULL;
+ ssh_string path_s = NULL;
+ ssh_string link_s = NULL;
+ ssh_buffer buffer;
+ char *lnk;
+ uint32_t ignored;
+ uint32_t id;
+ int rc;
+
+ if (sftp == NULL)
+ return NULL;
+ if (path == NULL) {
+ ssh_set_error_invalid(sftp);
+ return NULL;
+ }
+ if (sftp->version < 3){
+ ssh_set_error(sftp,SSH_REQUEST_DENIED,"sftp version %d does not support sftp_readlink",sftp->version);
+ return NULL;
+ }
+ buffer = ssh_buffer_new();
+ if (buffer == NULL) {
+ ssh_set_error_oom(sftp->session);
+ return NULL;
+ }
+
+ path_s = ssh_string_from_char(path);
+ if (path_s == NULL) {
+ ssh_set_error_oom(sftp->session);
+ ssh_buffer_free(buffer);
+ return NULL;
+ }
+
+ rc = ssh_buffer_allocate_size(buffer,
+ sizeof(uint32_t) * 2 +
+ ssh_string_len(path_s));
+ if (rc < 0) {
+ ssh_set_error_oom(sftp->session);
+ ssh_buffer_free(buffer);
+ ssh_string_free(path_s);
+ return NULL;
+ }
+
+ id = sftp_get_new_id(sftp);
+ if (ssh_buffer_add_u32(buffer, htonl(id)) < 0 ||
+ ssh_buffer_add_ssh_string(buffer, path_s) < 0) {
+ ssh_set_error_oom(sftp->session);
+ ssh_buffer_free(buffer);
+ ssh_string_free(path_s);
+ return NULL;
+ }
+ if (sftp_packet_write(sftp, SSH_FXP_READLINK, buffer) < 0) {
+ ssh_buffer_free(buffer);
+ ssh_string_free(path_s);
+ return NULL;
+ }
+ ssh_buffer_free(buffer);
+ ssh_string_free(path_s);
+
+ while (msg == NULL) {
+ if (sftp_read_and_dispatch(sftp) < 0) {
+ return NULL;
+ }
+ msg = sftp_dequeue(sftp, id);
+ }
+
+ if (msg->packet_type == SSH_FXP_NAME) {
+ /* we don't care about "count" */
+ ssh_buffer_get_u32(msg->payload, &ignored);
+ /* we only care about the file name string */
+ link_s = ssh_buffer_get_ssh_string(msg->payload);
+ sftp_message_free(msg);
+ if (link_s == NULL) {
+ /* TODO: what error to set here? */
+ return NULL;
+ }
+ lnk = ssh_string_to_char(link_s);
+ ssh_string_free(link_s);
+
+ return lnk;
+ } else if (msg->packet_type == SSH_FXP_STATUS) { /* bad response (error) */
+ status = parse_status_msg(msg);
+ sftp_message_free(msg);
+ if (status == NULL) {
+ return NULL;
+ }
+ ssh_set_error(sftp->session, SSH_REQUEST_DENIED,
+ "SFTP server: %s", status->errormsg);
+ status_msg_free(status);
+ } else { /* this shouldn't happen */
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Received message %d when attempting to set stats", msg->packet_type);
+ sftp_message_free(msg);
+ }
+
+ return NULL;
+}
+
+static sftp_statvfs_t sftp_parse_statvfs(sftp_session sftp, ssh_buffer buf) {
+ sftp_statvfs_t statvfs;
+ int rc;
+
+ statvfs = calloc(1, sizeof(struct sftp_statvfs_struct));
+ if (statvfs == NULL) {
+ ssh_set_error_oom(sftp->session);
+ return NULL;
+ }
+
+ rc = ssh_buffer_unpack(buf, "qqqqqqqqqqq",
+ &statvfs->f_bsize, /* file system block size */
+ &statvfs->f_frsize, /* fundamental fs block size */
+ &statvfs->f_blocks, /* number of blocks (unit f_frsize) */
+ &statvfs->f_bfree, /* free blocks in file system */
+ &statvfs->f_bavail, /* free blocks for non-root */
+ &statvfs->f_files, /* total file inodes */
+ &statvfs->f_ffree, /* free file inodes */
+ &statvfs->f_favail, /* free file inodes for to non-root */
+ &statvfs->f_fsid, /* file system id */
+ &statvfs->f_flag, /* bit mask of f_flag values */
+ &statvfs->f_namemax/* maximum filename length */
+ );
+ if (rc != SSH_OK) {
+ SAFE_FREE(statvfs);
+ ssh_set_error(sftp->session, SSH_FATAL, "Invalid statvfs structure");
+ return NULL;
+ }
+
+ return statvfs;
+}
+
+sftp_statvfs_t sftp_statvfs(sftp_session sftp, const char *path) {
+ sftp_status_message status = NULL;
+ sftp_message msg = NULL;
+ ssh_string pathstr;
+ ssh_string ext;
+ ssh_buffer buffer;
+ uint32_t id;
+ int rc;
+
+ if (sftp == NULL)
+ return NULL;
+ if (path == NULL) {
+ ssh_set_error_invalid(sftp->session);
+ return NULL;
+ }
+ if (sftp->version < 3){
+ ssh_set_error(sftp,SSH_REQUEST_DENIED,"sftp version %d does not support sftp_statvfs",sftp->version);
+ return NULL;
+ }
+
+ buffer = ssh_buffer_new();
+ if (buffer == NULL) {
+ ssh_set_error_oom(sftp->session);
+ return NULL;
+ }
+
+ ext = ssh_string_from_char("statvfs@openssh.com");
+ if (ext == NULL) {
+ ssh_set_error_oom(sftp->session);
+ ssh_buffer_free(buffer);
+ return NULL;
+ }
+
+ pathstr = ssh_string_from_char(path);
+ if (pathstr == NULL) {
+ ssh_set_error_oom(sftp->session);
+ ssh_buffer_free(buffer);
+ ssh_string_free(ext);
+ return NULL;
+ }
+
+ rc = ssh_buffer_allocate_size(buffer,
+ sizeof(uint32_t) * 3 +
+ ssh_string_len(ext) +
+ ssh_string_len(pathstr));
+ if (rc < 0) {
+ ssh_set_error_oom(sftp->session);
+ ssh_buffer_free(buffer);
+ ssh_string_free(ext);
+ ssh_string_free(pathstr);
+ return NULL;
+ }
+
+ id = sftp_get_new_id(sftp);
+ if (ssh_buffer_add_u32(buffer, htonl(id)) < 0 ||
+ ssh_buffer_add_ssh_string(buffer, ext) < 0 ||
+ ssh_buffer_add_ssh_string(buffer, pathstr) < 0) {
+ ssh_set_error_oom(sftp->session);
+ ssh_buffer_free(buffer);
+ ssh_string_free(ext);
+ ssh_string_free(pathstr);
+ return NULL;
+ }
+ if (sftp_packet_write(sftp, SSH_FXP_EXTENDED, buffer) < 0) {
+ ssh_buffer_free(buffer);
+ ssh_string_free(ext);
+ ssh_string_free(pathstr);
+ return NULL;
+ }
+ ssh_buffer_free(buffer);
+ ssh_string_free(ext);
+ ssh_string_free(pathstr);
+
+ while (msg == NULL) {
+ if (sftp_read_and_dispatch(sftp) < 0) {
+ return NULL;
+ }
+ msg = sftp_dequeue(sftp, id);
+ }
+
+ if (msg->packet_type == SSH_FXP_EXTENDED_REPLY) {
+ sftp_statvfs_t buf = sftp_parse_statvfs(sftp, msg->payload);
+ sftp_message_free(msg);
+ if (buf == NULL) {
+ return NULL;
+ }
+
+ return buf;
+ } else if (msg->packet_type == SSH_FXP_STATUS) { /* bad response (error) */
+ status = parse_status_msg(msg);
+ sftp_message_free(msg);
+ if (status == NULL) {
+ return NULL;
+ }
+ ssh_set_error(sftp->session, SSH_REQUEST_DENIED,
+ "SFTP server: %s", status->errormsg);
+ status_msg_free(status);
+ } else { /* this shouldn't happen */
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Received message %d when attempting to get statvfs", msg->packet_type);
+ sftp_message_free(msg);
+ }
+
+ return NULL;
+}
+
+int sftp_fsync(sftp_file file)
+{
+ sftp_session sftp;
+ sftp_message msg = NULL;
+ ssh_string ext;
+ ssh_buffer buffer;
+ uint32_t id;
+ int rc;
+
+ if (file == NULL) {
+ return -1;
+ }
+ sftp = file->sftp;
+
+ buffer = ssh_buffer_new();
+ if (buffer == NULL) {
+ ssh_set_error_oom(sftp->session);
+ return -1;
+ }
+
+ ext = ssh_string_from_char("fsync@openssh.com");
+ if (ext == NULL) {
+ ssh_set_error_oom(sftp->session);
+ rc = -1;
+ goto done;
+ }
+
+ rc = ssh_buffer_allocate_size(buffer,
+ sizeof(uint32_t) * 3 +
+ ssh_string_len(ext) +
+ ssh_string_len(file->handle));
+ if (rc < 0) {
+ ssh_set_error_oom(sftp->session);
+ goto done;
+ }
+
+ id = sftp_get_new_id(sftp);
+ rc = ssh_buffer_add_u32(buffer, htonl(id));
+ if (rc < 0) {
+ ssh_set_error_oom(sftp->session);
+ goto done;
+ }
+
+ rc = ssh_buffer_add_ssh_string(buffer, ext);
+ if (rc < 0) {
+ ssh_set_error_oom(sftp->session);
+ goto done;
+ }
+
+ rc = ssh_buffer_add_ssh_string(buffer, file->handle);
+ if (rc < 0) {
+ ssh_set_error_oom(sftp->session);
+ goto done;
+ }
+
+ rc = sftp_packet_write(sftp, SSH_FXP_EXTENDED, buffer);
+ if (rc < 0) {
+ ssh_set_error_oom(sftp->session);
+ goto done;
+ }
+
+ do {
+ rc = sftp_read_and_dispatch(sftp);
+ if (rc < 0) {
+ ssh_set_error_oom(sftp->session);
+ rc = -1;
+ goto done;
+ }
+ msg = sftp_dequeue(sftp, id);
+ } while (msg == NULL);
+
+ /* By specification, this command only returns SSH_FXP_STATUS */
+ if (msg->packet_type == SSH_FXP_STATUS) {
+ sftp_status_message status;
+
+ status = parse_status_msg(msg);
+ sftp_message_free(msg);
+ if (status == NULL) {
+ rc = -1;
+ goto done;
+ }
+
+ sftp_set_error(sftp, status->status);
+ switch (status->status) {
+ case SSH_FX_OK:
+ /* SUCCESS, LEAVE */
+ status_msg_free(status);
+ rc = 0;
+ goto done;
+ default:
+ break;
+ }
+
+ /*
+ * The status should be SSH_FX_OK if the command was successful, if it
+ * didn't, then there was an error
+ */
+ ssh_set_error(sftp->session,
+ SSH_REQUEST_DENIED,
+ "SFTP server: %s",
+ status->errormsg);
+ status_msg_free(status);
+
+ rc = -1;
+ goto done;
+ } else {
+ ssh_set_error(sftp->session,
+ SSH_FATAL,
+ "Received message %d when attempting to set stats",
+ msg->packet_type);
+ sftp_message_free(msg);
+ }
+
+ rc = -1;
+done:
+ ssh_buffer_free(buffer);
+ ssh_string_free(ext);
+
+ return rc;
+}
+
+sftp_statvfs_t sftp_fstatvfs(sftp_file file) {
+ sftp_status_message status = NULL;
+ sftp_message msg = NULL;
+ sftp_session sftp;
+ ssh_string ext;
+ ssh_buffer buffer;
+ uint32_t id;
+ int rc;
+
+ if (file == NULL) {
+ return NULL;
+ }
+ sftp = file->sftp;
+
+ buffer = ssh_buffer_new();
+ if (buffer == NULL) {
+ ssh_set_error_oom(sftp->session);
+ return NULL;
+ }
+
+ ext = ssh_string_from_char("fstatvfs@openssh.com");
+ if (ext == NULL) {
+ ssh_set_error_oom(sftp->session);
+ ssh_buffer_free(buffer);
+ return NULL;
+ }
+
+ rc = ssh_buffer_allocate_size(buffer,
+ sizeof(uint32_t) * 3 +
+ ssh_string_len(ext) +
+ ssh_string_len(file->handle));
+ if (rc < 0) {
+ ssh_set_error_oom(sftp->session);
+ ssh_buffer_free(buffer);
+ ssh_string_free(ext);
+ return NULL;
+ }
+
+ id = sftp_get_new_id(sftp);
+ if (ssh_buffer_add_u32(buffer, htonl(id)) < 0 ||
+ ssh_buffer_add_ssh_string(buffer, ext) < 0 ||
+ ssh_buffer_add_ssh_string(buffer, file->handle) < 0) {
+ ssh_set_error_oom(sftp->session);
+ ssh_buffer_free(buffer);
+ ssh_string_free(ext);
+ return NULL;
+ }
+ if (sftp_packet_write(sftp, SSH_FXP_EXTENDED, buffer) < 0) {
+ ssh_buffer_free(buffer);
+ ssh_string_free(ext);
+ return NULL;
+ }
+ ssh_buffer_free(buffer);
+ ssh_string_free(ext);
+
+ while (msg == NULL) {
+ if (sftp_read_and_dispatch(sftp) < 0) {
+ return NULL;
+ }
+ msg = sftp_dequeue(sftp, id);
+ }
+
+ if (msg->packet_type == SSH_FXP_EXTENDED_REPLY) {
+ sftp_statvfs_t buf = sftp_parse_statvfs(sftp, msg->payload);
+ sftp_message_free(msg);
+ if (buf == NULL) {
+ return NULL;
+ }
+
+ return buf;
+ } else if (msg->packet_type == SSH_FXP_STATUS) { /* bad response (error) */
+ status = parse_status_msg(msg);
+ sftp_message_free(msg);
+ if (status == NULL) {
+ return NULL;
+ }
+ ssh_set_error(sftp->session, SSH_REQUEST_DENIED,
+ "SFTP server: %s", status->errormsg);
+ status_msg_free(status);
+ } else { /* this shouldn't happen */
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Received message %d when attempting to set stats", msg->packet_type);
+ sftp_message_free(msg);
+ }
+
+ return NULL;
+}
+
+void sftp_statvfs_free(sftp_statvfs_t statvfs) {
+ if (statvfs == NULL) {
+ return;
+ }
+
+ SAFE_FREE(statvfs);
+}
+
+/* another code written by Nick */
+char *sftp_canonicalize_path(sftp_session sftp, const char *path) {
+ sftp_status_message status = NULL;
+ sftp_message msg = NULL;
+ ssh_string name = NULL;
+ ssh_string pathstr;
+ ssh_buffer buffer;
+ char *cname;
+ uint32_t ignored;
+ uint32_t id;
+ int rc;
+
+ if (sftp == NULL)
+ return NULL;
+ if (path == NULL) {
+ ssh_set_error_invalid(sftp->session);
+ return NULL;
+ }
+
+ buffer = ssh_buffer_new();
+ if (buffer == NULL) {
+ ssh_set_error_oom(sftp->session);
+ return NULL;
+ }
+
+ pathstr = ssh_string_from_char(path);
+ if (pathstr == NULL) {
+ ssh_set_error_oom(sftp->session);
+ ssh_buffer_free(buffer);
+ return NULL;
+ }
+
+ rc = ssh_buffer_allocate_size(buffer,
+ sizeof(uint32_t) * 2 +
+ ssh_string_len(pathstr));
+ if (rc < 0) {
+ ssh_set_error_oom(sftp->session);
+ ssh_buffer_free(buffer);
+ ssh_string_free(pathstr);
+ return NULL;
+ }
+
+ id = sftp_get_new_id(sftp);
+ if (ssh_buffer_add_u32(buffer, htonl(id)) < 0 ||
+ ssh_buffer_add_ssh_string(buffer, pathstr) < 0) {
+ ssh_set_error_oom(sftp->session);
+ ssh_buffer_free(buffer);
+ ssh_string_free(pathstr);
+ return NULL;
+ }
+ if (sftp_packet_write(sftp, SSH_FXP_REALPATH, buffer) < 0) {
+ ssh_buffer_free(buffer);
+ ssh_string_free(pathstr);
+ return NULL;
+ }
+ ssh_buffer_free(buffer);
+ ssh_string_free(pathstr);
+
+ while (msg == NULL) {
+ if (sftp_read_and_dispatch(sftp) < 0) {
+ return NULL;
+ }
+ msg = sftp_dequeue(sftp, id);
+ }
+
+ if (msg->packet_type == SSH_FXP_NAME) {
+ /* we don't care about "count" */
+ ssh_buffer_get_u32(msg->payload, &ignored);
+ /* we only care about the file name string */
+ name = ssh_buffer_get_ssh_string(msg->payload);
+ sftp_message_free(msg);
+ if (name == NULL) {
+ /* TODO: error message? */
+ return NULL;
+ }
+ cname = ssh_string_to_char(name);
+ ssh_string_free(name);
+ if (cname == NULL) {
+ ssh_set_error_oom(sftp->session);
+ }
+ return cname;
+ } else if (msg->packet_type == SSH_FXP_STATUS) { /* bad response (error) */
+ status = parse_status_msg(msg);
+ sftp_message_free(msg);
+ if (status == NULL) {
+ return NULL;
+ }
+ ssh_set_error(sftp->session, SSH_REQUEST_DENIED,
+ "SFTP server: %s", status->errormsg);
+ status_msg_free(status);
+ } else { /* this shouldn't happen */
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Received message %d when attempting to set stats", msg->packet_type);
+ sftp_message_free(msg);
+ }
+
+ return NULL;
+}
+
+static sftp_attributes sftp_xstat(sftp_session sftp, const char *path,
+ int param) {
+ sftp_status_message status = NULL;
+ sftp_message msg = NULL;
+ ssh_string pathstr;
+ ssh_buffer buffer;
+ uint32_t id;
+ int rc;
+
+ if (path == NULL) {
+ ssh_set_error_invalid(sftp->session);
+ return NULL;
+ }
+
+ buffer = ssh_buffer_new();
+ if (buffer == NULL) {
+ ssh_set_error_oom(sftp->session);
+ return NULL;
+ }
+
+ pathstr = ssh_string_from_char(path);
+ if (pathstr == NULL) {
+ ssh_set_error_oom(sftp->session);
+ ssh_buffer_free(buffer);
+ return NULL;
+ }
+
+ rc = ssh_buffer_allocate_size(buffer,
+ sizeof(uint32_t) * 2 +
+ ssh_string_len(pathstr));
+ if (rc < 0) {
+ ssh_set_error_oom(sftp->session);
+ ssh_buffer_free(buffer);
+ ssh_string_free(pathstr);
+ return NULL;
+ }
+
+ id = sftp_get_new_id(sftp);
+ if (ssh_buffer_add_u32(buffer, htonl(id)) < 0 ||
+ ssh_buffer_add_ssh_string(buffer, pathstr) < 0) {
+ ssh_set_error_oom(sftp->session);
+ ssh_buffer_free(buffer);
+ ssh_string_free(pathstr);
+ return NULL;
+ }
+ if (sftp_packet_write(sftp, param, buffer) < 0) {
+ ssh_buffer_free(buffer);
+ ssh_string_free(pathstr);
+ return NULL;
+ }
+ ssh_buffer_free(buffer);
+ ssh_string_free(pathstr);
+
+ while (msg == NULL) {
+ if (sftp_read_and_dispatch(sftp) < 0) {
+ return NULL;
+ }
+ msg = sftp_dequeue(sftp, id);
+ }
+
+ if (msg->packet_type == SSH_FXP_ATTRS) {
+ sftp_attributes attr = sftp_parse_attr(sftp, msg->payload, 0);
+ sftp_message_free(msg);
+
+ return attr;
+ } else if (msg->packet_type == SSH_FXP_STATUS) {
+ status = parse_status_msg(msg);
+ sftp_message_free(msg);
+ if (status == NULL) {
+ return NULL;
+ }
+ sftp_set_error(sftp, status->status);
+ ssh_set_error(sftp->session, SSH_REQUEST_DENIED,
+ "SFTP server: %s", status->errormsg);
+ status_msg_free(status);
+ return NULL;
+ }
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Received mesg %d during stat()", msg->packet_type);
+ sftp_message_free(msg);
+
+ return NULL;
+}
+
+sftp_attributes sftp_stat(sftp_session session, const char *path) {
+ return sftp_xstat(session, path, SSH_FXP_STAT);
+}
+
+sftp_attributes sftp_lstat(sftp_session session, const char *path) {
+ return sftp_xstat(session, path, SSH_FXP_LSTAT);
+}
+
+sftp_attributes sftp_fstat(sftp_file file) {
+ sftp_status_message status = NULL;
+ sftp_message msg = NULL;
+ ssh_buffer buffer;
+ uint32_t id;
+ int rc;
+
+ buffer = ssh_buffer_new();
+ if (buffer == NULL) {
+ ssh_set_error_oom(file->sftp->session);
+ return NULL;
+ }
+
+ rc = ssh_buffer_allocate_size(buffer,
+ sizeof(uint32_t) * 2 +
+ ssh_string_len(file->handle));
+ if (rc < 0) {
+ ssh_set_error_oom(file->sftp->session);
+ ssh_buffer_free(buffer);
+ return NULL;
+ }
+
+ id = sftp_get_new_id(file->sftp);
+ if (ssh_buffer_add_u32(buffer, htonl(id)) < 0 ||
+ ssh_buffer_add_ssh_string(buffer, file->handle) < 0) {
+ ssh_set_error_oom(file->sftp->session);
+ ssh_buffer_free(buffer);
+ return NULL;
+ }
+ if (sftp_packet_write(file->sftp, SSH_FXP_FSTAT, buffer) < 0) {
+ ssh_buffer_free(buffer);
+ return NULL;
+ }
+ ssh_buffer_free(buffer);
+
+ while (msg == NULL) {
+ if (sftp_read_and_dispatch(file->sftp) < 0) {
+ return NULL;
+ }
+ msg = sftp_dequeue(file->sftp, id);
+ }
+
+ if (msg->packet_type == SSH_FXP_ATTRS){
+ sftp_attributes attr = sftp_parse_attr(file->sftp, msg->payload, 0);
+ sftp_message_free(msg);
+
+ return attr;
+ } else if (msg->packet_type == SSH_FXP_STATUS) {
+ status = parse_status_msg(msg);
+ sftp_message_free(msg);
+ if (status == NULL) {
+ return NULL;
+ }
+ ssh_set_error(file->sftp->session, SSH_REQUEST_DENIED,
+ "SFTP server: %s", status->errormsg);
+ status_msg_free(status);
+
+ return NULL;
+ }
+ ssh_set_error(file->sftp->session, SSH_FATAL,
+ "Received msg %d during fstat()", msg->packet_type);
+ sftp_message_free(msg);
+
+ return NULL;
+}
+
+#endif /* WITH_SFTP */
diff --git a/external/version.ini b/external/version.ini
index 07e5c26..df3ce56 100644
--- a/external/version.ini
+++ b/external/version.ini
@@ -2,7 +2,7 @@
openssl = 1.0.2p,1000210f
libuv = 1.23.0
mbedtls = 2.12.0
-libssh = 0.7.5
+libssh = 0.8.2
jsoncpp = 0.10.6
mongoose = 6.12
diff --git a/server/.idea/server.iml b/server/.idea/server.iml
index f08604b..6d70257 100644
--- a/server/.idea/server.iml
+++ b/server/.idea/server.iml
@@ -1,2 +1,2 @@
-
+
\ No newline at end of file
diff --git a/server/tp_core/protocol/ssh/stdafx.cpp b/server/tp_core/protocol/ssh/stdafx.cpp
index d38f7dd..cdf02c4 100644
--- a/server/tp_core/protocol/ssh/stdafx.cpp
+++ b/server/tp_core/protocol/ssh/stdafx.cpp
@@ -11,9 +11,9 @@
#ifdef EX_OS_WIN32
# ifdef EX_DEBUG
-# pragma comment(lib, "libsshMTd.lib")
+# pragma comment(lib, "..\\..\\..\\..\\external\\libssh\\lib\\debug\\ssh.lib")
# else
-# pragma comment(lib, "libsshMT.lib")
+# pragma comment(lib, "..\\..\\..\\..\\external\\libssh\\lib\\release\\ssh.lib")
# endif
# pragma comment(lib, "libeay32.lib")
# pragma comment(lib, "ws2_32.lib")