001    /*
002     * Copyright (c) 2009 The openGion Project.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *     http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
013     * either express or implied. See the License for the specific language
014     * governing permissions and limitations under the License.
015     */
016    package org.opengion.fukurou.util;
017    
018    import java.io.IOException;
019    import java.util.Map;
020    import java.util.LinkedHashMap ;
021    import java.util.Vector;
022    import java.util.Hashtable;
023    
024    import com.jcraft.jsch.JSch;
025    import com.jcraft.jsch.Session;
026    import com.jcraft.jsch.ChannelSftp;
027    import com.jcraft.jsch.ChannelSftp.LsEntry;
028    import com.jcraft.jsch.SftpATTRS;
029    import com.jcraft.jsch.JSchException;
030    import com.jcraft.jsch.SftpException;
031    
032    
033    /**
034     * SFTPConnect.java は、å?通的に使用されã‚?SFTP関連の基本機è?を実è£?�—たã?クラスですã?
035     *
036     * これは、org.apache.commons.net.ftp.FTPClient をã?ースに開発されてã�?�¾すã?
037     * こã?クラスの実行には、commons-net-ftp-2.0.jar がå¿?¦�ですã?
038     *
039     * -host=SFTPサーバã? -user=ユーザー -passwd=パスワーãƒ?-remoteFile=SFTP先ã?ファイルå�?をå¿??設定しますã?
040     * -localFile=ローカルのファイル名ã?、å¿??ではありませんがã?-command=DEL の場合にのみ不要でありã€?
041     * それ以外ã? command の場合ã?、å¿?¦�ですã?
042     *
043     * -command=[GET/PUT/DEL/GETDIR/PUTDIR/DELDIR] は、SFTPサーバã?に対しての処ç�??方法をæŒ?®šしますã?
044     *   GET:SFTPサーバã?からローカルにファイル転送しまã�?初期値)ã€?
045     *   PUT:ローカルファイルをSFTPサーバã?に PUT(STORE、SAVE、UPLOAD、などと同意èª?しますã?
046     *   DEL:SFTPサーバã?のæŒ?®šã?ファイルを削除しますã?こã?場合ã?みã€?localFile 属æ?のæŒ?®šã?不要ですã?
047     *   GETDIR,PUTDIR,DELDIR:æŒ?®šã?フォルãƒ?»¥下ã?ファイルをå?ç�?�—ますã?
048     *
049     * -mkdirs=[true/false] は、受けå?のファイル(GETæ™?LOCAL、PUTæ™?SFTPサーバã?)に取り込ã‚?ƒ•ァイルのãƒ?‚£レクトリã�?
050     * 存在しなã�??合に、作æ?するかどã�?�‹を指定しまã�?初期値:true)ã€?
051     * 通常、SFTPサーバã?に、フォルãƒ?šŽ層を作æ?してPUTする場合ã?動的にフォルãƒ?šŽ層を作æ?したã�?‚±ースで便利ですã?
052     * é€?�«、フォルãƒ??確定しており、指定フォルãƒ?»¥外に PUT するのはバグってã�?‚‹と事がåˆ?�‹ってã�?‚‹場合には
053     * false に設定して、存在しなã�?ƒ•ォルãƒ?�«PUT しよã�?�¨すると、エラーになるよã�?�«しますã?
054     *
055     * 引数æ–?­—å?中に空白を含ã‚??合ã?、ダブルコーãƒ??ション("") で括って下さã�??
056     * 引数æ–?­—å?の ã€?』ã?前後には、空白は挟めません。å¿?�šã€?key=value の様に
057     * 繋げてくださいã€?
058     *
059     * @og.formSample
060     *  SFTPConnect -host=SFTPサーバã? -user=ユーザー -passwd=パスワーãƒ?-remoteFile=SFTP先ã?ファイルå�?[-localFile=ローカルのファイル名]
061     *                   [-mode=[ASCII/BINARY]  ] [-command=[GET/PUT/DEL/GETDIR/PUTDIR/DELDIR] ] [-passive=[true/false] ]
062     *
063     *    -host=SFTPサーバã?                ?š接続å?のSFTPサーバã?のアドレスまたã?、サーバã?å�?
064     *    -user=ユーザー                    ?š接続するユーザーå�?
065     *    -remoteFile=SFTP先ã?ファイルå�?   ?š接続å?のSFTPサーバã?側のファイル名ã?PUT,GET 関係なくSFTP側としてæŒ?®šしますã?
066     *   [-passwd=パスワード]               ?š接続するユーザーのパスワーãƒ?
067     *   [-localFile=ローカルのファイル名]  ?šローカルのファイル名ã?PUT,GET 関係なくローカルファイルを指定しますã?
068     *   [-port=ポã?ãƒ?]                    ?š接続するサーバã?のポã?トをæŒ?®šしますã?
069     *   [-keyFile=秘å¯?‚­ーファイル ]       ?šå?開キー暗号化方式を利用する場合ã?キーファイル名をæŒ?®šしますã?
070     *   [-command=[GET/PUT/DEL] ]          ?šSFTPサーバã?側での処ç�??方法をæŒ?®šしますã?
071     *             [GETDIR/PUTDIR/DELDIR]]          GET:SFTP⇒LOCAL、PUT:LOCAL⇒SFTP への転送でã�?初期値:GET)
072     *                                              DEL:SFTPファイルを削除しますã?
073     *                                              GETDIR,PUTDIR,DELDIR æŒ?®šã?フォルãƒ?»¥下ã?ファイルをå?ç�?�—ますã?
074     *   [-mkdirs=[true/false]  ]           ?š受けå?ファイル(GETæ™?LOCAL、PUTæ™?SFTPサーバã?)にãƒ?‚£レクトリを作æ?するかどã�?�‹(初期値:true)
075     *                                              (false:ãƒ?‚£レクトリが無ければ、エラーにしますã?)
076     *   [-timeout=タイãƒ?‚¢ウãƒ?秒] ]       ?šDataタイãƒ?‚¢ウãƒ?初期値:600 [秒])
077     *   [-display=[false/true] ]           ?štrueは、検索状況を表示しまã�?初期値:false)
078     *   [-debug=[false|true]   ]           ?šデバッグæƒ??を標準å?力に表示する(true)かしなã�?false)ã�?初期値:false[表示しない])
079     *
080     * @og.rev 5.1.6.0 (2010/05/01) 新規追åŠ?
081     *
082     * @version  5.0
083     * @author       Kazuhiko Hasegawa
084     * @since    JDK5.0,
085     */
086    public final class SFTPConnect extends AbstractConnect {
087            private final JSch jsch;
088    
089            private static final int DEF_PORT       = 22;                   // ポã?ãƒ?
090    
091            private boolean isConnect       = false;                // コネクト済みかどã�?�‹ã€?
092    
093            private String  lastRemoteDir   = "/";          // SFTP先ã?æœ?¾Œに登録したフォルãƒ?��(mkdir の高é?化ã?ため)
094            private String  keyFile                 = null;         // 公開キー暗号化方式を利用する場合ã?キーファイル名をæŒ?®šしますã?
095    
096            private Session         session = null;
097            private ChannelSftp channel     = null;
098    
099            /**
100             * ãƒ?ƒ•ォルトコンストラクター
101             */
102            public SFTPConnect() {
103                    jsch = new JSch();
104            }
105    
106            /**
107             * SFTPサーバã?への接続ã?ログインを行いますã?
108             *
109             * こã?メソãƒ?ƒ‰は、å?期化メソãƒ?ƒ‰ですã?
110             * SFTPサーバã?への接続ã?ログインを行いますã?で、è¤?•°ファイルを転送すã‚?
111             * ケースでは、最初に?‘度ã�?�‘呼び出すだけですã?
112             * 接続å?を変更する場合ã?、もã�?¸?º¦こã?メソãƒ?ƒ‰をコールするå¿?¦�がありますã?
113             * (そã?ような場合ã?ã€??常、オブジェクトを構築しなおす方がよã�?�¨思いますã?)
114             *
115             */
116            @Override
117            public void connect() {
118                    if( isDisplay ) { System.out.println( "CONNECT: HOST=" + host + ",USER=" + user + ",PORT=" + port ); }
119    
120                    // もし、すでに接続されてã�?�Ÿ場合ã?、クロース処ç�?‚’行いますã?
121                    if( isConnect ) { disconnect(); }
122    
123                    // HostKeyチェãƒ?‚¯を行わなã�?
124                    Hashtable<String,String> config = new Hashtable<String,String>();
125                    config.put( "StrictHostKeyChecking", "no" );
126                    JSch.setConfig( config );
127    
128                    // サーバã?に対して接続を行いますã?
129                    try {
130                            if( keyFile == null ) {
131                                    // パスワード認証
132                                    session=jsch.getSession( user, host, getPort( DEF_PORT ) );
133                                    session.setPassword( passwd );
134                            }
135                            else {
136                                    // 公開キー、秘å¯?‚­ー認証
137                                    jsch.addIdentity( keyFile );
138                                    session=jsch.getSession( user, host, getPort( DEF_PORT ) );
139                    //              session.setUserInfo(new MyUserInfo());
140                            }
141    
142                            session.connect( timeout*1000 );                // タイãƒ?‚¢ウトã?設å®?
143    
144                            channel=(ChannelSftp)session.openChannel("sftp");
145                            channel.connect();
146                    }
147                    catch ( JSchException ex ) {
148                            errAppend( "SFTP server refused connection. " );
149                            errAppend( "   host    = [" , host      , "]" );
150                            errAppend( "   user    = [" , user      , "]" );
151                            errAppend( "   port    = [" , port      , "]" );
152                            errAppend( ex );
153                            if( isDebug ) { ex.printStackTrace(); }
154                            disconnect();
155                            throw new RuntimeException( getErrMsg(),ex );
156                    }
157    
158                    isConnect = true;
159            }
160    
161            /**
162             * SFTPサーバã?との接続をクローズしますã?
163             *
164             * ログインされてã�?‚‹場合ã?、ログアウトも行いますã?
165             * コネクトされてã�?‚‹場合ã?、ディスコネクトしますã?
166             *
167             */
168            @Override
169            public void disconnect() {
170                    if( isDisplay ) { System.out.println( "DISCONNECT:" ); }
171    
172                    if( isConnect ) {
173                            isConnect = false;
174                            try {
175                                    channel.disconnect();
176                                    session.disconnect();
177                            }
178                            catch( Throwable th ) {
179                                    errAppend( "disconnect Error." );
180                                    errAppend( th );
181                                    if( isDebug ) { th.printStackTrace(); }
182                                    throw new RuntimeException( getErrMsg(),th );
183                            }
184                    }
185            }
186    
187            /**
188             * command="GET" が指定されたときã?処ç�?‚’行いますã?
189             *
190             * ローカルファイルをã?接続å?のSFTPサーバã?側にアãƒ??ロードしますã?
191             *
192             * @param       localFile       ローカルのファイルå�?
193             * @param       remoteFile      SFTP先ã?ファイルå�?
194             * @throws  JSchException JSCHエラーが発生したとã�?
195             * @throws  SftpException SFTPエラーが発生したとã�?
196             * @throws  IOException  入出力エラーが発生したとã�?
197             */
198            @Override
199            protected void actionGET( final String localFile, final String remoteFile ) throws JSchException, SftpException, IOException {
200                    if( isDebug ) { System.out.println( "GET: " + remoteFile + " => " + localFile ); }
201    
202                    // GET(DOWNLOAD)取得時は、ローカルファイルのãƒ?‚£レクトリを作æ?するå¿?¦�があるã€?
203                    if( isMkdirs ) {
204                            makeLocalDir( localFile );
205                    }
206    
207                    channel.get( remoteFile,localFile );
208            }
209    
210            /**
211             * command="GETDIR" が指定されたときã?処ç�?‚’行いますã?
212             *
213             * 接続å?のSFTPサーバã?側のãƒ?‚£レクトリ以下をローカルãƒ?‚£レクトリに階層構é?のままãƒ?‚¦ンロードしますã?
214             *
215             * @param       localDir        ローカルのãƒ?‚£レクトリå�?
216             * @param       remoteDir       SFTP先ã?ãƒ?‚£レクトリå�?
217             * @throws  JSchException JSCHエラーが発生したとã�?
218             * @throws  SftpException SFTPエラーが発生したとã�?
219             * @throws  IOException  入出力エラーが発生したとã�?
220             */
221            @Override
222            protected void actionGETdir( final String localDir, final String remoteDir )  throws IOException,JSchException,SftpException {
223                    Vector<?> list = channel.ls( remoteDir );
224                    for (int i=0;i<list.size();i++) {
225                            LsEntry entry = (LsEntry)list.get(i);
226                            String rmt = entry.getFilename();
227                            if( ".".equals( rmt ) || "..".equals( rmt ) ) { continue; }             // "." で始まるファイルもあるã?で、equasl 判å®?
228                            SftpATTRS stat = entry.getAttrs();
229                            if( stat.isDir() ) {
230                                    actionGETdir( addFile( localDir,rmt ),addFile( remoteDir,rmt ) );
231                            }
232                            else {
233                                    actionGET( addFile( localDir,rmt ),addFile( remoteDir,rmt ) );
234                            }
235                    }
236            }
237    
238            /**
239             * command="PUT" が指定されたときã?処ç�?‚’行いますã?
240             *
241             * 接続å?のSFTPサーバã?側のファイル名をローカルにãƒ?‚¦ンロードしますã?
242             *
243             * @param       localFile       ローカルのファイルå�?
244             * @param       remoteFile      SFTP先ã?ファイルå�?
245             * @throws JSchException 処ç�?¸­に JSch エラーが発生したå?å�?
246             * @throws SftpException 処ç�?¸­に Sftp エラーが発生したå?å�?
247             */
248            @Override
249            protected void actionPUT( final String localFile, final String remoteFile ) throws JSchException,SftpException {
250                    if( isDebug ) { System.out.println( "PUT: " + localFile + " => " + remoteFile ); }
251    
252                    // PUT(UPLOAD)登録時ã?、リモートファイルのãƒ?‚£レクトリを作æ?するå¿?¦�があるã€?
253                    if( isMkdirs ) {
254                            // 前回のDIRとの比è¼?�§、すでに存在してã�?‚Œば、makeDirectory 処ç�?‚’パスするã€?
255                            int ad = remoteFile.lastIndexOf( '/' ) + 1;             // 区åˆ?‚Šæ–?­—を?‹1するã?
256                            String tmp = remoteFile.substring( 0,ad );
257    
258                            if( ad > 0 && !lastRemoteDir.startsWith( tmp ) ) {
259                                    lastRemoteDir = tmp;
260                                    if( remoteFile.startsWith( "/" ) ) {
261                                            String[] fls = remoteFile.split( "/" );
262                                            channel.cd( "/" );
263                                            for( int i=1; i<fls.length-1; i++ ) {
264                                                    try {
265                            //                              SftpATTRS stat = channel.lstat(fls[i]);         // 存在しなã�?�¨、SftpException
266                                                            channel.cd( fls[i] );                                           // 存在しなã�?�¨、SftpException
267                                                            continue;
268                                                    } catch (SftpException ex) {
269                                                            // ファイルが存在しなã�?�¨ã�?
270                                                            channel.mkdir( fls[i] );
271                                                            channel.cd( fls[i] );
272                                                    }
273                                            }
274                                    }
275                            }
276                    }
277    
278                    channel.put( localFile,remoteFile );
279            }
280    
281            /**
282             * command="DEL" が指定されたときã?処ç�?‚’行いますã?
283             *
284             * 接続å?のSFTPサーバã?側のファイル名を削除しますã?
285             *
286             * @param       remoteFile      SFTP先ã?ファイルå�?
287             * @throws SftpException SFTPサーバã?側のファイル名ã?削除に失敗したとã�?
288             */
289            @Override
290            protected void actionDEL( final String remoteFile ) throws SftpException {
291                    if( isDebug ) { System.out.println( "DEL: " + remoteFile ); }
292    
293                    channel.rm( remoteFile );
294            }
295    
296            /**
297             * command="DELDIR" が指定されたときã?処ç�?‚’行いますã?
298             *
299             * 接続å?のSFTPサーバã?側のãƒ?‚£レクトリ名を削除しますã?
300             *
301             * @param       remoteDir       SFTP先ã?ãƒ?‚£レクトリå�?
302             * @throws SftpException SFTPサーバã?側のãƒ?‚£レクトリ名ã?削除に失敗したとã�?
303             */
304            @Override
305            protected void actionDELdir( final String remoteDir ) throws SftpException {
306    
307                    Vector<?> list = channel.ls( remoteDir );
308                    for (int i=0;i<list.size();i++) {
309                            LsEntry entry = (LsEntry)list.get(i);
310                            String rmt = entry.getFilename();
311                            if( ".".equals( rmt ) || "..".equals( rmt ) ) { continue; }             // "." で始まるファイルもあるã?で、equasl 判å®?
312                            SftpATTRS stat = entry.getAttrs();
313                            if( stat.isDir() ) {
314                                    actionDELdir( addFile( remoteDir,rmt ) );
315                            }
316                            else {
317                                    actionDEL( addFile( remoteDir,rmt ) );
318    //                              channel.rm( addFile( remoteDir,rmt ) );
319                            }
320                    }
321                    channel.rmdir( remoteDir );
322            }
323    
324            /**
325             * 公開キー暗号化方式を利用する場合ã?キーファイル名をæŒ?®šしますã?
326             *
327             * @param       keyFile 秘å¯?‚­ーファイルå�?
328             */
329            public void setKeyFile( final String keyFile ) {
330                    if( keyFile != null ) {
331                            this.keyFile = keyFile ;
332                    }
333            }
334    
335    //      private static class MyUserInfo implements UserInfo {
336    //              public String getPassword() {
337    //                      return null;
338    //              }
339    //              // パスフレーズは空
340    //              public String getPassphrase() {
341    //                      return "";
342    //              }
343    //              public boolean promptPassword(String arg0) {
344    //                      return true;
345    //              }
346    //              public boolean promptPassphrase(String arg0) {
347    //                      return true;
348    //              }
349    //              public boolean promptYesNo(String arg0) {
350    //                      return true;
351    //              }
352    //              public void showMessage(String arg0) {
353    //              }
354    //      }
355    
356    // ******************************************************************************************************* //
357    //       以下ã?単独で使用する場合ã? main処ç�?
358    // ******************************************************************************************************* //
359    
360            private static final Map<String,String> mustProparty   ;          // ?»プロパティ?½å¿??チェãƒ?‚¯用 Map
361            private static final Map<String,String> usableProparty ;          // ?»プロパティ?½整合æ?チェãƒ?‚¯ Map
362    
363            static {
364                    mustProparty = new LinkedHashMap<String,String>();
365                    mustProparty.put( "host",               "接続å?のSFTPサーバã?のアドレスまたã?、サーバã?å�?å¿??)" );
366                    mustProparty.put( "user",               "接続するユーザーå�?å¿??)" );
367                    mustProparty.put( "remoteFile", "接続å?のSFTPサーバã?側のファイルå�?å¿??)" );
368    
369                    usableProparty = new LinkedHashMap<String,String>();
370                    usableProparty.put( "passwd",           "接続するユーザーのパスワーãƒ? );
371                    usableProparty.put( "localFile",        "ローカルのファイルå�? );
372                    usableProparty.put( "port",                     "接続に利用するポã?ト番号を設定しますã?" );
373                    usableProparty.put( "keyFile",          "公開キー暗号化方式を利用する場合ã?キーファイル名をæŒ?®šしますã?" );
374                    usableProparty.put( "command",          "SFTPサーバã?側での処ç�??方æ³?GET/PUT/DEL)を指定しまã�?初期値:GET)" );
375                    usableProparty.put( "mkdirs",           "受け側ファイル(GETæ™?LOCAL、PUTæ™?SFTPサーバã?)にãƒ?‚£レクトリを作æ?するかどã�?�‹(初期値:true)" );
376                    usableProparty.put( "timeout",          "Dataタイãƒ?‚¢ウãƒ?初期値:600 [秒])" );
377                    usableProparty.put( "display",          "[false/true]:trueは、検索状況を表示しまã�?初期値:false)" );
378                    usableProparty.put( "debug",            "ãƒ?ƒ�ãƒ?‚°æƒ??を標準å?力に表示する(true)かしなã�?false)ã�? +
379                                                                                            CR + "(初期値:false:表示しなã�?" );
380            }
381    
382            private static final String[] CMD_LST  = new String[] { "GET","PUT","DEL","GETDIR","PUTDIR","DELDIR" };
383    
384            /**
385             * こã?クラスの動作確認用の、main メソãƒ?ƒ‰ですã?
386             *
387             * @param       args    コマンド引数配å?
388             */
389            public static void main( final String[] args ) {
390                    Argument arg = new Argument( "org.opengion.fukurou.util.SFTPConnect" );
391                    arg.setMustProparty( mustProparty );
392                    arg.setUsableProparty( usableProparty );
393                    arg.setArgument( args );
394    
395                    SFTPConnect sftp = new SFTPConnect();
396    
397                    String host   = arg.getProparty( "host");                       // SFTPサーバã?
398                    String user   = arg.getProparty( "user" );                      // ユーザー
399                    String passwd = arg.getProparty( "passwd" );            // パスワーãƒ?
400    
401                    sftp.setHostUserPass( host , user , passwd );
402    
403                    sftp.setPort(           arg.getProparty( "port"                                 ) );    // 接続に利用するポã?ト番号を設定しますã?
404                    sftp.setKeyFile(        arg.getProparty( "keyFile"                              ) );    // 公開キー暗号化方式を利用する場合ã?キーファイル名をæŒ?®šしますã?
405                    sftp.setMkdirs(         arg.getProparty( "mkdirs"       ,true           ) );    // 受け側ファイルにãƒ?‚£レクトリを作æ?するかどã�?�‹
406                    sftp.setTimeout(        arg.getProparty( "timeout"      ,TIMEOUT        ) );    // Dataタイãƒ?‚¢ウãƒ?初期値:600 [秒])
407                    sftp.setDisplay(        arg.getProparty( "display"      ,false          ) );    // trueは、検索状況を表示しまã�?初期値:false)
408                    sftp.setDebug(          arg.getProparty( "debug"        ,false          ) );    // ãƒ?ƒ�ãƒ?‚°æƒ??を標準å?力に表示する(true)かしなã�?false)ã�?
409    
410                    try {
411                            // コネクトしますã?
412                            sftp.connect();
413    
414                            String command          = arg.getProparty( "command" ,"GET" ,CMD_LST  );        // SFTP処ç�??方法をæŒ?®šしますã?
415                            String localFile        = arg.getProparty( "localFile"  );                                      // ローカルのファイルå�?
416                            String remoteFile       = arg.getProparty( "remoteFile" );                                      // SFTP先ã?ファイルå�?
417    
418                            // command , localFile , remoteFile をå?に、SFTP処ç�?‚’行いますã?
419                            sftp.action( command,localFile,remoteFile );
420                    }
421                    catch( RuntimeException ex ) {
422                            System.err.println( sftp.getErrMsg() );
423                    }
424                    finally {
425                            // ホストとの接続を終äº?�—ますã?
426                            sftp.disconnect();
427                    }
428            }
429    }