sftp:5 解決編その2 SFTP の文字化け問題解決

最終章はソースコードを直接いじり、文字化け問題を解決しよう。


その前に、使用する環境のプロファイルを説明しよう。


使用環境


Debian を用いているため、apt を用いて sshのソースを取得する。

apt-get source ssh


そして、sftp-server.c を以下のように修正。

  #endif
+  
+  #include "sftp_iconv.c"
  
  /* input and output queue */
  Buffer iqueue;
  Buffer oqueue;

sftp_iconv.c を作成する。

#include	<stdio.h>
#include	<stdlib.h>
#include	<string.h>
#include	<iconv.h>
#include	<errno.h>
#include	<dirent.h>
#include	<unistd.h>
#include	<glib.h>

//#define SFTP_ICONV_DEBUG 1
#define SFTP_HIDE_PARENT_DIR 1


#ifdef SFTP_ICONV_DEBUG
#	define DEBUG_FILE	"/tmp/sftp-out.txt"

	void sftp_print(const char* string,const char* printpath);
	void sftp_printInt(const char* string, int i);
	void sftp_putcs(const char* string, const char* printpath,int length);
#else  //SFTP_ICONV_DEBUG
#	define sftp_print(a,b)
#	define sftp_printInt(a,b)
#	define sftp_putcs(a,b,c)
#endif //SFTP_ICONV_DEBUG

#ifdef SFTP_HIDE_PARENT_DIR
	const static char dum_dir=0x00;
	int isUnderHome(const char* path);
#else  //SFTP_HIDE_PARENT_DIR
#	define isUnderHome(a) 1
#endif //SFTP_HIDE_PARENT_DIR



#define SFTP_CLIENT_CHARSET	"CP932"
#define SFTP_SERVER_CHARSET	"UTF-8"

int client_pathLen(const char* client_path){
// SFTP_CLIENT_CHARSET == "CP932"
	const unsigned char* p;
	int r=0;
	if(!client_path)return 0;
	for(p=(const char*)client_path;*p;p++)r++;
	return r;
// #error
}
int server_pathLen(const char* server_path){
// SFTP_SERVER_CHARSET == "UTF-8"
	const unsigned char* p;
	int r=0;
	if(!server_path)return 0;
	for(p=(const char*)server_path;*p;p++)r++;
	return r;
//#error
}


int _conv_open(const char* client_path, int flags, mode_t mode)
{
	int  r;
	char* server_path;

sftp_putcs("[open:start]",client_path,client_pathLen (client_path));
	server_path = g_convert (client_path, client_pathLen (client_path),
		  SFTP_SERVER_CHARSET, SFTP_CLIENT_CHARSET,  NULL, NULL, NULL); 
	if(!server_path) { errno = ENOMEM; return -1; }

	r = open(server_path, flags, mode);
sftp_print("[open:end] %s",server_path);
	g_free(server_path);

	return r;
}


int _conv_creat(const char* client_path, mode_t mode)
{
	int		r;
	char* server_path;

sftp_putcs("[creat:start]",client_path,client_pathLen (client_path));
	server_path = g_convert (client_path, client_pathLen (client_path),
		  SFTP_SERVER_CHARSET, SFTP_CLIENT_CHARSET,  NULL, NULL, NULL); 
	if(!server_path) { errno = ENOMEM; return -1; }

	r = creat(server_path, mode);
sftp_print("[creat:end] %s",server_path);
	g_free(server_path);

	return r;
}


int _conv_mkdir(const char* client_path, mode_t mode)
{
	int		r;
	char* server_path;

sftp_putcs("[mkdir:start]",client_path,client_pathLen (client_path));
	server_path = g_convert (client_path, client_pathLen (client_path),
		  SFTP_SERVER_CHARSET, SFTP_CLIENT_CHARSET,  NULL, NULL, NULL); 
	if(!server_path) { errno = ENOMEM; return -1; }

	r = mkdir(server_path, mode);
sftp_print("[mkdir:end] %s",server_path);
	g_free(server_path);

	return r;
}


int _conv_rmdir(const char* client_path)
{
	int		r;
	char* server_path;

sftp_putcs("[rmdir:start]",client_path,client_pathLen (client_path));
	server_path = g_convert (client_path, client_pathLen (client_path),
		  SFTP_SERVER_CHARSET, SFTP_CLIENT_CHARSET,  NULL, NULL, NULL); 
	if(!server_path) { errno = ENOMEM; return -1; }

	r = rmdir(server_path);
sftp_print("[rmdir:end] %s",server_path);
	g_free(server_path);

	return r;
}


int _conv_unlink(const char* client_path)
{
	int		r;
	char* server_path;

sftp_putcs("[ulink:start]",client_path,client_pathLen (client_path));
	server_path = g_convert (client_path, client_pathLen (client_path),
		  SFTP_SERVER_CHARSET, SFTP_CLIENT_CHARSET,  NULL, NULL, NULL); 
	if(!server_path) { errno = ENOMEM; return -1; }

	r = unlink(server_path);
sftp_print("[ulink:end] %s",server_path);
	g_free(server_path);

	return r;
}


int _conv_remove(const char* client_path)
{
	int		r;
	char* server_path;

sftp_putcs("[remove:start]",client_path,client_pathLen (client_path));
	server_path = g_convert (client_path, client_pathLen (client_path),
		  SFTP_SERVER_CHARSET, SFTP_CLIENT_CHARSET,  NULL, NULL, NULL); 
	if(!server_path) { errno = ENOMEM; return -1; }

	r = remove(server_path);
sftp_print("[remove:end] %s",server_path);
	g_free(server_path);

	return r;
}


int _conv_rename(const char* client_path_old, const char* client_path_new)
{
	int		r;
	char* server_path_old, server_path_new;

sftp_putcs("[rename:start:old]",client_path_old,client_pathLen (client_path_old));
sftp_putcs("[rename:start:new]",client_path_new,client_pathLen (client_path_new));
	
	server_path_old = g_convert (client_path_old, client_pathLen (client_path_old),
		  SFTP_SERVER_CHARSET, SFTP_CLIENT_CHARSET,  NULL, NULL, NULL); 
	if(!server_path_old) { errno = ENOMEM; return -1; }

	server_path_new = (char*)g_convert ((const gchar *)client_path_new, 
		client_pathLen (client_path_new), SFTP_SERVER_CHARSET, SFTP_CLIENT_CHARSET,
		 NULL, NULL, NULL); 
	if(!server_path_new) { g_free(server_path_old);errno = ENOMEM; return -1; }

	r = rename((const char*)server_path_old, (const char*)server_path_new);
sftp_print("[rename:end:old] %s",server_path_old);
sftp_print("[rename:end;new] %s",server_path_new);
	g_free(server_path_old);
	g_free((gchar*)server_path_new);

	return r;
}


#ifdef	_USE_FILE_OFFSET64
int _conv_stat(const char* client_path, struct stat64* buf)
#else
int _conv_stat(const char* client_path, struct stat* buf)
#endif
{
	int  r;
	char* server_path;

sftp_putcs("[stat:start]",client_path,client_pathLen (client_path));
	server_path = g_convert (client_path, client_pathLen (client_path),
		  SFTP_SERVER_CHARSET, SFTP_CLIENT_CHARSET,  NULL, NULL, NULL); 
	if(!server_path) { errno = ENOMEM; return -1; }
	r = stat(server_path, buf);
sftp_print("[stat:end] %s",server_path);
	g_free(server_path);

	return r;
}


#ifdef	_USE_FILE_OFFSET64
int _conv_lstat(const char* client_path, struct stat64* buf)
#else
int _conv_lstat(const char* client_path, struct stat* buf)
#endif
{
	int		r;
	char* server_path;

sftp_putcs("[lstat:start]",client_path,client_pathLen (client_path));
	server_path = g_convert (client_path, client_pathLen (client_path),
		  SFTP_SERVER_CHARSET, SFTP_CLIENT_CHARSET,  NULL, NULL, NULL); 
	if(!server_path) { errno = ENOMEM; return -1; }

	r = lstat(server_path, buf);
sftp_print("[lstat:end] %s",server_path);
	g_free(server_path);

	return r;
}



DIR* _conv_opendir(const char* client_path)
{
	DIR*	dir;
	char* server_path;
	
sftp_putcs("[opendir:start]",client_path,client_pathLen (client_path));

	server_path = g_convert (client_path, client_pathLen (client_path),
		  SFTP_SERVER_CHARSET, SFTP_CLIENT_CHARSET,  NULL, NULL, NULL); 
	if(!(int)server_path) { errno = ENOMEM; return -1; }

sftp_printInt("[isUnderHome:ret]%d",isUnderHome(server_path));
	if(!isUnderHome((const char*)server_path)){ g_free(server_path); return &dum_dir; }

	dir = opendir(server_path);
sftp_print("[opendir:end] %s",server_path);	
	g_free(server_path);

	return dir;
}


#ifdef	_USE_FILE_OFFSET64
struct dirent64* _conv_readdir(DIR* dir)
#else
struct dirent* _conv_readdir(DIR* dir)
#endif
{
	static struct dirent*	d = NULL;
	struct dirent*	r;

	if(dir==(DIR*)&dum_dir) return NULL;
	
	r = readdir(dir);
	if (r == NULL) return NULL;

	if (d) free(d);
	d = malloc(sizeof(struct dirent)+server_pathLen(r->d_name)+1);
	if (!d) { errno = ENOMEM; return NULL; }

	memcpy(d, r, sizeof(struct dirent));

sftp_print("[readdir:start] %s",r->d_name);
	int server_path_len=server_pathLen( r->d_name );
	char* client_path;
	client_path = g_convert (&(r->d_name[0]), server_path_len,
		 SFTP_CLIENT_CHARSET, SFTP_SERVER_CHARSET,  NULL, NULL, NULL); 
	if(!client_path) {/* free(d) ;*/ errno = ENOMEM; return NULL; }
sftp_putcs("[readdir:end]",client_path,client_pathLen (client_path));
	memcpy(&(d->d_name[0]),client_path,server_path_len+1);
	g_free(client_path);

	return d;
}

#ifdef	_USE_FILE_OFFSET64
int _conv_closedir(DIR* dir)
#else
int _conv_closedir(DIR* dir)
#endif
{
	if(dir==(DIR*)&dum_dir)return 0;
	return closedir(dir);
}

char* _conv_realpath(char* client_path, char resolved_path[])
{
	char*	r;
	char*	server_path;
	char*	server_resolved = malloc(MAXPATHLEN+1);
	if(!(int)server_resolved) { errno = ENOMEM; return -1; }

sftp_putcs("[realpath:start]",client_path,client_pathLen (client_path));
	server_path = g_convert (client_path, client_pathLen (client_path),
		  SFTP_SERVER_CHARSET, SFTP_CLIENT_CHARSET,  NULL, NULL, NULL);
	if(!server_path) { free(server_resolved);errno = ENOMEM; return -1; }

	r = realpath(server_path, server_resolved);
	if (r)
	{
sftp_print("[realpath:mid] %s",server_resolved);
		int server_resolved_len=server_pathLen( server_resolved );
		char* client_path2;
		client_path2 = g_convert (server_resolved, server_resolved_len,
			 SFTP_CLIENT_CHARSET, SFTP_SERVER_CHARSET,  NULL, NULL, NULL); 
		if(!client_path2) {
			g_free(server_path);
			free(server_resolved);
			errno = ENOMEM;
			resolved_path[0] = NULL;
			return 0;
		}
sftp_putcs("[realpath:end]",client_path2,client_pathLen (client_path2));
		memcpy(resolved_path,client_path2,client_pathLen(client_path2)+1);
		g_free(client_path2);
	} else {
		resolved_path[0] = NULL;
	}
	g_free(server_path);
	free(server_resolved);

	return r;
}


int _conv_readlink(const char* client_path, char* buf, size_t bufsiz)
{
	int		r;
	char* server_path;

sftp_putcs("[readlink:start]",client_path,client_pathLen (client_path));
	server_path = g_convert (client_path, client_pathLen (client_path),
		  SFTP_SERVER_CHARSET, SFTP_CLIENT_CHARSET,  NULL, NULL, NULL); 
	if(!server_path) { errno = ENOMEM; return -1; }

	r = readlink(server_path, buf, bufsiz);
sftp_print("[readlink:end] %s",server_path);
	g_free(server_path);

	return r;
}


int _conv_symlink(const char* client_path_old, const char* client_path_new)
{
	int		r;
	char* server_path_old, server_path_new;

sftp_putcs("[symlink:start:old]",client_path_old,client_pathLen (client_path_old));
sftp_putcs("[symlink:start:new]",client_path_new,client_pathLen (client_path_new));

	server_path_old = g_convert (client_path_old, client_pathLen (client_path_old),
		  SFTP_SERVER_CHARSET, SFTP_CLIENT_CHARSET,  NULL, NULL, NULL); 
	if(!server_path_old) { errno = ENOMEM; return -1; }

	server_path_new = g_convert (client_path_new, client_pathLen (client_path_new),
		  SFTP_SERVER_CHARSET, SFTP_CLIENT_CHARSET,  NULL, NULL, NULL); 
	if(!server_path_new) { g_free(server_path_old); errno = ENOMEM; return -1; }

	r = symlink(server_path_old, server_path_new);
sftp_print("[symlink:end:old] %s",server_path_old);
sftp_print("[symlink:end:new] %s",server_path_new);
	g_free(server_path_old);
	g_free(server_path_new);

	return r;
}


int _conv_chmod(const char* client_path, mode_t mode)
{
	int		r;
	char* server_path;

sftp_putcs("[chmod:start]",client_path,client_pathLen (client_path));
	server_path = g_convert (client_path, client_pathLen (client_path),
		  SFTP_SERVER_CHARSET, SFTP_CLIENT_CHARSET,  NULL, NULL, NULL); 
	if(!server_path) { errno = ENOMEM; return -1; }

	r = chmod(server_path, mode);
sftp_print("[chmod:end] %s",server_path);
	g_free(server_path);

	return r;
}


int _conv_chown(const char* client_path, uid_t owner, gid_t group)
{
	int		r;
	char* server_path;

sftp_putcs("[chown:start]",client_path,client_pathLen (client_path));
	server_path = g_convert (client_path, client_pathLen (client_path),
		  SFTP_SERVER_CHARSET, SFTP_CLIENT_CHARSET,  NULL, NULL, NULL); 
	if(!server_path) { errno = ENOMEM; return -1; }

	r = chown(server_path, owner, group);
sftp_print("[chown:end] %s",server_path);
	g_free(server_path);

	return r;
}


int _conv_lchown(const char* client_path, uid_t owner, gid_t group)
{
	int		r;
	char* server_path;

sftp_putcs("[lchown:start]",client_path,client_pathLen (client_path));
	server_path = g_convert (client_path, client_pathLen (client_path),
		  SFTP_SERVER_CHARSET, SFTP_CLIENT_CHARSET,  NULL, NULL, NULL); 
	if(!server_path) { errno = ENOMEM; return -1; }

	r = lchown(server_path, owner, group);
sftp_print("[lchown:end] %s",server_path);
	g_free(server_path);

	return r;
}


int _conv_utime(const char* client_path, struct utimbuf* buf)
{
	int		r;
	char* server_path;

sftp_putcs("[utime:start]",client_path,client_pathLen (client_path));
	server_path = g_convert (client_path, client_pathLen (client_path),
		  SFTP_SERVER_CHARSET, SFTP_CLIENT_CHARSET,  NULL, NULL, NULL); 
	if(!server_path) { errno = ENOMEM; return -1; }

	r = utime(server_path, buf);
sftp_print("[utime:end] %s",server_path);
	g_free(server_path);

	return r;
}


int _conv_utimes(const char* client_path, struct timeval* tvp)
{
	int		r;
	char* server_path;

sftp_putcs("[utimes:start]",client_path,client_pathLen (client_path));
	server_path = g_convert (client_path, client_pathLen (client_path),
		  SFTP_SERVER_CHARSET, SFTP_CLIENT_CHARSET,  NULL, NULL, NULL); 
	if(!server_path) { errno = ENOMEM; return -1; }

	r = utimes(server_path, tvp);
sftp_print("[utimes:end] %s",server_path);
	g_free(server_path);

	return r;
}



#ifdef	open
#undef	open
#endif
#ifdef	creat
#undef	creat
#endif

#ifdef	stat
#undef	stat
#endif

#ifdef	lstat
#undef	lstat
#endif

#ifdef	readdir
#undef	readdir
#endif

#define	open		_conv_open
#define	creat		_conv_creat
#define	mkdir		_conv_mkdir
#define	rmdir		_conv_rmdir
#define	unlink		_conv_unlink
#define	rename		_conv_rename
#define	remove(a)		_conv_remove(a)
#define	stat(X, Y)	_conv_stat(X, Y)
#define	lstat(a, b)		_conv_lstat(a, b)
#define	opendir(a)		_conv_opendir(a)
#define	readdir(a)		_conv_readdir(a)
#define	closedir(a)		_conv_closedir(a)
#define	realpath(a, b)	_conv_realpath(a, b)
#define	readlink(a, b, c)	_conv_readlink(a, b, c)
#define	symlink(a, b)		_conv_symlink(a, b)
#define	chmod(a, b)		_conv_chmod(a, b)
#define	chown(a, b, c)		_conv_chown(a, b, c)
#define	lchown(a, b, c)		_conv_lchown(a, b, c)
#define	utime(a, b)		_conv_utime(a, b)
#define	utimes(a, b)		_conv_utimes(a, b)


#ifdef SFTP_ICONV_DEBUG
void sftp_print(const char* string,const char* printpath){
	FILE *sftp_file;
	sftp_file=fopen(DEBUG_FILE,"a+");
	if(!sftp_file)return;

	fprintf(sftp_file,string,printpath);
	fprintf(sftp_file,"\n");
	fclose(sftp_file);
	return;
}
void sftp_printInt(const char* string, int i){
	FILE *sftp_file;

	sftp_file=fopen(DEBUG_FILE,"a+");
	if(!sftp_file)return;

	fprintf(sftp_file,string,i);
	fprintf(sftp_file,"\n");
	fclose(sftp_file);
	return;
}
void sftp_putcs(const char* string, const char* printpath,int length){
	FILE *sftp_file;
	int i;
	sftp_file=fopen(DEBUG_FILE,"a+");
	if(!sftp_file)return;

	fprintf(sftp_file,string);
	for(i=0;i<length;i++) fputc(*printpath++,sftp_file);
	fprintf(sftp_file,"\n");
	fclose(sftp_file);
	return;
}
#endif //SFTP_ICONV_DEBUG

#ifdef SFTP_HIDE_PARENT_DIR
int isUnderHome(const char* path){
	char *home= g_get_home_dir();
	char *p=path;
	int i,j;
	if(!home)return 0;

	sftp_print("[isUnderHome]%s",home);
	i=strlen(home);
	j=strlen(p);
	
	if(home[i-2]=='/')i--;
	if(p[j-2]=='/')j--;
	if(j<i)return 0;
	
	if(memcmp(home,p,i))return 0;
	
	if(p[i]==0)return 1;
	if(p[i]=='/')return 1;
	
	return 0;
}
#endif //SFTP_HIDE_PARENT_DIR

そのあと ./configure し、できた Makefile に glib が利用できるようにする。

 122 .c.o:
 123         $(CC) $(CFLAGS) $(CPPFLAGS) -c $<
 124
+125 sftp-server.cftp-server.o:       sftp-server.c
+126         $(CC) $(CFLAGS) -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include $(CPPFLAGS) -c $<
 127 
 128 LIBCOMPAT=openbsd-compat/libopenbsd-compat.a

 161 sftp-server$(EXEEXT): $(LIBCOMPAT) libssh.a sftp.o sftp-common.o sftp-server.o
-162        $(LD) -o $@ sftp-server.o sftp-common.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) /usr/lib/libglib-2.0.a
+162        $(LD) -o $@ sftp-server.o sftp-common.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS)
 163

後は make し、出来上がった sftp-server モジュールを /usr/libへ持っていく

 # mv sftp-server sftp-server-new
 # mv sftp-server-new /usr/lib

最期は、 sshd_config を修正し、新しく作成した sftp-server モジュールを利用するようにする。

- Subsystem	sftp	/usr/lib/sftp-server
+ Subsystem	sftp	/usr/lib/sftp-server-new


あとは、FileZillaWinSCP からアクセスし、保存したファイルをサーバーにログインしても見られることを確認できれば成功。