summaryrefslogtreecommitdiff
path: root/app/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main')
-rw-r--r--app/src/main/AndroidManifest.xml86
-rw-r--r--app/src/main/java/com/lc8n/blauploader/FileArrayAdapter.java165
-rw-r--r--app/src/main/java/com/lc8n/blauploader/FileBrowser.java289
-rw-r--r--app/src/main/java/com/lc8n/blauploader/FileShare.java513
-rw-r--r--app/src/main/java/com/lc8n/blauploader/FileUpload.java214
-rw-r--r--app/src/main/java/com/lc8n/blauploader/HomeScreen.java57
-rw-r--r--app/src/main/java/com/lc8n/blauploader/ProgressInputStream.java97
-rw-r--r--app/src/main/java/com/lc8n/blauploader/SoundRecorder.java162
-rw-r--r--app/src/main/java/com/lc8n/blauploader/UploadLocation.java129
-rw-r--r--app/src/main/java/com/lc8n/util/FileUploader.java105
-rw-r--r--app/src/main/java/com/lc8n/util/ProgressMultipartEntity.java72
-rw-r--r--app/src/main/java/org/apache/commons/net/DatagramSocketClient.java275
-rw-r--r--app/src/main/java/org/apache/commons/net/DatagramSocketFactory.java67
-rw-r--r--app/src/main/java/org/apache/commons/net/DefaultDatagramSocketFactory.java75
-rw-r--r--app/src/main/java/org/apache/commons/net/DefaultSocketFactory.java165
-rw-r--r--app/src/main/java/org/apache/commons/net/MalformedServerReplyException.java55
-rw-r--r--app/src/main/java/org/apache/commons/net/PrintCommandListener.java52
-rw-r--r--app/src/main/java/org/apache/commons/net/ProtocolCommandEvent.java146
-rw-r--r--app/src/main/java/org/apache/commons/net/ProtocolCommandListener.java59
-rw-r--r--app/src/main/java/org/apache/commons/net/ProtocolCommandSupport.java134
-rw-r--r--app/src/main/java/org/apache/commons/net/SocketClient.java586
-rw-r--r--app/src/main/java/org/apache/commons/net/ftp/Configurable.java35
-rw-r--r--app/src/main/java/org/apache/commons/net/ftp/FTP.java1514
-rw-r--r--app/src/main/java/org/apache/commons/net/ftp/FTPClient.java2448
-rw-r--r--app/src/main/java/org/apache/commons/net/ftp/FTPClientConfig.java580
-rw-r--r--app/src/main/java/org/apache/commons/net/ftp/FTPCommand.java131
-rw-r--r--app/src/main/java/org/apache/commons/net/ftp/FTPConnectionClosedException.java55
-rw-r--r--app/src/main/java/org/apache/commons/net/ftp/FTPFile.java392
-rw-r--r--app/src/main/java/org/apache/commons/net/ftp/FTPFileEntryParser.java152
-rw-r--r--app/src/main/java/org/apache/commons/net/ftp/FTPFileEntryParserImpl.java85
-rw-r--r--app/src/main/java/org/apache/commons/net/ftp/FTPListParseEngine.java290
-rw-r--r--app/src/main/java/org/apache/commons/net/ftp/FTPReply.java240
-rw-r--r--app/src/main/java/org/apache/commons/net/ftp/FTPSClient.java533
-rw-r--r--app/src/main/java/org/apache/commons/net/ftp/FTPSCommand.java50
-rw-r--r--app/src/main/java/org/apache/commons/net/ftp/FTPSSocketFactory.java82
-rw-r--r--app/src/main/java/org/apache/commons/net/ftp/FTPSTrustManager.java54
-rw-r--r--app/src/main/java/org/apache/commons/net/ftp/parser/CompositeFileEntryParser.java72
-rw-r--r--app/src/main/java/org/apache/commons/net/ftp/parser/ConfigurableFTPFileEntryParserImpl.java116
-rw-r--r--app/src/main/java/org/apache/commons/net/ftp/parser/DefaultFTPFileEntryParserFactory.java247
-rw-r--r--app/src/main/java/org/apache/commons/net/ftp/parser/EnterpriseUnixFTPEntryParser.java167
-rw-r--r--app/src/main/java/org/apache/commons/net/ftp/parser/FTPFileEntryParserFactory.java68
-rw-r--r--app/src/main/java/org/apache/commons/net/ftp/parser/FTPTimestampParser.java52
-rw-r--r--app/src/main/java/org/apache/commons/net/ftp/parser/FTPTimestampParserImpl.java301
-rw-r--r--app/src/main/java/org/apache/commons/net/ftp/parser/MVSFTPEntryParser.java495
-rw-r--r--app/src/main/java/org/apache/commons/net/ftp/parser/NTFTPEntryParser.java148
-rw-r--r--app/src/main/java/org/apache/commons/net/ftp/parser/NetwareFTPEntryParser.java176
-rw-r--r--app/src/main/java/org/apache/commons/net/ftp/parser/OS2FTPEntryParser.java148
-rw-r--r--app/src/main/java/org/apache/commons/net/ftp/parser/OS400FTPEntryParser.java158
-rw-r--r--app/src/main/java/org/apache/commons/net/ftp/parser/ParserInitializationException.java65
-rw-r--r--app/src/main/java/org/apache/commons/net/ftp/parser/RegexFTPFileEntryParserImpl.java155
-rw-r--r--app/src/main/java/org/apache/commons/net/ftp/parser/UnixFTPEntryParser.java296
-rw-r--r--app/src/main/java/org/apache/commons/net/ftp/parser/VMSFTPEntryParser.java289
-rw-r--r--app/src/main/java/org/apache/commons/net/ftp/parser/VMSVersioningFTPEntryParser.java183
-rw-r--r--app/src/main/java/org/apache/commons/net/io/CopyStreamAdapter.java122
-rw-r--r--app/src/main/java/org/apache/commons/net/io/CopyStreamEvent.java98
-rw-r--r--app/src/main/java/org/apache/commons/net/io/CopyStreamException.java71
-rw-r--r--app/src/main/java/org/apache/commons/net/io/CopyStreamListener.java72
-rw-r--r--app/src/main/java/org/apache/commons/net/io/DotTerminatedMessageReader.java280
-rw-r--r--app/src/main/java/org/apache/commons/net/io/DotTerminatedMessageWriter.java215
-rw-r--r--app/src/main/java/org/apache/commons/net/io/FromNetASCIIInputStream.java205
-rw-r--r--app/src/main/java/org/apache/commons/net/io/FromNetASCIIOutputStream.java174
-rw-r--r--app/src/main/java/org/apache/commons/net/io/SocketInputStream.java69
-rw-r--r--app/src/main/java/org/apache/commons/net/io/SocketOutputStream.java89
-rw-r--r--app/src/main/java/org/apache/commons/net/io/ToNetASCIIInputStream.java181
-rw-r--r--app/src/main/java/org/apache/commons/net/io/ToNetASCIIOutputStream.java119
-rw-r--r--app/src/main/java/org/apache/commons/net/io/Util.java334
-rw-r--r--app/src/main/java/org/apache/commons/net/util/ListenerList.java63
-rw-r--r--app/src/main/java/org/apache/commons/net/util/SubnetUtils.java211
-rw-r--r--app/src/main/res/drawable-hdpi/icon.pngbin0 -> 8230 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/marcus72.pngbin0 -> 8230 bytes
-rw-r--r--app/src/main/res/drawable-ldpi/binary.pngbin0 -> 310 bytes
-rw-r--r--app/src/main/res/drawable-ldpi/compressed.pngbin0 -> 1108 bytes
-rw-r--r--app/src/main/res/drawable-ldpi/folder.pngbin0 -> 295 bytes
-rw-r--r--app/src/main/res/drawable-ldpi/generic.pngbin0 -> 275 bytes
-rw-r--r--app/src/main/res/drawable-ldpi/icon.pngbin0 -> 2635 bytes
-rw-r--r--app/src/main/res/drawable-ldpi/image.pngbin0 -> 371 bytes
-rw-r--r--app/src/main/res/drawable-ldpi/link.pngbin0 -> 314 bytes
-rw-r--r--app/src/main/res/drawable-ldpi/movie.pngbin0 -> 272 bytes
-rw-r--r--app/src/main/res/drawable-ldpi/music.pngbin0 -> 315 bytes
-rw-r--r--app/src/main/res/drawable-ldpi/text.pngbin0 -> 288 bytes
-rw-r--r--app/src/main/res/drawable-ldpi/unknown.pngbin0 -> 307 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/icon.pngbin0 -> 4195 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/up_arrow.pngbin0 -> 899 bytes
-rw-r--r--app/src/main/res/layout/browser.xml37
-rw-r--r--app/src/main/res/layout/filerow.xml47
-rw-r--r--app/src/main/res/layout/home.xml29
-rw-r--r--app/src/main/res/layout/share.xml82
-rw-r--r--app/src/main/res/layout/soundrecorder.xml19
-rw-r--r--app/src/main/res/layout/upload.xml19
-rw-r--r--app/src/main/res/layout/uploadlocation.xml36
-rw-r--r--app/src/main/res/menu/menu.xml16
-rw-r--r--app/src/main/res/values-v11/strings.xml6
-rw-r--r--app/src/main/res/values-v11/themes.xml6
-rw-r--r--app/src/main/res/values-v14/strings.xml6
-rw-r--r--app/src/main/res/values-v14/themes.xml6
-rw-r--r--app/src/main/res/values/strings.xml11
-rw-r--r--app/src/main/res/values/themes.xml6
97 files changed, 15909 insertions, 0 deletions
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..1c24572
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.lc8n.blauploader"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <uses-sdk
+ android:minSdkVersion="8"
+ android:targetSdkVersion="14" />
+
+ <application
+ android:icon="@drawable/icon"
+ android:label="@string/app_name"
+ android:theme="@style/LightTheme" >
+ <activity
+ android:name="com.lc8n.blauploader.HomeScreen"
+ android:label="@string/app_name" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity
+ android:name="com.lc8n.blauploader.FileBrowser"
+ android:windowSoftInputMode="stateHidden"
+ android:label="@string/app_name" >
+
+ <!--
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ -->
+
+ </activity>
+ <activity
+ android:name="com.lc8n.blauploader.FileShare"
+ android:label="up.org.je"
+ android:windowSoftInputMode="stateAlwaysVisible" >
+ <intent-filter>
+ <action android:name="android.intent.action.SEND" />
+
+ <category android:name="android.intent.category.DEFAULT" />
+
+ <data android:mimeType="*/*" />
+ </intent-filter>
+ </activity>
+ <activity
+ android:name="com.lc8n.blauploader.SoundRecorder"
+ android:label="@string/app_name" >
+
+ <!--
+ intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ -->
+ </activity>
+ <activity
+ android:name="com.lc8n.blauploader.UploadLocation"
+ android:label="@string/app_name" >
+
+ <!--
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ -->
+ </activity>
+ <activity
+ android:name="com.lc8n.blauploader.MainActivity"
+ android:label="@string/title_activity_main" >
+ </activity>
+ </application>
+
+ <uses-permission android:name="android.permission.INTERNET" >
+ </uses-permission>
+ <uses-permission android:name="android.permission.RECORD_AUDIO" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+ <uses-permission android:name="android.permission.SEND_SMS" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+</manifest> \ No newline at end of file
diff --git a/app/src/main/java/com/lc8n/blauploader/FileArrayAdapter.java b/app/src/main/java/com/lc8n/blauploader/FileArrayAdapter.java
new file mode 100644
index 0000000..4c4f7fe
--- /dev/null
+++ b/app/src/main/java/com/lc8n/blauploader/FileArrayAdapter.java
@@ -0,0 +1,165 @@
+package com.lc8n.blauploader;
+
+import android.app.Activity;
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.io.File;
+import java.util.Calendar;
+
+//This class is used to create a list of files
+public class FileArrayAdapter extends ArrayAdapter<File> {
+
+ private Context context;
+ private int layoutResourceId;
+ private File[] files = null;
+
+ public FileArrayAdapter(Context context, int layoutResourceId,
+ File[] files) {
+ super(context, layoutResourceId, files);
+ this.context = context;
+ this.layoutResourceId = layoutResourceId;
+ this.files = files;
+ }
+
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View row = convertView;
+ FileHolder holder = null;
+
+ if(row == null)
+ {
+ LayoutInflater inflater = ((Activity)context).getLayoutInflater();
+ row = inflater.inflate(layoutResourceId, parent, false);
+
+ //Connect the holder to the GUI objects
+ holder = new FileHolder();
+ holder.fileIcon = (ImageView)row.findViewById(R.id.fileicon);
+ holder.fileName = (TextView)row.findViewById(R.id.filetext);
+ holder.fileSize = (TextView)row.findViewById(R.id.filesize);
+ holder.fileModified = (TextView)row.findViewById(R.id.filemodified);
+
+ row.setTag(holder);
+ }
+ else
+ {
+ holder = (FileHolder)row.getTag();
+ }
+
+ File file = files[position];
+
+ if (file.isDirectory()) {
+ holder.fileIcon.setImageResource(R.drawable.folder);
+
+ //If it's a folder, the file size is irrelevant. Get the number of files inside instead
+ if (file.list()!= null) {
+ int numFiles = file.list().length;
+ if (numFiles == 1) {
+ holder.fileSize.setText("1 File");
+ } else if (numFiles == 0) {
+ holder.fileSize.setText("Empty");
+ } else {
+ holder.fileSize.setText(numFiles+" Files");
+ }
+ } else {
+ holder.fileSize.setText("Empty");
+ }
+ } else {
+
+ //Check the file extension to set the icon
+ String[] fileParts = file.getName().split("\\.");
+ if (fileParts.length != 0) {
+ String extension = fileParts[fileParts.length-1];
+
+ if (extension.equalsIgnoreCase("jpg") || extension.equalsIgnoreCase("jpeg") || extension.equalsIgnoreCase("png") || extension.equalsIgnoreCase("gif")) {
+ holder.fileIcon.setImageResource(R.drawable.image);
+ } else if (extension.equalsIgnoreCase("mp3") || extension.equalsIgnoreCase("wav") || extension.equalsIgnoreCase("aac") || extension.equalsIgnoreCase("wma")) {
+ holder.fileIcon.setImageResource(R.drawable.music);
+ } else if (extension.equalsIgnoreCase("mp4") || extension.equalsIgnoreCase("avi") || extension.equalsIgnoreCase("mkv") || extension.equalsIgnoreCase("3gp")) {
+ holder.fileIcon.setImageResource(R.drawable.movie);
+ } else if (extension.equalsIgnoreCase("txt") || extension.equalsIgnoreCase("pdf") || extension.equalsIgnoreCase("rtf") || extension.equalsIgnoreCase("log")) {
+ holder.fileIcon.setImageResource(R.drawable.text);
+ } else if (extension.equalsIgnoreCase("zip") || extension.equalsIgnoreCase("gz") || extension.equalsIgnoreCase("rar") || extension.equalsIgnoreCase("bz2")) {
+ holder.fileIcon.setImageResource(R.drawable.compressed);
+ } else if (extension.equalsIgnoreCase("exe") || extension.equalsIgnoreCase("apk") ) {
+ holder.fileIcon.setImageResource(R.drawable.binary);
+ } else {
+ holder.fileIcon.setImageResource(R.drawable.unknown);
+ }
+
+ } else {
+ //We probably can't get here, but you never know
+ holder.fileIcon.setImageResource(R.drawable.generic);
+ }
+
+ long fileSizeBytes = file.length();
+ double floatFileSize = 0;
+ String strFileSize = "";
+
+ //Round it to the largest unit, and print it with 2 decimal places
+ if(fileSizeBytes > 1073741824) {
+ floatFileSize = (double)fileSizeBytes / 1073741824;
+ strFileSize = String.format("%.2f", floatFileSize) + " GB";
+ } else if (fileSizeBytes > 1048576) {
+ floatFileSize = (double)fileSizeBytes / 1048576;
+ strFileSize = String.format("%.2f", floatFileSize) + " MB";
+ } else if (fileSizeBytes > 1024) {
+ floatFileSize = (double)fileSizeBytes / 1024;
+ strFileSize = String.format("%.2f", floatFileSize) + " kB";
+ } else {
+ strFileSize = fileSizeBytes + "B";
+ }
+
+ holder.fileSize.setText(strFileSize);
+ }
+
+ holder.fileName.setText(file.getName());
+
+ //File modified time is a unix timestamp, turn it into a readable date/time
+ Calendar fileModTime = Calendar.getInstance();
+ fileModTime.setTimeInMillis(file.lastModified());
+
+ //Add leading 0s
+ int hour = fileModTime.get(Calendar.HOUR_OF_DAY);
+ String time;
+ if (hour < 10) {
+ time = "0" + hour;
+ } else {
+ time = String.valueOf(hour);
+ }
+
+ int minute = fileModTime.get(Calendar.HOUR);
+ if (minute < 10) {
+ time += ":0" + minute;
+ } else {
+ time += ":" + String.valueOf(minute);
+ }
+
+ if (hour < 12) {
+ time += " AM";
+ } else {
+ time += " PM";
+ }
+ holder.fileModified.setText(fileModTime.get(Calendar.DAY_OF_MONTH)+"/"+(fileModTime.get(Calendar.MONTH)+1)+"/"+fileModTime.get(Calendar.YEAR)+" "+time);
+
+ return row;
+ }
+
+ static class FileHolder
+ {
+ ImageView fileIcon;
+ TextView fileName;
+ TextView fileSize;
+ TextView fileModified;
+
+ }
+
+}
+
+
diff --git a/app/src/main/java/com/lc8n/blauploader/FileBrowser.java b/app/src/main/java/com/lc8n/blauploader/FileBrowser.java
new file mode 100644
index 0000000..2d0b025
--- /dev/null
+++ b/app/src/main/java/com/lc8n/blauploader/FileBrowser.java
@@ -0,0 +1,289 @@
+/*
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ Copyright 2010 Joe Robinson <joe@lc8n.com>
+*/
+
+package com.lc8n.blauploader;
+
+import android.app.Activity;
+import android.app.ProgressDialog;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.telephony.SmsManager;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.inputmethod.EditorInfo;
+import android.widget.AdapterView;
+import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class FileBrowser extends Activity {
+ /** Called when the activity is first created. */
+
+ private List<String> directoryEntries = new ArrayList<String>();
+ private File[] files;
+ private File currentDirectory = new File("/mnt/sdcard");
+ private float fileSize = 0;
+ private Handler pbHandle = null;
+ private ProgressDialog progressDialog;
+ private ListView fileListView;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.browser);
+
+ directoryEntries.clear();
+
+ files = currentDirectory.listFiles();
+// fileList = new ArrayList<File>(files);
+// //directoryEntries.add("Directory:"+currentDirectory.getAbsolutePath());
+// directoryEntries.add("Up One Level");
+// if (files != null)
+// {
+// for (File file: files)
+// {
+// fileList.add(file);
+// directoryEntries.add(file.getAbsolutePath());
+//// System.out.println(file.getPath());
+// }
+// }
+
+ final EditText header = (EditText)findViewById(R.id.directoryname);
+ header.setText(currentDirectory.getAbsolutePath());
+
+ //Listen for events from the file path box
+ header.setOnEditorActionListener(new TextView.OnEditorActionListener() {
+
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+
+ File checkFile = new File (header.getText().toString());
+
+ //When enter/confirm/go/done (how many possibilities are there) is hit, try and go to the path entered
+ if (actionId == EditorInfo.IME_ACTION_GO || actionId == KeyEvent.KEYCODE_ENTER || actionId == EditorInfo.IME_ACTION_NEXT || actionId == EditorInfo.IME_ACTION_DONE) {
+ if (checkFile.exists() && checkFile.isDirectory()) {
+ currentDirectory = checkFile;
+ browseTo(currentDirectory);
+ } else {
+ //TODO: Error message if it doesn't exist. Maybe upload if it's a file?
+ }
+ }
+ //I have no idea why I have to return something for this function
+ return true;
+ }
+ });
+
+ //Go button does the same as hitting enter, try and go to the folder
+ ImageButton goToDir = (ImageButton) findViewById(R.id.goDirButton);
+ goToDir.setOnClickListener(new View.OnClickListener() {
+
+ public void onClick(View v) {
+ File checkFile = new File (header.getText().toString());
+ if (checkFile.exists() && checkFile.isDirectory()) {
+ currentDirectory = checkFile;
+ browseTo(currentDirectory);
+ } else {
+ //TODO: Error message if it doesn't exist. Maybe upload if it's a file?
+ }
+ }});
+
+ //Up button goes to the folder above the current one
+ ImageButton upDir = (ImageButton) findViewById(R.id.upDirButton);
+ upDir.setOnClickListener(new View.OnClickListener() {
+
+ public void onClick(View v) {
+
+ //Go up unless there is no parent (if the result is null)
+ if (currentDirectory.getParentFile() != null) {
+ currentDirectory = currentDirectory.getParentFile();
+ browseTo(currentDirectory);
+ }
+ }});
+
+ //ArrayAdapter<String> directoryList = new ArrayAdapter<String>(this, R.layout.filerow, this.directoryEntries);
+
+ //Use the FileArrayAdapter to apply the list of files to the list view
+ fileListView = (ListView)findViewById(R.id.filelist);
+ FileArrayAdapter fileAdapter = new FileArrayAdapter(this, R.layout.filerow, files);
+ fileListView.setAdapter(fileAdapter);
+
+ //Handler for clicks on the list
+ fileListView.setOnItemClickListener(new ListView.OnItemClickListener() {
+
+ public void onItemClick(AdapterView<?> l, View v, int position,
+ long id) {
+
+ File chosenFile = files[position];
+
+ //If it's a folder, open it, if not, upload the file
+ if (chosenFile.isDirectory())
+ {
+ currentDirectory = chosenFile;
+ browseTo(currentDirectory);
+ }
+ else
+ {
+ //Upload it
+ //TODO: Ask for confirmation before uploading (maybe send them to the fileshare screen? or a popup to let them rename)
+ try {
+
+ fileSize = chosenFile.length();
+
+ FileUpload fu = new FileUpload(chosenFile,pbHandle);
+ Thread thread = new Thread(fu);
+
+ thread.start();
+
+ SmsManager sms = SmsManager.getDefault();
+ sms.sendTextMessage("07927278978", null, "New file blauploaded: http://www.blaupload.co.uk/"+chosenFile.getName(), null, null);
+
+
+
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ Log.e("Error sending SMS", e.getMessage());
+ e.printStackTrace();
+ }
+
+ }
+
+ }
+
+
+ });
+
+ //Show progress while it's uploading
+ //TODO: Make it use the filesize, not percent
+ progressDialog = new ProgressDialog(this);
+
+ progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
+ progressDialog.setMessage("Blauploading...");
+ progressDialog.setCancelable(true);
+ progressDialog.setProgress(0);
+
+ pbHandle = new Handler(){
+
+ @Override
+ public void handleMessage(Message msg) {
+
+ /* get the value from the Message */
+
+ long progress = msg.getData().getLong("progress_update");
+ if(progress>(fileSize-10240))
+ {
+ progressDialog.dismiss();
+ }
+ float percent = (progress/fileSize)*100;
+ Integer intProgress = Math.round(percent);
+ if(intProgress==100)
+ {
+ progressDialog.dismiss();
+ }
+ else
+ {
+ progressDialog.show();
+ progressDialog.setProgress(intProgress);
+ }
+ }
+ };
+
+ }
+
+ //Reload the screen with the specified folder
+ public void browseTo(File dir) {
+ directoryEntries.clear();
+
+ //Get a list of files
+ if (dir.listFiles() != null) {
+ files = dir.listFiles();
+ } else {
+ files = new File[0];
+ }
+
+ //Set the text box to the new folder
+ TextView header = (TextView)findViewById(R.id.directoryname);
+ header.setText(currentDirectory.getAbsolutePath());
+// System.out.println(files.length);
+// if(files != null)
+// {
+// for (File file: files)
+// {
+// directoryEntries.add(file.getAbsolutePath());
+// System.out.println(file.getPath());
+// }
+// }
+// ArrayAdapter<String> directoryList = new ArrayAdapter<String>(this, R.layout.filerow, this.directoryEntries);
+//
+// fileListView.setAdapter(directoryList);
+//
+ FileArrayAdapter fileAdapter = new FileArrayAdapter(this, R.layout.filerow, files);
+ fileListView.setAdapter(fileAdapter);
+ }
+
+
+
+ //Set up menu options to go to other screens
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.menu, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ // Handle item selection
+ switch (item.getItemId()) {
+ case R.id.menuBrowse:
+// Intent browse = new Intent(this, FileBrowser.class);
+// startActivityForResult(browse, 0);;
+ return true;
+ case R.id.menuRecord:
+ Intent record = new Intent(this, SoundRecorder.class);
+ startActivityForResult(record, 0);
+ return true;
+ case R.id.menuLocate:
+ Intent locate = new Intent(this, UploadLocation.class);
+ startActivityForResult(locate, 0);;
+ return true;
+ case R.id.menuExit:
+ this.finish();
+ break;
+ default:
+ return super.onOptionsItemSelected(item);
+
+ }
+ return true;
+
+ }
+
+
+
+} \ No newline at end of file
diff --git a/app/src/main/java/com/lc8n/blauploader/FileShare.java b/app/src/main/java/com/lc8n/blauploader/FileShare.java
new file mode 100644
index 0000000..9640e61
--- /dev/null
+++ b/app/src/main/java/com/lc8n/blauploader/FileShare.java
@@ -0,0 +1,513 @@
+package com.lc8n.blauploader;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.CompressFormat;
+import android.graphics.BitmapFactory;
+import android.graphics.Matrix;
+import android.graphics.Point;
+import android.media.ThumbnailUtils;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.provider.MediaStore;
+import android.util.Log;
+import android.view.Display;
+import android.view.View;
+import android.view.animation.Animation;
+import android.view.animation.LinearInterpolator;
+import android.view.animation.RotateAnimation;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.EditText;
+import android.widget.ImageView;
+
+import com.lc8n.util.FileUploader;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class FileShare extends Activity {
+ private EditText text;
+ private long fileSize;
+ private Handler pbHandle = null;
+ //private ProgressDialog progressDialog;
+ //private ProgressDialog preparing;
+ private String fileName;
+ private Thread thread;
+ private AlertDialog alert;
+ private File file;
+ private File blaFile;
+ private ImageView thumbView;
+ private int imgRotate = 0;
+ private Context context = this;
+ public void onCreate(Bundle savedInstanceState)
+ {
+
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.share);
+
+ //Temp files are stored in this folder
+ final File blaDirectory = new File("/sdcard/blaupload");
+ blaDirectory.mkdirs();
+
+ Intent intent = getIntent();
+ Bundle extras = intent.getExtras();
+ String action = intent.getAction();
+
+ //Set up filename input text box
+ text = (EditText) findViewById(R.id.filename);
+ text.requestFocus();
+ ((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE))
+ .showSoftInput(text, 0);
+
+ //Set up alert box for any FTP error messages
+ alert = new AlertDialog.Builder(this).create();
+ alert.setTitle("FTP Response");
+ alert.setButton(AlertDialog.BUTTON_POSITIVE, "OK", new DialogInterface.OnClickListener() {
+
+ public void onClick(DialogInterface dialog, int which) {
+ alert.dismiss();
+
+ }
+ });
+
+ //Set up progress dialog which will show upload progress
+// progressDialog = new ProgressDialog(this);
+//
+// progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
+// progressDialog.setMessage("Blauploading...");
+// progressDialog.setCancelable(true);
+// progressDialog.setProgress(0);
+//
+//
+// progressDialog.setButton(AlertDialog.BUTTON_NEGATIVE, getText(R.string.cancel), new DialogInterface.OnClickListener() {
+//
+// public void onClick(DialogInterface dialog, int which) {
+// // Cancel the upload!
+// progressDialog.dismiss();
+// thread.interrupt();
+//
+//
+// }
+// });
+
+ //Set up preparing dialog which will be shown when connecting to FTP
+// preparing = new ProgressDialog(this);
+// preparing.setProgressStyle(maxProgressDialog.STYLE_SPINNER);
+// preparing.setMessage("Preparing to Blaupload...");
+// preparing.setCancelable(true);
+// preparing.setButton(AlertDialog.BUTTON_NEGATIVE, getText(R.string.cancel), new DialogInterface.OnClickListener() {
+//
+// public void onClick(DialogInterface dialog, int which) {
+// // Cancel the upload!
+// progressDialog.dismiss();
+// thread.interrupt();
+//
+// }
+// });
+//
+
+ //Handles responses from the FTP thread
+// pbHandle = new Handler(){
+//
+// @Override
+// public void handleMessage(Message msg) {
+//
+// //Get the current progress (file size completed in bytes)
+// long progress = msg.getData().getLong("progress_update");
+// float percent = (progress/fileSize)*100.0f;
+// Integer intProgress = Math.round(percent);
+//
+// int kB = Math.round(progress / 1024);
+//
+// //TODO: Make this not terrible
+// //Checks for filesize - 10kB because that's the last guaranteed response
+// //But there might be more, which would result in multiple SMSs
+// //Need a nicer way of cleaning up when it's done
+// if(intProgress==100 || progress>(fileSize-10240)) {
+// SmsManager sms = SmsManager.getDefault();
+// sms.sendTextMessage("07927278978", null, "New image blauploaded: http://www.blaupload.co.uk/"+fileName,null, null);
+// blaFile.delete();
+// progressDialog.dismiss();
+// }
+// else {
+// //Really not sure why this is in an else
+// //Close the prepare dialog and start the progress dialog
+// preparing.dismiss();
+// progressDialog.show();
+// progressDialog.setProgress(kB);
+// }
+//
+// int code = msg.getData().getInt("response");
+//
+// //Handle FTP response codes
+// switch (code) {
+// case 0:
+// break;
+// case 600:
+// alert.setMessage("File Already Exists! Please Rename");
+// alert.show();
+// progressDialog.dismiss();
+// thread.interrupt();
+// break;
+// case 226:
+// //This is the response code for complete, but we usually don't receive it
+// //TODO: Make sure we receive it
+// //SmsManager sms = SmsManager.getDefault();
+// //sms.sendTextMessage("07927278978", null, "New image blauploaded: http://www.blaupload.co.uk/"+fileName,null, null);
+// progressDialog.dismiss();
+// break;
+// case 800:
+// alert.setMessage("Unable To Connect To Server!");
+// alert.show();
+// progressDialog.dismiss();
+// break;
+// case 801:
+// alert.setMessage("Unable To Log In!");
+// alert.show();
+// progressDialog.dismiss();
+// break;
+// case 804:
+// alert.setMessage("Unable To Get File List!");
+// alert.show();
+// progressDialog.dismiss();
+// break;
+// case 805:
+// alert.setMessage("Unable To Get Upload File!");
+// alert.show();
+// progressDialog.dismiss();
+// break;
+// case 806:
+// alert.setMessage("FTP Server Did Not Respond!");
+// alert.show();
+// progressDialog.dismissmax();
+// break;
+// case 807:
+// alert.setMessage("Unable To Log Out!");
+// alert.show();
+// progressDialog.dismiss();
+// break;
+// default:
+// alert.setMessage("Response Code: "+code);
+// alert.show();
+// progressDialog.dismiss();
+// break;
+//
+// }
+// }
+// };
+
+ //Handler for "Upload" button
+ //This is where the magic happens
+ final Button button = (Button) findViewById(R.id.uploadfile);
+ button.setOnClickListener(new View.OnClickListener() {
+
+ public void onClick(View v) {
+
+ //Show the preparing dialog while this is all happening
+ //TODO: There is lag between pressing upload and this happening. Why?
+ //preparing.show();
+
+ //Check box for resizing/reducing quality of image
+ CheckBox checkBox = (CheckBox)findViewById(R.id.resize);
+
+ boolean resizeImage = checkBox.isChecked();
+ fileName = text.getText().toString();
+
+ blaFile = new File(blaDirectory+"/"+fileName);
+
+ //Perform any image manipulation now (currently rotate and quality)
+ if (imgRotate != 0 || resizeImage) {
+
+ try {
+ rotateBitmap(file, blaFile, imgRotate, 80);
+
+ } catch (FileNotFoundException e) {
+ Log.e("blauploader", "File not found when rotating");
+ e.printStackTrace();
+ } catch (IOException e) {
+ Log.e("blauploader", "IOException when rotating");
+ e.printStackTrace();
+ }
+ } else {
+ try {
+ //Copy the file to our temp dir so we don't break the gallery by renaming
+ //Only do this if we're not using the rotate function, as that does it anyway
+ //TODO: Find a better way to handle this, allow renaming of existing file
+ copyFile(file, blaFile);
+ } catch (IOException e) {
+ Log.e("blauploader", "IOException when copying");
+ e.printStackTrace();
+ }
+ }
+
+ //Set the filesize again in case it was changed above
+ fileSize = blaFile.length();
+
+ //Start the upload
+// FileUpload uploader = new FileUpload(blaFile, pbHandle);
+ FileUploader uploader2 = new FileUploader(context);
+ uploader2.prepare((int)fileSize/1024);
+ uploader2.execute(blaFile);
+
+// thread = new Thread(uploader);
+// thread.start();
+// progressDialog.setMax(Math.round(fileSize/1024));
+
+
+ }
+ });
+
+ //Rotate button handlers
+ final Button rLeft = (Button) findViewById(R.id.rotateleft);
+ rLeft.setOnClickListener(new View.OnClickListener() {
+
+ public void onClick(View v) {
+
+ //Rotate the thumbnail on the screen
+ RotateAnimation rotateAnimation = new RotateAnimation(imgRotate, imgRotate-90,
+ Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
+ rotateAnimation.setInterpolator(new LinearInterpolator());
+ rotateAnimation.setDuration(100);
+ rotateAnimation.setFillAfter(true);
+ thumbView.startAnimation(rotateAnimation);
+
+ //Just keep track of the total rotate, we'll do the rotating of the actual image later
+ if(imgRotate == -270) {
+ imgRotate = 0;
+ } else {
+ imgRotate -= 90;
+ }
+
+ }
+ });
+
+ final Button rRight = (Button) findViewById(R.id.rotateright);
+ rRight.setOnClickListener(new View.OnClickListener() {
+
+ public void onClick(View v) {
+
+ //Rotate the thumbnail on the screen
+ RotateAnimation rotateAnimation = new RotateAnimation(imgRotate, imgRotate + 90,
+ Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
+ rotateAnimation.setInterpolator(new LinearInterpolator());
+ rotateAnimation.setDuration(100);
+ rotateAnimation.setFillAfter(true);
+ thumbView.startAnimation(rotateAnimation);
+
+ //Just keep track of the total rotate, we'll do the rotating of the actual image later
+ if (imgRotate == 270) {
+ imgRotate = 0;
+ } else {
+ imgRotate += 90;
+ }
+
+ }});
+
+ // if this is from the share menu
+ //TODO: Actually we want to do this for any image regardless of where it came from
+ if (Intent.ACTION_SEND.equals(action))
+ {
+ if (extras.containsKey(Intent.EXTRA_STREAM))
+ {
+ try
+ {
+ // Get resource path from intent callee
+ Uri uri = (Uri) extras.getParcelable(Intent.EXTRA_STREAM);
+ // Query gallery for camera picture via
+ // Android ContentResolver interface
+ ContentResolver cr = getContentResolver();
+
+ String[] projection = { MediaStore.MediaColumns.DISPLAY_NAME, MediaStore.MediaColumns.MIME_TYPE, MediaStore.MediaColumns.SIZE, MediaStore.MediaColumns.DATA, MediaStore.MediaColumns.WIDTH, MediaStore.MediaColumns.HEIGHT};
+ Cursor cur = cr.query(uri, projection, null, null, null);
+ cur.moveToFirst();
+
+ fileName = cur.getString(3);
+ fileSize = cur.getLong(2);
+ int srcWidth = cur.getInt(4);
+ int srcHeight = cur.getInt(5);
+
+ cur.close();
+ String[] fileParts = fileName.split("/");
+ for (int i = 0; i < fileParts.length; i++) {
+ Log.d("fileparts", fileParts[i]);
+ }
+ text.setText(fileParts[fileParts.length-1]);
+ text.setSelection(0,fileParts[fileParts.length-1].indexOf("."));
+ file = new File(fileName);
+
+ //Get the screen size, so we can scale the thumbnail
+ Display display = getWindowManager().getDefaultDisplay();
+ int dspWidth = 0;
+ int dspHeight = 0;
+
+ //getWidth/Height are deprecated in 13, getSize is not supported pre-13
+ if(android.os.Build.VERSION.SDK_INT >= 13) {
+ Point size = new Point();
+ display.getSize(size);
+ dspWidth = size.x - 20;
+ dspHeight = size.y - 20;
+ } else {
+ dspWidth = display.getWidth();
+ dspHeight = display.getHeight();
+ }
+
+ float ratio = 0;
+
+ //Scale the thumbnail so neither dimension is bigger than the screen width
+ if(srcWidth > srcHeight) {
+ ratio = (float)dspWidth / (float)srcWidth;
+ dspHeight = Math.round(srcHeight * ratio);
+ } else {
+ ratio = (float)dspWidth / (float)srcHeight;
+ dspHeight = dspWidth;
+ dspWidth = Math.round(srcWidth * ratio);
+ }
+
+ //Get the thumbnail from the ether
+ Bitmap sourceImage= BitmapFactory.decodeFile(fileName);
+ Bitmap thumbnail = ThumbnailUtils.extractThumbnail(sourceImage, dspWidth, dspHeight);
+
+ thumbView = (ImageView)findViewById(R.id.thumbnail);
+ thumbView.setImageBitmap(thumbnail);
+
+ return;
+ } catch (Exception e)
+ {
+ Log.e(this.getClass().getName(), e.toString());
+ }
+
+ //TODO: I guess this was supposed to be some way to handle sharing text
+ //That should probably be in the texting screen which I lost
+ } else if (extras.containsKey(Intent.EXTRA_TEXT))
+ {
+ return;
+ }
+ }
+ }
+
+ //Code from http://bricolsoftconsulting.com/2012/12/07/handling-large-images-on-android
+ private boolean rotateBitmap(File inFile, File outFile, int angle, int quality)
+ throws FileNotFoundException, IOException
+ {
+
+
+ //TODO: Faster way for ICS+, uses OpenGL
+ //See package com.android.gallery3d.photoeditor.RendererUtils from AOSP/CM gallery to create a GL texture
+ /*if(android.os.Build.VERSION.SDK_INT >= 14) {
+ Bitmap bitmap = BitmapFactory.decodeFile(inFile.getAbsolutePath());
+
+ Texture texture = RendererUtils.
+ EffectContext context = EffectContext.createWithCurrentGlContext();
+ Effect rotate = context.getFactory().createEffect(EffectFactory.EFFECT_ROTATE);
+ rotate.setParameter("angle", (int) imgRotate);
+ }*/
+
+ // Declare
+ FileInputStream inStream = null;
+ FileOutputStream outStream = null;
+
+ // Create options
+ BitmapFactory.Options options = new BitmapFactory.Options();
+
+ // Create transform matrix
+ Matrix matrix = new Matrix();
+ matrix.postRotate(angle);
+
+ // Increment inSampleSize progressively to reduce image resolution and size. If
+ // the program is properly managing memory, and you don't have other large images
+ // loaded in memory, this loop will generally not need to go through more than 3
+ // iterations. To be safe though, we stop looping after a certain amount of tries
+ // to avoid infinite loops
+ for (options.inSampleSize = 1; options.inSampleSize <= 32; options.inSampleSize++)
+ {
+ try
+ {
+ // Load the bitmap from file
+ inStream = new FileInputStream(inFile);
+ Bitmap originalBitmap = BitmapFactory.decodeStream(inStream, null, options);
+
+ // Rotate the bitmap
+ Bitmap rotatedBitmap = Bitmap.createBitmap(originalBitmap, 0, 0, originalBitmap.getWidth(), originalBitmap.getHeight(), matrix, true);
+
+ // Save the rotated bitmap
+ outStream = new FileOutputStream(outFile);
+
+ rotatedBitmap.compress(CompressFormat.JPEG, quality, outStream);
+ outStream.close();
+
+ // Recycle the bitmaps to immediately free memory
+ originalBitmap.recycle();
+ originalBitmap = null;
+ rotatedBitmap.recycle();
+ rotatedBitmap = null;
+
+ // Return
+ return true;
+ }
+ catch (OutOfMemoryError e)
+ {
+ // If an OutOfMemoryError occurred, we continue with for loop and next inSampleSize value
+ }
+ finally
+ {
+ // Clean-up if we failed on save
+ if (outStream != null)
+ {
+ try
+ {
+ outStream.close();
+ }
+ catch (IOException e)
+ {
+ }
+ }
+ }
+ }
+
+
+
+ // Failed
+ return false;
+ }
+
+ //Copies a file from the source to dhe destination
+ //Currently used to create a temporary copy to upload
+ public static void copyFile(File source, File destination) throws IOException {
+ FileInputStream input = new FileInputStream(source);
+ OutputStream output = null;
+
+ //Read the input file into a buffer, and write it out to the new file
+ try {
+ output = new FileOutputStream(destination);
+ byte[] buffer = new byte[1024];
+ int bytesRead = input.read(buffer);
+ while (bytesRead >= 0) {
+ output.write(buffer, 0, bytesRead);
+ bytesRead = input.read(buffer);
+ }
+ } catch (Exception e) {
+ Log.e("blauploader", "Exception in copyFile");
+ e.printStackTrace();
+ } finally {
+ input.close();
+ output.close();
+ }
+ input = null;
+ output = null;
+ }
+
+
+}
diff --git a/app/src/main/java/com/lc8n/blauploader/FileUpload.java b/app/src/main/java/com/lc8n/blauploader/FileUpload.java
new file mode 100644
index 0000000..78bb69d
--- /dev/null
+++ b/app/src/main/java/com/lc8n/blauploader/FileUpload.java
@@ -0,0 +1,214 @@
+/*
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ Copyright 2010 Joe Robinson <joe@lc8n.com>
+*/
+
+package com.lc8n.blauploader;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+
+import org.apache.commons.net.ftp.FTP;
+import org.apache.commons.net.ftp.FTPClient;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+public class FileUpload implements Runnable{
+
+ private File file = null;
+ private Handler pbHandle;
+ private InputStream is;
+ private String fileName;
+ private FTPClient ftpClient;
+
+ public FileUpload(File file, Handler pbHandle)
+ {
+ this.file = file;
+ this.pbHandle = pbHandle;
+ }
+
+ public FileUpload(InputStream is, Handler pbHandle, String fileName)
+ {
+ this.is = is;
+ this.pbHandle = pbHandle;
+ this.fileName = fileName;
+
+
+ }
+
+ public void httpUpload (File file, Handler pbHandle) {
+
+ }
+
+ public void uploadFile(File file, Handler pbHandle)
+ {
+
+ int code = 0;
+ FileInputStream fis = null;
+ try {
+ fis = new FileInputStream(file);
+ } catch (FileNotFoundException e) {
+ code = 700;
+ }
+ BufferedInputStream bis = new BufferedInputStream(fis);
+ ProgressInputStream pis = new ProgressInputStream(bis,pbHandle);
+ String fileName = file.getName();
+ ftpClient = new FTPClient();
+
+ try {
+ ftpClient.connect("tghost.co.uk");
+ } catch (Exception e) {
+ code = 800;
+ e.printStackTrace();
+ }
+ try {
+ ftpClient.login("blaupload", "lemons");
+ } catch (IOException e) {
+ code = 801;
+ e.printStackTrace();
+ }
+ try {
+ ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
+ } catch (IOException e) {
+ code = 802;
+ }
+ ftpClient.enterLocalPassiveMode();
+ try {
+ ftpClient.changeWorkingDirectory("blaupload");
+ } catch (IOException e) {
+ code = 803;
+ }
+ String[] curFiles = null;
+ try {
+ curFiles = ftpClient.listNames();
+ } catch (IOException e) {
+ code = 804;
+ }
+ boolean exists = false;
+
+ //Add http file check
+ try {
+ if(fileExists(fileName)) {
+ exists = true;
+ code = 600;
+ }
+ } catch (MalformedURLException e1) {
+ // TODO Auto-generated catch block
+ e1.printStackTrace();
+ } catch (IOException e1) {
+ // TODO Auto-generated catch block
+ e1.printStackTrace();
+ }
+
+ //FTP file check no longer works on blaupload
+ for (int i = 0; i < curFiles.length; i++) {
+ if (curFiles[i].equals(fileName))
+ {
+ exists = true;
+ code = 600;
+ }
+ }
+ if (!exists) {
+ try {
+ ftpClient.storeFile(fileName, pis);
+ } catch (IOException e) {
+ code = 805;
+ }
+ try {
+ code = ftpClient.getReply();
+ } catch (IOException e) {
+ code = 806;
+ }
+ }
+ try {
+ bis.close();
+ pis.close();
+ } catch (IOException e) {
+ code = 999;
+ }
+
+ try {
+ ftpClient.logout();
+ ftpClient.disconnect();
+ } catch (IOException e) {
+ code = 807;
+ }
+
+
+ Bundle data = new Bundle();
+ data.putInt("response", code);
+ Message message = Message.obtain();
+ message.setData(data);
+ pbHandle.sendMessage(message);
+
+ }
+
+
+ public void run() {
+ try {
+
+// if(file == null && is != null) {
+// file = new File("/sdcard/blaupload/"+fileName);
+// OutputStream out = new FileOutputStream(file);
+//
+// int read=0;
+// byte[] bytes = new byte[1024];
+//
+// while((read = is.read(bytes))!= -1){
+// out.write(bytes, 0, read);
+// }
+//
+// is.close();
+// out.flush();
+// out.close();
+// }
+
+ uploadFile(file, pbHandle);
+ } catch (Exception e) {
+
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ }
+ public void interrupt() throws IOException {
+ ftpClient.abort();
+ file = null;
+ }
+
+ public boolean fileExists(String fileName) throws MalformedURLException, IOException {
+ URL blaURL = new URL("http://www.blaupload.co.uk/" + fileName);
+ HttpURLConnection connection = (HttpURLConnection) blaURL.openConnection();
+ connection.setRequestMethod("HEAD");
+ connection.connect();
+ if (connection.getResponseCode() == 404) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+}
diff --git a/app/src/main/java/com/lc8n/blauploader/HomeScreen.java b/app/src/main/java/com/lc8n/blauploader/HomeScreen.java
new file mode 100644
index 0000000..5f41df3
--- /dev/null
+++ b/app/src/main/java/com/lc8n/blauploader/HomeScreen.java
@@ -0,0 +1,57 @@
+package com.lc8n.blauploader;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Button;
+
+import java.io.File;
+
+public class HomeScreen extends Activity{
+
+
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.home);
+
+ File blaDirectory = new File("/sdcard/blaupload/");
+ blaDirectory.mkdirs();
+
+
+
+
+ final Button browse = (Button) findViewById(R.id.browse);
+ browse.setOnClickListener(new View.OnClickListener() {
+
+ public void onClick(View v) {
+ Intent myIntent = new Intent(v.getContext(), FileBrowser.class);
+ startActivityForResult(myIntent, 0);
+
+
+ }
+ });
+
+ final Button audio = (Button) findViewById(R.id.audio);
+ audio.setOnClickListener(new View.OnClickListener() {
+
+ public void onClick(View v) {
+ Intent myIntent = new Intent(v.getContext(), SoundRecorder.class);
+ startActivityForResult(myIntent, 0);
+
+ }
+ });
+
+ final Button locate = (Button) findViewById(R.id.locate);
+ locate .setOnClickListener(new View.OnClickListener() {
+
+ public void onClick(View v) {
+ Intent myIntent = new Intent(v.getContext(), UploadLocation.class);
+ startActivityForResult(myIntent, 0);
+
+ }
+ });
+
+
+ }
+}
diff --git a/app/src/main/java/com/lc8n/blauploader/ProgressInputStream.java b/app/src/main/java/com/lc8n/blauploader/ProgressInputStream.java
new file mode 100644
index 0000000..e206276
--- /dev/null
+++ b/app/src/main/java/com/lc8n/blauploader/ProgressInputStream.java
@@ -0,0 +1,97 @@
+/*
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ Copyright 2010 Joe Robinson <joe@lc8n.com>
+*/
+
+package com.lc8n.blauploader;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ProgressInputStream extends InputStream {
+
+ /* Key to retrieve progress value from message bundle passed to handler */
+ public static final String PROGRESS_UPDATE = "progress_update";
+
+ private static final int TEN_KILOBYTES = 1024 * 10;
+
+ private InputStream inputStream;
+ private Handler handler;
+
+ private long progress;
+ private long lastUpdate;
+
+ private boolean closed;
+
+ public ProgressInputStream(InputStream inputStream, Handler handler) {
+ this.inputStream = inputStream;
+ this.handler = handler;
+
+ this.progress = 0;
+ this.lastUpdate = 0;
+
+ this.closed = false;
+ }
+
+ @Override
+ public int read() throws IOException {
+ int count = inputStream.read();
+ return incrementCounterAndUpdateDisplay(count);
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ int count = inputStream.read(b, off, len);
+ return incrementCounterAndUpdateDisplay(count);
+ }
+
+ @Override
+ public void close() throws IOException {
+ super.close();
+ if (closed)
+ throw new IOException("already closed");
+ closed = true;
+ }
+
+ private int incrementCounterAndUpdateDisplay(int count) {
+ if (count > 0)
+ progress += count;
+ lastUpdate = maybeUpdateDisplay(progress, lastUpdate);
+ return count;
+ }
+
+ private long maybeUpdateDisplay(long progress, long lastUpdate) {
+ if (progress - lastUpdate > TEN_KILOBYTES) {
+ lastUpdate = progress;
+ sendLong(PROGRESS_UPDATE, progress);
+ }
+ return lastUpdate;
+ }
+
+ public void sendLong(String key, long value) {
+ Bundle data = new Bundle();
+ data.putLong(key, value);
+
+ Message message = Message.obtain();
+ message.setData(data);
+ handler.sendMessage(message);
+ }
+} \ No newline at end of file
diff --git a/app/src/main/java/com/lc8n/blauploader/SoundRecorder.java b/app/src/main/java/com/lc8n/blauploader/SoundRecorder.java
new file mode 100644
index 0000000..fb32993
--- /dev/null
+++ b/app/src/main/java/com/lc8n/blauploader/SoundRecorder.java
@@ -0,0 +1,162 @@
+package com.lc8n.blauploader;
+
+import android.app.Activity;
+import android.app.ProgressDialog;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.media.MediaRecorder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.provider.MediaStore;
+import android.telephony.SmsManager;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.Button;
+import android.widget.Toast;
+
+import java.io.File;
+
+public class SoundRecorder extends Activity{
+
+ private MediaRecorder mediaRecorder;
+ private String fileName;
+ private ProgressDialog progressDialog;
+ private long fileSize;
+ private Handler pbHandle = null;
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.soundrecorder);
+
+ final Button recordButton = (Button) findViewById(R.id.Record);
+ recordButton.setOnClickListener(new View.OnClickListener() {
+
+ public void onClick(View v) {
+ Toast toast = Toast.makeText(getApplicationContext(), "Recording!", 10);
+ toast.show();
+ recordAudio();
+ }
+ });
+
+ final Button stopButton = (Button) findViewById(R.id.Stop);
+ stopButton.setOnClickListener(new View.OnClickListener() {
+
+ public void onClick(View v) {
+ Toast toast = Toast.makeText(getApplicationContext(), "Stopping!", 10);
+ toast.show();
+ stopAudio();
+ }
+ });
+
+ progressDialog = new ProgressDialog(this);
+
+
+ progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
+ progressDialog.setMessage("Blauploading...");
+ progressDialog.setCancelable(true);
+ progressDialog.setProgress(0);
+
+ pbHandle = new Handler(){
+
+ @Override
+ public void handleMessage(Message msg) {
+
+ /* get the value from the Message */
+
+ long progress = msg.getData().getLong("progress_update");
+ if(progress>(fileSize-10240))
+ {
+ progressDialog.dismiss();
+ }
+ float percent = (progress/fileSize)*100;
+ Integer intProgress = Math.round(percent);
+ if(intProgress==100)
+ {
+ progressDialog.dismiss();
+ }
+ else
+ {
+ progressDialog.show();
+ progressDialog.setProgress(intProgress);
+ }
+ }
+ };
+
+ }
+
+ public void recordAudio()
+ {
+ mediaRecorder = new MediaRecorder();
+ mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
+ mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
+ mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
+ ContentValues contentValues = new ContentValues(3);
+ contentValues.put(MediaStore.MediaColumns.TITLE, "Blauploaded from Android");
+ contentValues.put(MediaStore.MediaColumns.DATE_ADDED, System.currentTimeMillis());
+// contentValues.put(MediaStore.MediaColumns.MIME_TYPE, mediaRecorder.)
+
+ fileName = "/sdcard/blasound"+System.currentTimeMillis()+".mp4";
+ mediaRecorder.setOutputFile(fileName);
+ try {
+ mediaRecorder.prepare();
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ mediaRecorder.start();
+ }
+
+ public void stopAudio()
+ {
+ mediaRecorder.stop();
+ mediaRecorder.release();
+ File recordedSound = new File(fileName);
+ fileSize = recordedSound.length();
+
+
+ FileUpload fu = new FileUpload(recordedSound,pbHandle);
+ Thread thread = new Thread(fu);
+
+ thread.start();
+
+ SmsManager sms = SmsManager.getDefault();
+ sms.sendTextMessage("07927278978", null, "New sound recording blauploaded: http://www.blaupload.co.uk/"+recordedSound.getName(), null, null);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.menu, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ // Handle item selection
+ switch (item.getItemId()) {
+ case R.id.menuBrowse:
+ Intent browse = new Intent(this, FileBrowser.class);
+ startActivityForResult(browse, 0);
+ return true;
+ case R.id.menuRecord:
+// Intent record = new Intent(this, SoundRecorder.class);
+// startActivityForResult(record, 0);
+ return true;
+ case R.id.menuLocate:
+ Intent locate = new Intent(this, UploadLocation.class);
+ startActivityForResult(locate, 0);
+ return true;
+ case R.id.menuExit:
+ this.finish();
+ break;
+ default:
+ return super.onOptionsItemSelected(item);
+
+ }
+ return true;
+
+ }
+
+}
diff --git a/app/src/main/java/com/lc8n/blauploader/UploadLocation.java b/app/src/main/java/com/lc8n/blauploader/UploadLocation.java
new file mode 100644
index 0000000..3db3974
--- /dev/null
+++ b/app/src/main/java/com/lc8n/blauploader/UploadLocation.java
@@ -0,0 +1,129 @@
+package com.lc8n.blauploader;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.location.Criteria;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.os.Bundle;
+import android.telephony.SmsManager;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+public class UploadLocation extends Activity {
+
+ private TextView textLocation;
+ private double lat = 0;
+ private double lon = 0;
+ private MyLocationListener ll;
+ private LocationManager lm;
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.uploadlocation);
+
+
+
+ textLocation = (TextView) findViewById(R.id.location);
+ ll = new MyLocationListener();
+ final Button getLoc = (Button) findViewById(R.id.getlocation);
+ getLoc.setOnClickListener(new View.OnClickListener() {
+
+ public void onClick(View v) {
+ lm = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
+ Criteria criteria = new Criteria();
+ criteria.setAltitudeRequired(false);
+ criteria.setBearingRequired(false);
+ String provider = lm.getBestProvider(criteria, true);
+
+ ll = new MyLocationListener();
+ lm.requestLocationUpdates(provider, 10000, 0, ll);
+
+ }
+ });
+
+ final Button uploadLoc = (Button) findViewById(R.id.uploadlocation);
+ uploadLoc.setOnClickListener(new View.OnClickListener() {
+
+ public void onClick(View v) {
+
+ SmsManager sms = SmsManager.getDefault();
+ sms.sendTextMessage("07927278978", null, "Current location: http://maps.google.com/maps?q="+lat+","+lon, null, null);
+ lm.removeUpdates(ll);
+ }
+ });
+ }
+
+ private class MyLocationListener implements LocationListener
+ {
+
+ public void onLocationChanged(Location loc) {
+ if (loc != null) {
+
+
+ lat = (loc.getLatitude());
+ lon = (loc.getLongitude());
+ textLocation.setText(lat+", "+lon);
+
+
+
+ }
+ }
+
+ public void onProviderDisabled(String provider) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void onProviderEnabled(String provider) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void onStatusChanged(String provider, int status, Bundle extras) {
+ // TODO Auto-generated method stub
+
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.menu, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ // Handle item selection
+ switch (item.getItemId()) {
+ case R.id.menuBrowse:
+ Intent browse = new Intent(this, FileBrowser.class);
+ startActivityForResult(browse, 0);;
+ return true;
+ case R.id.menuRecord:
+ Intent record = new Intent(this, SoundRecorder.class);
+ startActivityForResult(record, 0);
+ return true;
+ case R.id.menuLocate:
+// Intent locate = new Intent(this, UploadLocation.class);
+// startActivityForResult(locate, 0);;
+ return true;
+ case R.id.menuExit:
+ this.finish();
+ break;
+ default:
+ return super.onOptionsItemSelected(item);
+
+ }
+ return true;
+
+ }
+
+
+}
diff --git a/app/src/main/java/com/lc8n/util/FileUploader.java b/app/src/main/java/com/lc8n/util/FileUploader.java
new file mode 100644
index 0000000..6adf229
--- /dev/null
+++ b/app/src/main/java/com/lc8n/util/FileUploader.java
@@ -0,0 +1,105 @@
+/**
+ *
+ */
+package com.lc8n.util;
+
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.os.AsyncTask;
+import android.util.Log;
+
+import com.lc8n.util.ProgressMultipartEntity.ProgressListener;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.mime.content.FileBody;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.util.EntityUtils;
+
+import java.io.File;
+
+
+/**
+ * @author joe
+ *
+ */
+public class FileUploader extends AsyncTask<File, Integer, String> {
+
+ ProgressDialog pd;
+ int totalSize;
+ protected Context mContext;
+
+ public FileUploader(Context context) {
+
+ mContext = context;
+ }
+ @Override
+ protected void onPreExecute()
+ {
+
+ }
+
+ public void prepare(int max) {
+ pd = new ProgressDialog(mContext);
+ pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
+ pd.setMessage("Uploading File...");
+ pd.setCancelable(false);
+ pd.setMax(max);
+ pd.show();
+ }
+
+ @Override
+ protected String doInBackground(File... file) {
+
+ HttpClient httpClient = new DefaultHttpClient();
+ HttpContext httpContext = new BasicHttpContext();
+ HttpPost httpPost = new HttpPost("http://up.org.je/upload_file.php");
+ Log.d("File", file[0].getName());
+
+ try
+ {
+ ProgressMultipartEntity multipartContent = new ProgressMultipartEntity(new ProgressListener()
+ {
+
+ public void transferred(long num)
+ {
+ publishProgress((int)(num/1024));
+ }
+ });
+
+ // We use FileBody to transfer an image
+ multipartContent.addPart("file", new FileBody(file[0]));
+
+ totalSize = (int)(multipartContent.getContentLength()/1024);
+ Log.d("Size", String.valueOf(totalSize));
+ // Send it
+ httpPost.setEntity(multipartContent);
+ HttpResponse response = httpClient.execute(httpPost, httpContext);
+ String serverResponse = EntityUtils.toString(response.getEntity());
+
+ return serverResponse;
+ }
+
+ catch (Exception e)
+ {
+ Log.e("Upload error", e.getMessage());
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ @Override
+ protected void onProgressUpdate(Integer... progress)
+ {
+ pd.setProgress((int) (progress[0]));
+ }
+
+ @Override
+ protected void onPostExecute(String a)
+ {
+ pd.dismiss();
+ }
+}
diff --git a/app/src/main/java/com/lc8n/util/ProgressMultipartEntity.java b/app/src/main/java/com/lc8n/util/ProgressMultipartEntity.java
new file mode 100644
index 0000000..21142fd
--- /dev/null
+++ b/app/src/main/java/com/lc8n/util/ProgressMultipartEntity.java
@@ -0,0 +1,72 @@
+package com.lc8n.util;
+
+import org.apache.http.entity.mime.HttpMultipartMode;
+import org.apache.http.entity.mime.MultipartEntity;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.charset.Charset;
+
+public class ProgressMultipartEntity extends MultipartEntity
+{
+
+ private final ProgressListener listener;
+
+ public ProgressMultipartEntity(final ProgressListener listener)
+ {
+ super();
+ this.listener = listener;
+ }
+
+ public ProgressMultipartEntity(final HttpMultipartMode mode, final ProgressListener listener)
+ {
+ super(mode);
+ this.listener = listener;
+ }
+
+ public ProgressMultipartEntity(HttpMultipartMode mode, final String boundary, final Charset charset, final ProgressListener listener)
+ {
+ super(mode, boundary, charset);
+ this.listener = listener;
+ }
+
+ @Override
+ public void writeTo(final OutputStream outstream) throws IOException
+ {
+ super.writeTo(new CountingOutputStream(outstream, this.listener));
+ }
+
+ public static interface ProgressListener
+ {
+ void transferred(long num);
+ }
+
+ public static class CountingOutputStream extends FilterOutputStream
+ {
+
+ private final ProgressListener listener;
+ private long transferred;
+
+ public CountingOutputStream(final OutputStream out, final ProgressListener listener)
+ {
+ super(out);
+ this.listener = listener;
+ this.transferred = 0;
+ }
+
+ public void write(byte[] b, int off, int len) throws IOException
+ {
+ out.write(b, off, len);
+ this.transferred += len;
+ this.listener.transferred(this.transferred);
+ }
+
+ public void write(int b) throws IOException
+ {
+ out.write(b);
+ this.transferred++;
+ this.listener.transferred(this.transferred);
+ }
+ }
+} \ No newline at end of file
diff --git a/app/src/main/java/org/apache/commons/net/DatagramSocketClient.java b/app/src/main/java/org/apache/commons/net/DatagramSocketClient.java
new file mode 100644
index 0000000..9199704
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/DatagramSocketClient.java
@@ -0,0 +1,275 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net;
+
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.SocketException;
+
+/***
+ * The DatagramSocketClient provides the basic operations that are required
+ * of client objects accessing datagram sockets. It is meant to be
+ * subclassed to avoid having to rewrite the same code over and over again
+ * to open a socket, close a socket, set timeouts, etc. Of special note
+ * is the {@link #setDatagramSocketFactory setDatagramSocketFactory }
+ * method, which allows you to control the type of DatagramSocket the
+ * DatagramSocketClient creates for network communications. This is
+ * especially useful for adding things like proxy support as well as better
+ * support for applets. For
+ * example, you could create a
+ * {@link org.apache.commons.net.DatagramSocketFactory}
+ * that
+ * requests browser security capabilities before creating a socket.
+ * All classes derived from DatagramSocketClient should use the
+ * {@link #_socketFactory_ _socketFactory_ } member variable to
+ * create DatagramSocket instances rather than instantiating
+ * them by directly invoking a constructor. By honoring this contract
+ * you guarantee that a user will always be able to provide his own
+ * Socket implementations by substituting his own SocketFactory.
+ * <p>
+ * <p>
+ * @author Daniel F. Savarese
+ * @see DatagramSocketFactory
+ ***/
+
+public abstract class DatagramSocketClient
+{
+ /***
+ * The default DatagramSocketFactory shared by all DatagramSocketClient
+ * instances.
+ ***/
+ private static final DatagramSocketFactory __DEFAULT_SOCKET_FACTORY =
+ new DefaultDatagramSocketFactory();
+
+ /*** The timeout to use after opening a socket. ***/
+ protected int _timeout_;
+
+ /*** The datagram socket used for the connection. ***/
+ protected DatagramSocket _socket_;
+
+ /***
+ * A status variable indicating if the client's socket is currently open.
+ ***/
+ protected boolean _isOpen_;
+
+ /*** The datagram socket's DatagramSocketFactory. ***/
+ protected DatagramSocketFactory _socketFactory_;
+
+ /***
+ * Default constructor for DatagramSocketClient. Initializes
+ * _socket_ to null, _timeout_ to 0, and _isOpen_ to false.
+ ***/
+ public DatagramSocketClient()
+ {
+ _socket_ = null;
+ _timeout_ = 0;
+ _isOpen_ = false;
+ _socketFactory_ = __DEFAULT_SOCKET_FACTORY;
+ }
+
+
+ /***
+ * Opens a DatagramSocket on the local host at the first available port.
+ * Also sets the timeout on the socket to the default timeout set
+ * by {@link #setDefaultTimeout setDefaultTimeout() }.
+ * <p>
+ * _isOpen_ is set to true after calling this method and _socket_
+ * is set to the newly opened socket.
+ * <p>
+ * @exception SocketException If the socket could not be opened or the
+ * timeout could not be set.
+ ***/
+ public void open() throws SocketException
+ {
+ _socket_ = _socketFactory_.createDatagramSocket();
+ _socket_.setSoTimeout(_timeout_);
+ _isOpen_ = true;
+ }
+
+
+ /***
+ * Opens a DatagramSocket on the local host at a specified port.
+ * Also sets the timeout on the socket to the default timeout set
+ * by {@link #setDefaultTimeout setDefaultTimeout() }.
+ * <p>
+ * _isOpen_ is set to true after calling this method and _socket_
+ * is set to the newly opened socket.
+ * <p>
+ * @param port The port to use for the socket.
+ * @exception SocketException If the socket could not be opened or the
+ * timeout could not be set.
+ ***/
+ public void open(int port) throws SocketException
+ {
+ _socket_ = _socketFactory_.createDatagramSocket(port);
+ _socket_.setSoTimeout(_timeout_);
+ _isOpen_ = true;
+ }
+
+
+ /***
+ * Opens a DatagramSocket at the specified address on the local host
+ * at a specified port.
+ * Also sets the timeout on the socket to the default timeout set
+ * by {@link #setDefaultTimeout setDefaultTimeout() }.
+ * <p>
+ * _isOpen_ is set to true after calling this method and _socket_
+ * is set to the newly opened socket.
+ * <p>
+ * @param port The port to use for the socket.
+ * @param laddr The local address to use.
+ * @exception SocketException If the socket could not be opened or the
+ * timeout could not be set.
+ ***/
+ public void open(int port, InetAddress laddr) throws SocketException
+ {
+ _socket_ = _socketFactory_.createDatagramSocket(port, laddr);
+ _socket_.setSoTimeout(_timeout_);
+ _isOpen_ = true;
+ }
+
+
+
+ /***
+ * Closes the DatagramSocket used for the connection.
+ * You should call this method after you've finished using the class
+ * instance and also before you call {@link #open open() }
+ * again. _isOpen_ is set to false and _socket_ is set to null.
+ * If you call this method when the client socket is not open,
+ * a NullPointerException is thrown.
+ ***/
+ public void close()
+ {
+ _socket_.close();
+ _socket_ = null;
+ _isOpen_ = false;
+ }
+
+
+ /***
+ * Returns true if the client has a currently open socket.
+ * <p>
+ * @return True if the client has a curerntly open socket, false otherwise.
+ ***/
+ public boolean isOpen()
+ {
+ return _isOpen_;
+ }
+
+
+ /***
+ * Set the default timeout in milliseconds to use when opening a socket.
+ * After a call to open, the timeout for the socket is set using this value.
+ * This method should be used prior to a call to {@link #open open()}
+ * and should not be confused with {@link #setSoTimeout setSoTimeout()}
+ * which operates on the currently open socket. _timeout_ contains
+ * the new timeout value.
+ * <p>
+ * @param timeout The timeout in milliseconds to use for the datagram socket
+ * connection.
+ ***/
+ public void setDefaultTimeout(int timeout)
+ {
+ _timeout_ = timeout;
+ }
+
+
+ /***
+ * Returns the default timeout in milliseconds that is used when
+ * opening a socket.
+ * <p>
+ * @return The default timeout in milliseconds that is used when
+ * opening a socket.
+ ***/
+ public int getDefaultTimeout()
+ {
+ return _timeout_;
+ }
+
+
+ /***
+ * Set the timeout in milliseconds of a currently open connection.
+ * Only call this method after a connection has been opened
+ * by {@link #open open()}.
+ * <p>
+ * @param timeout The timeout in milliseconds to use for the currently
+ * open datagram socket connection.
+ ***/
+ public void setSoTimeout(int timeout) throws SocketException
+ {
+ _socket_.setSoTimeout(timeout);
+ }
+
+
+ /***
+ * Returns the timeout in milliseconds of the currently opened socket.
+ * If you call this method when the client socket is not open,
+ * a NullPointerException is thrown.
+ * <p>
+ * @return The timeout in milliseconds of the currently opened socket.
+ ***/
+ public int getSoTimeout() throws SocketException
+ {
+ return _socket_.getSoTimeout();
+ }
+
+
+ /***
+ * Returns the port number of the open socket on the local host used
+ * for the connection. If you call this method when the client socket
+ * is not open, a NullPointerException is thrown.
+ * <p>
+ * @return The port number of the open socket on the local host used
+ * for the connection.
+ ***/
+ public int getLocalPort()
+ {
+ return _socket_.getLocalPort();
+ }
+
+
+ /***
+ * Returns the local address to which the client's socket is bound.
+ * If you call this method when the client socket is not open, a
+ * NullPointerException is thrown.
+ * <p>
+ * @return The local address to which the client's socket is bound.
+ ***/
+ public InetAddress getLocalAddress()
+ {
+ return _socket_.getLocalAddress();
+ }
+
+
+ /***
+ * Sets the DatagramSocketFactory used by the DatagramSocketClient
+ * to open DatagramSockets. If the factory value is null, then a default
+ * factory is used (only do this to reset the factory after having
+ * previously altered it).
+ * <p>
+ * @param factory The new DatagramSocketFactory the DatagramSocketClient
+ * should use.
+ ***/
+ public void setDatagramSocketFactory(DatagramSocketFactory factory)
+ {
+ if (factory == null)
+ _socketFactory_ = __DEFAULT_SOCKET_FACTORY;
+ else
+ _socketFactory_ = factory;
+ }
+}
diff --git a/app/src/main/java/org/apache/commons/net/DatagramSocketFactory.java b/app/src/main/java/org/apache/commons/net/DatagramSocketFactory.java
new file mode 100644
index 0000000..c152eb2
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/DatagramSocketFactory.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net;
+
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.SocketException;
+
+/***
+ * The DatagramSocketFactory interface provides a means for the
+ * programmer to control the creation of datagram sockets and
+ * provide his own DatagramSocket implementations for use by all
+ * classes derived from
+ * {@link org.apache.commons.net.DatagramSocketClient}
+ * .
+ * This allows you to provide your own DatagramSocket implementations and
+ * to perform security checks or browser capability requests before
+ * creating a DatagramSocket.
+ * <p>
+ * <p>
+ * @author Daniel F. Savarese
+ ***/
+
+public interface DatagramSocketFactory
+{
+
+ /***
+ * Creates a DatagramSocket on the local host at the first available port.
+ * <p>
+ * @exception SocketException If the socket could not be created.
+ ***/
+ public DatagramSocket createDatagramSocket() throws SocketException;
+
+ /***
+ * Creates a DatagramSocket on the local host at a specified port.
+ * <p>
+ * @param port The port to use for the socket.
+ * @exception SocketException If the socket could not be created.
+ ***/
+ public DatagramSocket createDatagramSocket(int port) throws SocketException;
+
+ /***
+ * Creates a DatagramSocket at the specified address on the local host
+ * at a specified port.
+ * <p>
+ * @param port The port to use for the socket.
+ * @param laddr The local address to use.
+ * @exception SocketException If the socket could not be created.
+ ***/
+ public DatagramSocket createDatagramSocket(int port, InetAddress laddr)
+ throws SocketException;
+}
diff --git a/app/src/main/java/org/apache/commons/net/DefaultDatagramSocketFactory.java b/app/src/main/java/org/apache/commons/net/DefaultDatagramSocketFactory.java
new file mode 100644
index 0000000..3983fcb
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/DefaultDatagramSocketFactory.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net;
+
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.SocketException;
+
+/***
+ * DefaultDatagramSocketFactory implements the DatagramSocketFactory
+ * interface by simply wrapping the java.net.DatagramSocket
+ * constructors. It is the default DatagramSocketFactory used by
+ * {@link org.apache.commons.net.DatagramSocketClient}
+ * implementations.
+ * <p>
+ * <p>
+ * @author Daniel F. Savarese
+ * @see DatagramSocketFactory
+ * @see DatagramSocketClient
+ * @see DatagramSocketClient#setDatagramSocketFactory
+ ***/
+
+public class DefaultDatagramSocketFactory implements DatagramSocketFactory
+{
+
+ /***
+ * Creates a DatagramSocket on the local host at the first available port.
+ * <p>
+ * @exception SocketException If the socket could not be created.
+ ***/
+ public DatagramSocket createDatagramSocket() throws SocketException
+ {
+ return new DatagramSocket();
+ }
+
+ /***
+ * Creates a DatagramSocket on the local host at a specified port.
+ * <p>
+ * @param port The port to use for the socket.
+ * @exception SocketException If the socket could not be created.
+ ***/
+ public DatagramSocket createDatagramSocket(int port) throws SocketException
+ {
+ return new DatagramSocket(port);
+ }
+
+ /***
+ * Creates a DatagramSocket at the specified address on the local host
+ * at a specified port.
+ * <p>
+ * @param port The port to use for the socket.
+ * @param laddr The local address to use.
+ * @exception SocketException If the socket could not be created.
+ ***/
+ public DatagramSocket createDatagramSocket(int port, InetAddress laddr)
+ throws SocketException
+ {
+ return new DatagramSocket(port, laddr);
+ }
+}
diff --git a/app/src/main/java/org/apache/commons/net/DefaultSocketFactory.java b/app/src/main/java/org/apache/commons/net/DefaultSocketFactory.java
new file mode 100644
index 0000000..e809f84
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/DefaultSocketFactory.java
@@ -0,0 +1,165 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.UnknownHostException;
+
+import javax.net.SocketFactory;
+
+/***
+ * DefaultSocketFactory implements the SocketFactory interface by
+ * simply wrapping the java.net.Socket and java.net.ServerSocket
+ * constructors. It is the default SocketFactory used by
+ * {@link org.apache.commons.net.SocketClient}
+ * implementations.
+ * <p>
+ * <p>
+ * @author Daniel F. Savarese
+ * @see SocketFactory
+ * @see SocketClient
+ * @see SocketClient#setSocketFactory
+ ***/
+
+public class DefaultSocketFactory extends SocketFactory
+{
+
+ /***
+ * Creates a Socket connected to the given host and port.
+ * <p>
+ * @param host The hostname to connect to.
+ * @param port The port to connect to.
+ * @return A Socket connected to the given host and port.
+ * @exception UnknownHostException If the hostname cannot be resolved.
+ * @exception IOException If an I/O error occurs while creating the Socket.
+ ***/
+ @Override
+ public Socket createSocket(String host, int port)
+ throws UnknownHostException, IOException
+ {
+ return new Socket(host, port);
+ }
+
+ /***
+ * Creates a Socket connected to the given host and port.
+ * <p>
+ * @param address The address of the host to connect to.
+ * @param port The port to connect to.
+ * @return A Socket connected to the given host and port.
+ * @exception IOException If an I/O error occurs while creating the Socket.
+ ***/
+ @Override
+ public Socket createSocket(InetAddress address, int port)
+ throws IOException
+ {
+ return new Socket(address, port);
+ }
+
+ /***
+ * Creates a Socket connected to the given host and port and
+ * originating from the specified local address and port.
+ * <p>
+ * @param host The hostname to connect to.
+ * @param port The port to connect to.
+ * @param localAddr The local address to use.
+ * @param localPort The local port to use.
+ * @return A Socket connected to the given host and port.
+ * @exception UnknownHostException If the hostname cannot be resolved.
+ * @exception IOException If an I/O error occurs while creating the Socket.
+ ***/
+ @Override
+ public Socket createSocket(String host, int port,
+ InetAddress localAddr, int localPort)
+ throws UnknownHostException, IOException
+ {
+ return new Socket(host, port, localAddr, localPort);
+ }
+
+ /***
+ * Creates a Socket connected to the given host and port and
+ * originating from the specified local address and port.
+ * <p>
+ * @param address The address of the host to connect to.
+ * @param port The port to connect to.
+ * @param localAddr The local address to use.
+ * @param localPort The local port to use.
+ * @return A Socket connected to the given host and port.
+ * @exception IOException If an I/O error occurs while creating the Socket.
+ ***/
+ @Override
+ public Socket createSocket(InetAddress address, int port,
+ InetAddress localAddr, int localPort)
+ throws IOException
+ {
+ return new Socket(address, port, localAddr, localPort);
+ }
+
+ /***
+ * Creates a ServerSocket bound to a specified port. A port
+ * of 0 will create the ServerSocket on a system-determined free port.
+ * <p>
+ * @param port The port on which to listen, or 0 to use any free port.
+ * @return A ServerSocket that will listen on a specified port.
+ * @exception IOException If an I/O error occurs while creating
+ * the ServerSocket.
+ ***/
+ public ServerSocket createServerSocket(int port) throws IOException
+ {
+ return new ServerSocket(port);
+ }
+
+ /***
+ * Creates a ServerSocket bound to a specified port with a given
+ * maximum queue length for incoming connections. A port of 0 will
+ * create the ServerSocket on a system-determined free port.
+ * <p>
+ * @param port The port on which to listen, or 0 to use any free port.
+ * @param backlog The maximum length of the queue for incoming connections.
+ * @return A ServerSocket that will listen on a specified port.
+ * @exception IOException If an I/O error occurs while creating
+ * the ServerSocket.
+ ***/
+ public ServerSocket createServerSocket(int port, int backlog)
+ throws IOException
+ {
+ return new ServerSocket(port, backlog);
+ }
+
+ /***
+ * Creates a ServerSocket bound to a specified port on a given local
+ * address with a given maximum queue length for incoming connections.
+ * A port of 0 will
+ * create the ServerSocket on a system-determined free port.
+ * <p>
+ * @param port The port on which to listen, or 0 to use any free port.
+ * @param backlog The maximum length of the queue for incoming connections.
+ * @param bindAddr The local address to which the ServerSocket should bind.
+ * @return A ServerSocket that will listen on a specified port.
+ * @exception IOException If an I/O error occurs while creating
+ * the ServerSocket.
+ ***/
+ public ServerSocket createServerSocket(int port, int backlog,
+ InetAddress bindAddr)
+ throws IOException
+ {
+ return new ServerSocket(port, backlog, bindAddr);
+ }
+}
diff --git a/app/src/main/java/org/apache/commons/net/MalformedServerReplyException.java b/app/src/main/java/org/apache/commons/net/MalformedServerReplyException.java
new file mode 100644
index 0000000..3cad5f0
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/MalformedServerReplyException.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net;
+
+import java.io.IOException;
+
+/***
+ * This exception is used to indicate that the reply from a server
+ * could not be interpreted. Most of the NetComponents classes attempt
+ * to be as lenient as possible when receiving server replies. Many
+ * server implementations deviate from IETF protocol specifications, making
+ * it necessary to be as flexible as possible. However, there will be
+ * certain situations where it is not possible to continue an operation
+ * because the server reply could not be interpreted in a meaningful manner.
+ * In these cases, a MalformedServerReplyException should be thrown.
+ * <p>
+ * <p>
+ * @author Daniel F. Savarese
+ ***/
+
+public class MalformedServerReplyException extends IOException
+{
+
+ /*** Constructs a MalformedServerReplyException with no message ***/
+ public MalformedServerReplyException()
+ {
+ super();
+ }
+
+ /***
+ * Constructs a MalformedServerReplyException with a specified message.
+ * <p>
+ * @param message The message explaining the reason for the exception.
+ ***/
+ public MalformedServerReplyException(String message)
+ {
+ super(message);
+ }
+
+}
diff --git a/app/src/main/java/org/apache/commons/net/PrintCommandListener.java b/app/src/main/java/org/apache/commons/net/PrintCommandListener.java
new file mode 100644
index 0000000..78b5d38
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/PrintCommandListener.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net;
+
+import java.io.PrintWriter;
+
+/***
+ * This is a support class for some of the example programs. It is
+ * a sample implementation of the ProtocolCommandListener interface
+ * which just prints out to a specified stream all command/reply traffic.
+ * <p>
+ *
+ * @since 2.0
+ ***/
+
+public class PrintCommandListener implements ProtocolCommandListener
+{
+ private PrintWriter __writer;
+
+ public PrintCommandListener(PrintWriter writer)
+ {
+ __writer = writer;
+ }
+
+ public void protocolCommandSent(ProtocolCommandEvent event)
+ {
+ __writer.print(event.getMessage());
+ __writer.flush();
+ }
+
+ public void protocolReplyReceived(ProtocolCommandEvent event)
+ {
+ __writer.print(event.getMessage());
+ __writer.flush();
+ }
+}
+
diff --git a/app/src/main/java/org/apache/commons/net/ProtocolCommandEvent.java b/app/src/main/java/org/apache/commons/net/ProtocolCommandEvent.java
new file mode 100644
index 0000000..8977c03
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/ProtocolCommandEvent.java
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net;
+import java.util.EventObject;
+
+/***
+ * There exists a large class of IETF protocols that work by sending an
+ * ASCII text command and arguments to a server, and then receiving an
+ * ASCII text reply. For debugging and other purposes, it is extremely
+ * useful to log or keep track of the contents of the protocol messages.
+ * The ProtocolCommandEvent class coupled with the
+ * {@link org.apache.commons.net.ProtocolCommandListener}
+ * interface facilitate this process.
+ * <p>
+ * <p>
+ * @see ProtocolCommandListener
+ * @see ProtocolCommandSupport
+ * @author Daniel F. Savarese
+ ***/
+
+public class ProtocolCommandEvent extends EventObject
+{
+ private int __replyCode;
+ private boolean __isCommand;
+ private String __message, __command;
+
+ /***
+ * Creates a ProtocolCommandEvent signalling a command was sent to
+ * the server. ProtocolCommandEvents created with this constructor
+ * should only be sent after a command has been sent, but before the
+ * reply has been received.
+ * <p>
+ * @param source The source of the event.
+ * @param command The string representation of the command type sent, not
+ * including the arguments (e.g., "STAT" or "GET").
+ * @param message The entire command string verbatim as sent to the server,
+ * including all arguments.
+ ***/
+ public ProtocolCommandEvent(Object source, String command, String message)
+ {
+ super(source);
+ __replyCode = 0;
+ __message = message;
+ __isCommand = true;
+ __command = command;
+ }
+
+
+ /***
+ * Creates a ProtocolCommandEvent signalling a reply to a command was
+ * received. ProtocolCommandEvents created with this constructor
+ * should only be sent after a complete command reply has been received
+ * fromt a server.
+ * <p>
+ * @param source The source of the event.
+ * @param replyCode The integer code indicating the natureof the reply.
+ * This will be the protocol integer value for protocols
+ * that use integer reply codes, or the reply class constant
+ * corresponding to the reply for protocols like POP3 that use
+ * strings like OK rather than integer codes (i.e., POP3Repy.OK).
+ * @param message The entire reply as received from the server.
+ ***/
+ public ProtocolCommandEvent(Object source, int replyCode, String message)
+ {
+ super(source);
+ __replyCode = replyCode;
+ __message = message;
+ __isCommand = false;
+ __command = null;
+ }
+
+ /***
+ * Returns the string representation of the command type sent (e.g., "STAT"
+ * or "GET"). If the ProtocolCommandEvent is a reply event, then null
+ * is returned.
+ * <p>
+ * @return The string representation of the command type sent, or null
+ * if this is a reply event.
+ ***/
+ public String getCommand()
+ {
+ return __command;
+ }
+
+
+ /***
+ * Returns the reply code of the received server reply. Undefined if
+ * this is not a reply event.
+ * <p>
+ * @return The reply code of the received server reply. Undefined if
+ * not a reply event.
+ ***/
+ public int getReplyCode()
+ {
+ return __replyCode;
+ }
+
+ /***
+ * Returns true if the ProtocolCommandEvent was generated as a result
+ * of sending a command.
+ * <p>
+ * @return true If the ProtocolCommandEvent was generated as a result
+ * of sending a command. False otherwise.
+ ***/
+ public boolean isCommand()
+ {
+ return __isCommand;
+ }
+
+ /***
+ * Returns true if the ProtocolCommandEvent was generated as a result
+ * of receiving a reply.
+ * <p>
+ * @return true If the ProtocolCommandEvent was generated as a result
+ * of receiving a reply. False otherwise.
+ ***/
+ public boolean isReply()
+ {
+ return !isCommand();
+ }
+
+ /***
+ * Returns the entire message sent to or received from the server.
+ * <p>
+ * @return The entire message sent to or received from the server.
+ ***/
+ public String getMessage()
+ {
+ return __message;
+ }
+}
diff --git a/app/src/main/java/org/apache/commons/net/ProtocolCommandListener.java b/app/src/main/java/org/apache/commons/net/ProtocolCommandListener.java
new file mode 100644
index 0000000..8089926
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/ProtocolCommandListener.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net;
+import java.util.EventListener;
+
+/***
+ * There exists a large class of IETF protocols that work by sending an
+ * ASCII text command and arguments to a server, and then receiving an
+ * ASCII text reply. For debugging and other purposes, it is extremely
+ * useful to log or keep track of the contents of the protocol messages.
+ * The ProtocolCommandListener interface coupled with the
+ * {@link ProtocolCommandEvent} class facilitate this process.
+ * <p>
+ * To receive ProtocolCommandEvents, you merely implement the
+ * ProtocolCommandListener interface and register the class as a listener
+ * with a ProtocolCommandEvent source such as
+ * {@link org.apache.commons.net.ftp.FTPClient}.
+ * <p>
+ * <p>
+ * @see ProtocolCommandEvent
+ * @see ProtocolCommandSupport
+ * @author Daniel F. Savarese
+ ***/
+
+public interface ProtocolCommandListener extends EventListener
+{
+
+ /***
+ * This method is invoked by a ProtocolCommandEvent source after
+ * sending a protocol command to a server.
+ * <p>
+ * @param event The ProtocolCommandEvent fired.
+ ***/
+ public void protocolCommandSent(ProtocolCommandEvent event);
+
+ /***
+ * This method is invoked by a ProtocolCommandEvent source after
+ * receiving a reply from a server.
+ * <p>
+ * @param event The ProtocolCommandEvent fired.
+ ***/
+ public void protocolReplyReceived(ProtocolCommandEvent event);
+
+}
diff --git a/app/src/main/java/org/apache/commons/net/ProtocolCommandSupport.java b/app/src/main/java/org/apache/commons/net/ProtocolCommandSupport.java
new file mode 100644
index 0000000..e9e4380
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/ProtocolCommandSupport.java
@@ -0,0 +1,134 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net;
+
+import org.apache.commons.net.util.ListenerList;
+
+import java.io.Serializable;
+import java.util.EventListener;
+
+/***
+ * ProtocolCommandSupport is a convenience class for managing a list of
+ * ProtocolCommandListeners and firing ProtocolCommandEvents. You can
+ * simply delegate ProtocolCommandEvent firing and listener
+ * registering/unregistering tasks to this class.
+ * <p>
+ * <p>
+ * @see ProtocolCommandEvent
+ * @see ProtocolCommandListener
+ * @author Daniel F. Savarese
+ ***/
+
+public class ProtocolCommandSupport implements Serializable
+{
+ private Object __source;
+ private ListenerList __listeners;
+
+ /***
+ * Creates a ProtocolCommandSupport instant using the indicated source
+ * as the source of fired ProtocolCommandEvents.
+ * <p>
+ * @param source The source to use for all generated ProtocolCommandEvents.
+ ***/
+ public ProtocolCommandSupport(Object source)
+ {
+ __listeners = new ListenerList();
+ __source = source;
+ }
+
+
+ /***
+ * Fires a ProtocolCommandEvent signalling the sending of a command to all
+ * registered listeners, invoking their
+ * {@link org.apache.commons.net.ProtocolCommandListener#protocolCommandSent protocolCommandSent() }
+ * methods.
+ * <p>
+ * @param command The string representation of the command type sent, not
+ * including the arguments (e.g., "STAT" or "GET").
+ * @param message The entire command string verbatim as sent to the server,
+ * including all arguments.
+ ***/
+ public void fireCommandSent(String command, String message)
+ {
+ ProtocolCommandEvent event;
+
+ event = new ProtocolCommandEvent(__source, command, message);
+
+ for (EventListener listener : __listeners)
+ {
+ ((ProtocolCommandListener)listener).protocolCommandSent(event);
+ }
+ }
+
+ /***
+ * Fires a ProtocolCommandEvent signalling the reception of a command reply
+ * to all registered listeners, invoking their
+ * {@link org.apache.commons.net.ProtocolCommandListener#protocolReplyReceived protocolReplyReceived() }
+ * methods.
+ * <p>
+ * @param replyCode The integer code indicating the natureof the reply.
+ * This will be the protocol integer value for protocols
+ * that use integer reply codes, or the reply class constant
+ * corresponding to the reply for protocols like POP3 that use
+ * strings like OK rather than integer codes (i.e., POP3Repy.OK).
+ * @param message The entire reply as received from the server.
+ ***/
+ public void fireReplyReceived(int replyCode, String message)
+ {
+ ProtocolCommandEvent event;
+ event = new ProtocolCommandEvent(__source, replyCode, message);
+
+ for (EventListener listener : __listeners)
+ {
+ ((ProtocolCommandListener)listener).protocolReplyReceived(event);
+ }
+ }
+
+ /***
+ * Adds a ProtocolCommandListener.
+ * <p>
+ * @param listener The ProtocolCommandListener to add.
+ ***/
+ public void addProtocolCommandListener(ProtocolCommandListener listener)
+ {
+ __listeners.addListener(listener);
+ }
+
+ /***
+ * Removes a ProtocolCommandListener.
+ * <p>
+ * @param listener The ProtocolCommandListener to remove.
+ ***/
+ public void removeProtocolCommandListener(ProtocolCommandListener listener)
+ {
+ __listeners.removeListener(listener);
+ }
+
+
+ /***
+ * Returns the number of ProtocolCommandListeners currently registered.
+ * <p>
+ * @return The number of ProtocolCommandListeners currently registered.
+ ***/
+ public int getListenerCount()
+ {
+ return __listeners.getListenerCount();
+ }
+
+}
+
diff --git a/app/src/main/java/org/apache/commons/net/SocketClient.java b/app/src/main/java/org/apache/commons/net/SocketClient.java
new file mode 100644
index 0000000..a5a8ead
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/SocketClient.java
@@ -0,0 +1,586 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+
+import javax.net.ServerSocketFactory;
+import javax.net.SocketFactory;
+
+
+/**
+ * The SocketClient provides the basic operations that are required of
+ * client objects accessing sockets. It is meant to be
+ * subclassed to avoid having to rewrite the same code over and over again
+ * to open a socket, close a socket, set timeouts, etc. Of special note
+ * is the {@link #setSocketFactory setSocketFactory }
+ * method, which allows you to control the type of Socket the SocketClient
+ * creates for initiating network connections. This is especially useful
+ * for adding SSL or proxy support as well as better support for applets. For
+ * example, you could create a
+ * {@link org.apache.commons.net.SocketFactory} that
+ * requests browser security capabilities before creating a socket.
+ * All classes derived from SocketClient should use the
+ * {@link #_socketFactory_ _socketFactory_ } member variable to
+ * create Socket and ServerSocket instances rather than instanting
+ * them by directly invoking a constructor. By honoring this contract
+ * you guarantee that a user will always be able to provide his own
+ * Socket implementations by substituting his own SocketFactory.
+ * @author Daniel F. Savarese
+ * @see SocketFactory
+ */
+public abstract class SocketClient
+{
+ /**
+ * The end of line character sequence used by most IETF protocols. That
+ * is a carriage return followed by a newline: "\r\n"
+ */
+ public static final String NETASCII_EOL = "\r\n";
+
+ /** The default SocketFactory shared by all SocketClient instances. */
+ private static final SocketFactory __DEFAULT_SOCKET_FACTORY =
+ SocketFactory.getDefault();
+
+ private static final ServerSocketFactory __DEFAULT_SERVER_SOCKET_FACTORY =
+ ServerSocketFactory.getDefault();
+
+ /** The timeout to use after opening a socket. */
+ protected int _timeout_;
+
+ /** The socket used for the connection. */
+ protected Socket _socket_;
+
+ /** The default port the client should connect to. */
+ protected int _defaultPort_;
+
+ /** The socket's InputStream. */
+ protected InputStream _input_;
+
+ /** The socket's OutputStream. */
+ protected OutputStream _output_;
+
+ /** The socket's SocketFactory. */
+ protected SocketFactory _socketFactory_;
+
+ /** The socket's ServerSocket Factory. */
+ protected ServerSocketFactory _serverSocketFactory_;
+
+ /** The socket's connect timeout (0 = infinite timeout) */
+ private static final int DEFAULT_CONNECT_TIMEOUT = 0;
+ protected int connectTimeout = DEFAULT_CONNECT_TIMEOUT;
+
+ /**
+ * Default constructor for SocketClient. Initializes
+ * _socket_ to null, _timeout_ to 0, _defaultPort to 0,
+ * _isConnected_ to false, and _socketFactory_ to a shared instance of
+ * {@link org.apache.commons.net.DefaultSocketFactory}.
+ */
+ public SocketClient()
+ {
+ _socket_ = null;
+ _input_ = null;
+ _output_ = null;
+ _timeout_ = 0;
+ _defaultPort_ = 0;
+ _socketFactory_ = __DEFAULT_SOCKET_FACTORY;
+ _serverSocketFactory_ = __DEFAULT_SERVER_SOCKET_FACTORY;
+ }
+
+
+ /**
+ * Because there are so many connect() methods, the _connectAction_()
+ * method is provided as a means of performing some action immediately
+ * after establishing a connection, rather than reimplementing all
+ * of the connect() methods. The last action performed by every
+ * connect() method after opening a socket is to call this method.
+ * <p>
+ * This method sets the timeout on the just opened socket to the default
+ * timeout set by {@link #setDefaultTimeout setDefaultTimeout() },
+ * sets _input_ and _output_ to the socket's InputStream and OutputStream
+ * respectively, and sets _isConnected_ to true.
+ * <p>
+ * Subclasses overriding this method should start by calling
+ * <code> super._connectAction_() </code> first to ensure the
+ * initialization of the aforementioned protected variables.
+ */
+ protected void _connectAction_() throws IOException
+ {
+ _socket_.setSoTimeout(_timeout_);
+ _input_ = _socket_.getInputStream();
+ _output_ = _socket_.getOutputStream();
+ }
+
+
+ /**
+ * Opens a Socket connected to a remote host at the specified port and
+ * originating from the current host at a system assigned port.
+ * Before returning, {@link #_connectAction_ _connectAction_() }
+ * is called to perform connection initialization actions.
+ * <p>
+ * @param host The remote host.
+ * @param port The port to connect to on the remote host.
+ * @exception SocketException If the socket timeout could not be set.
+ * @exception IOException If the socket could not be opened. In most
+ * cases you will only want to catch IOException since SocketException is
+ * derived from it.
+ */
+ public void connect(InetAddress host, int port)
+ throws SocketException, IOException
+ {
+ _socket_ = _socketFactory_.createSocket();
+ _socket_.connect(new InetSocketAddress(host, port), connectTimeout);
+
+ _connectAction_();
+ }
+
+ /**
+ * Opens a Socket connected to a remote host at the specified port and
+ * originating from the current host at a system assigned port.
+ * Before returning, {@link #_connectAction_ _connectAction_() }
+ * is called to perform connection initialization actions.
+ * <p>
+ * @param hostname The name of the remote host.
+ * @param port The port to connect to on the remote host.
+ * @exception SocketException If the socket timeout could not be set.
+ * @exception IOException If the socket could not be opened. In most
+ * cases you will only want to catch IOException since SocketException is
+ * derived from it.
+ * @exception UnknownHostException If the hostname cannot be resolved.
+ */
+ public void connect(String hostname, int port)
+ throws SocketException, IOException
+ {
+ _socket_= _socketFactory_.createSocket();
+ _socket_.connect(new InetSocketAddress(hostname, port), connectTimeout);
+
+ _connectAction_();
+ }
+
+
+ /**
+ * Opens a Socket connected to a remote host at the specified port and
+ * originating from the specified local address and port.
+ * Before returning, {@link #_connectAction_ _connectAction_() }
+ * is called to perform connection initialization actions.
+ * <p>
+ * @param host The remote host.
+ * @param port The port to connect to on the remote host.
+ * @param localAddr The local address to use.
+ * @param localPort The local port to use.
+ * @exception SocketException If the socket timeout could not be set.
+ * @exception IOException If the socket could not be opened. In most
+ * cases you will only want to catch IOException since SocketException is
+ * derived from it.
+ */
+ public void connect(InetAddress host, int port,
+ InetAddress localAddr, int localPort)
+ throws SocketException, IOException
+ {
+ _socket_ = _socketFactory_.createSocket();
+ _socket_.bind(new InetSocketAddress(localAddr, localPort));
+ _socket_.connect(new InetSocketAddress(host, port), connectTimeout);
+
+ _connectAction_();
+ }
+
+
+ /**
+ * Opens a Socket connected to a remote host at the specified port and
+ * originating from the specified local address and port.
+ * Before returning, {@link #_connectAction_ _connectAction_() }
+ * is called to perform connection initialization actions.
+ * <p>
+ * @param hostname The name of the remote host.
+ * @param port The port to connect to on the remote host.
+ * @param localAddr The local address to use.
+ * @param localPort The local port to use.
+ * @exception SocketException If the socket timeout could not be set.
+ * @exception IOException If the socket could not be opened. In most
+ * cases you will only want to catch IOException since SocketException is
+ * derived from it.
+ * @exception UnknownHostException If the hostname cannot be resolved.
+ */
+ public void connect(String hostname, int port,
+ InetAddress localAddr, int localPort)
+ throws SocketException, IOException
+ {
+ _socket_ =
+ _socketFactory_.createSocket(hostname, port, localAddr, localPort);
+ _connectAction_();
+ }
+
+
+ /**
+ * Opens a Socket connected to a remote host at the current default port
+ * and originating from the current host at a system assigned port.
+ * Before returning, {@link #_connectAction_ _connectAction_() }
+ * is called to perform connection initialization actions.
+ * <p>
+ * @param host The remote host.
+ * @exception SocketException If the socket timeout could not be set.
+ * @exception IOException If the socket could not be opened. In most
+ * cases you will only want to catch IOException since SocketException is
+ * derived from it.
+ */
+ public void connect(InetAddress host) throws SocketException, IOException
+ {
+ connect(host, _defaultPort_);
+ }
+
+
+ /**
+ * Opens a Socket connected to a remote host at the current default
+ * port and originating from the current host at a system assigned port.
+ * Before returning, {@link #_connectAction_ _connectAction_() }
+ * is called to perform connection initialization actions.
+ * <p>
+ * @param hostname The name of the remote host.
+ * @exception SocketException If the socket timeout could not be set.
+ * @exception IOException If the socket could not be opened. In most
+ * cases you will only want to catch IOException since SocketException is
+ * derived from it.
+ * @exception UnknownHostException If the hostname cannot be resolved.
+ */
+ public void connect(String hostname) throws SocketException, IOException
+ {
+ connect(hostname, _defaultPort_);
+ }
+
+
+ /**
+ * Disconnects the socket connection.
+ * You should call this method after you've finished using the class
+ * instance and also before you call
+ * {@link #connect connect() }
+ * again. _isConnected_ is set to false, _socket_ is set to null,
+ * _input_ is set to null, and _output_ is set to null.
+ * <p>
+ * @exception IOException If there is an error closing the socket.
+ */
+ public void disconnect() throws IOException
+ {
+ if (_socket_ != null) _socket_.close();
+ if (_input_ != null) _input_.close();
+ if (_output_ != null) _output_.close();
+ if (_socket_ != null) _socket_ = null;
+ _input_ = null;
+ _output_ = null;
+ }
+
+
+ /**
+ * Returns true if the client is currently connected to a server.
+ * <p>
+ * @return True if the client is currently connected to a server,
+ * false otherwise.
+ */
+ public boolean isConnected()
+ {
+ if (_socket_ == null)
+ return false;
+
+ return _socket_.isConnected();
+ }
+
+
+ /**
+ * Sets the default port the SocketClient should connect to when a port
+ * is not specified. The {@link #_defaultPort_ _defaultPort_ }
+ * variable stores this value. If never set, the default port is equal
+ * to zero.
+ * <p>
+ * @param port The default port to set.
+ */
+ public void setDefaultPort(int port)
+ {
+ _defaultPort_ = port;
+ }
+
+ /**
+ * Returns the current value of the default port (stored in
+ * {@link #_defaultPort_ _defaultPort_ }).
+ * <p>
+ * @return The current value of the default port.
+ */
+ public int getDefaultPort()
+ {
+ return _defaultPort_;
+ }
+
+
+ /**
+ * Set the default timeout in milliseconds to use when opening a socket.
+ * This value is only used previous to a call to
+ * {@link #connect connect()}
+ * and should not be confused with {@link #setSoTimeout setSoTimeout()}
+ * which operates on an the currently opened socket. _timeout_ contains
+ * the new timeout value.
+ * <p>
+ * @param timeout The timeout in milliseconds to use for the socket
+ * connection.
+ */
+ public void setDefaultTimeout(int timeout)
+ {
+ _timeout_ = timeout;
+ }
+
+
+ /**
+ * Returns the default timeout in milliseconds that is used when
+ * opening a socket.
+ * <p>
+ * @return The default timeout in milliseconds that is used when
+ * opening a socket.
+ */
+ public int getDefaultTimeout()
+ {
+ return _timeout_;
+ }
+
+
+ /**
+ * Set the timeout in milliseconds of a currently open connection.
+ * Only call this method after a connection has been opened
+ * by {@link #connect connect()}.
+ * <p>
+ * @param timeout The timeout in milliseconds to use for the currently
+ * open socket connection.
+ * @exception SocketException If the operation fails.
+ */
+ public void setSoTimeout(int timeout) throws SocketException
+ {
+ _socket_.setSoTimeout(timeout);
+ }
+
+
+ /**
+ * Set the underlying socket send buffer size.
+ * <p>
+ * @param size The size of the buffer in bytes.
+ * @throws SocketException
+ * @since 2.0
+ */
+ public void setSendBufferSize(int size) throws SocketException {
+ _socket_.setSendBufferSize(size);
+ }
+
+
+ /**
+ * Sets the underlying socket receive buffer size.
+ * <p>
+ * @param size The size of the buffer in bytes.
+ * @throws SocketException
+ * @since 2.0
+ */
+ public void setReceiveBufferSize(int size) throws SocketException {
+ _socket_.setReceiveBufferSize(size);
+ }
+
+
+ /**
+ * Returns the timeout in milliseconds of the currently opened socket.
+ * <p>
+ * @return The timeout in milliseconds of the currently opened socket.
+ * @exception SocketException If the operation fails.
+ */
+ public int getSoTimeout() throws SocketException
+ {
+ return _socket_.getSoTimeout();
+ }
+
+ /**
+ * Enables or disables the Nagle's algorithm (TCP_NODELAY) on the
+ * currently opened socket.
+ * <p>
+ * @param on True if Nagle's algorithm is to be enabled, false if not.
+ * @exception SocketException If the operation fails.
+ */
+ public void setTcpNoDelay(boolean on) throws SocketException
+ {
+ _socket_.setTcpNoDelay(on);
+ }
+
+
+ /**
+ * Returns true if Nagle's algorithm is enabled on the currently opened
+ * socket.
+ * <p>
+ * @return True if Nagle's algorithm is enabled on the currently opened
+ * socket, false otherwise.
+ * @exception SocketException If the operation fails.
+ */
+ public boolean getTcpNoDelay() throws SocketException
+ {
+ return _socket_.getTcpNoDelay();
+ }
+
+
+ /**
+ * Sets the SO_LINGER timeout on the currently opened socket.
+ * <p>
+ * @param on True if linger is to be enabled, false if not.
+ * @param val The linger timeout (in hundredths of a second?)
+ * @exception SocketException If the operation fails.
+ */
+ public void setSoLinger(boolean on, int val) throws SocketException
+ {
+ _socket_.setSoLinger(on, val);
+ }
+
+
+ /**
+ * Returns the current SO_LINGER timeout of the currently opened socket.
+ * <p>
+ * @return The current SO_LINGER timeout. If SO_LINGER is disabled returns
+ * -1.
+ * @exception SocketException If the operation fails.
+ */
+ public int getSoLinger() throws SocketException
+ {
+ return _socket_.getSoLinger();
+ }
+
+
+ /**
+ * Returns the port number of the open socket on the local host used
+ * for the connection.
+ * <p>
+ * @return The port number of the open socket on the local host used
+ * for the connection.
+ */
+ public int getLocalPort()
+ {
+ return _socket_.getLocalPort();
+ }
+
+
+ /**
+ * Returns the local address to which the client's socket is bound.
+ * <p>
+ * @return The local address to which the client's socket is bound.
+ */
+ public InetAddress getLocalAddress()
+ {
+ return _socket_.getLocalAddress();
+ }
+
+ /**
+ * Returns the port number of the remote host to which the client is
+ * connected.
+ * <p>
+ * @return The port number of the remote host to which the client is
+ * connected.
+ */
+ public int getRemotePort()
+ {
+ return _socket_.getPort();
+ }
+
+
+ /**
+ * @return The remote address to which the client is connected.
+ */
+ public InetAddress getRemoteAddress()
+ {
+ return _socket_.getInetAddress();
+ }
+
+
+ /**
+ * Verifies that the remote end of the given socket is connected to the
+ * the same host that the SocketClient is currently connected to. This
+ * is useful for doing a quick security check when a client needs to
+ * accept a connection from a server, such as an FTP data connection or
+ * a BSD R command standard error stream.
+ * <p>
+ * @return True if the remote hosts are the same, false if not.
+ */
+ public boolean verifyRemote(Socket socket)
+ {
+ InetAddress host1, host2;
+
+ host1 = socket.getInetAddress();
+ host2 = getRemoteAddress();
+
+ return host1.equals(host2);
+ }
+
+
+ /**
+ * Sets the SocketFactory used by the SocketClient to open socket
+ * connections. If the factory value is null, then a default
+ * factory is used (only do this to reset the factory after having
+ * previously altered it).
+ * <p>
+ * @param factory The new SocketFactory the SocketClient should use.
+ */
+ public void setSocketFactory(SocketFactory factory)
+ {
+ if (factory == null)
+ _socketFactory_ = __DEFAULT_SOCKET_FACTORY;
+ else
+ _socketFactory_ = factory;
+ }
+
+ /**
+ * Sets the ServerSocketFactory used by the SocketClient to open ServerSocket
+ * connections. If the factory value is null, then a default
+ * factory is used (only do this to reset the factory after having
+ * previously altered it).
+ * <p>
+ * @param factory The new ServerSocketFactory the SocketClient should use.
+ * @since 2.0
+ */
+ public void setServerSocketFactory(ServerSocketFactory factory) {
+ if (factory == null)
+ _serverSocketFactory_ = __DEFAULT_SERVER_SOCKET_FACTORY;
+ else
+ _serverSocketFactory_ = factory;
+ }
+
+ /**
+ * Sets the connection timeout in milliseconds, which will be passed to the {@link Socket} object's
+ * connect() method.
+ * @param connectTimeout The connection timeout to use (in ms)
+ * @since 2.0
+ */
+ public void setConnectTimeout(int connectTimeout) {
+ this.connectTimeout = connectTimeout;
+ }
+
+ /**
+ * Get the underlying socket connection timeout.
+ * @return
+ * @since 2.0
+ */
+ public int getConnectTimeout() {
+ return connectTimeout;
+ }
+
+
+
+}
+
+
diff --git a/app/src/main/java/org/apache/commons/net/ftp/Configurable.java b/app/src/main/java/org/apache/commons/net/ftp/Configurable.java
new file mode 100644
index 0000000..5e8e749
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/ftp/Configurable.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.ftp;
+
+
+/**
+ * This interface adds the aspect of configurability by means of
+ * a supplied FTPClientConfig object to other classes in the
+ * system, especially listing parsers.
+ */
+public interface Configurable {
+
+ /**
+ * @param config the object containing the configuration data
+ * @throws IllegalArgumentException if the elements of the
+ * <code>config</code> are somehow inadequate to configure the
+ * Configurable object.
+ */
+ public void configure(FTPClientConfig config);
+}
diff --git a/app/src/main/java/org/apache/commons/net/ftp/FTP.java b/app/src/main/java/org/apache/commons/net/ftp/FTP.java
new file mode 100644
index 0000000..b906a40
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/ftp/FTP.java
@@ -0,0 +1,1514 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.ftp;
+
+import org.apache.commons.net.MalformedServerReplyException;
+import org.apache.commons.net.ProtocolCommandListener;
+import org.apache.commons.net.ProtocolCommandSupport;
+import org.apache.commons.net.SocketClient;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.SocketException;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/***
+ * FTP provides the basic the functionality necessary to implement your
+ * own FTP client. It extends org.apache.commons.net.SocketClient since
+ * extending TelnetClient was causing unwanted behavior (like connections
+ * that did not time out properly).
+ * <p>
+ * To derive the full benefits of the FTP class requires some knowledge
+ * of the FTP protocol defined in RFC 959. However, there is no reason
+ * why you should have to use the FTP class. The
+ * {@link org.apache.commons.net.ftp.FTPClient} class,
+ * derived from FTP,
+ * implements all the functionality required of an FTP client. The
+ * FTP class is made public to provide access to various FTP constants
+ * and to make it easier for adventurous programmers (or those with
+ * special needs) to interact with the FTP protocol and implement their
+ * own clients. A set of methods with names corresponding to the FTP
+ * command names are provided to facilitate this interaction.
+ * <p>
+ * You should keep in mind that the FTP server may choose to prematurely
+ * close a connection if the client has been idle for longer than a
+ * given time period (usually 900 seconds). The FTP class will detect a
+ * premature FTP server connection closing when it receives a
+ * {@link org.apache.commons.net.ftp.FTPReply#SERVICE_NOT_AVAILABLE FTPReply.SERVICE_NOT_AVAILABLE }
+ * response to a command.
+ * When that occurs, the FTP class method encountering that reply will throw
+ * an {@link org.apache.commons.net.ftp.FTPConnectionClosedException}
+ * . <code>FTPConectionClosedException</code>
+ * is a subclass of <code> IOException </code> and therefore need not be
+ * caught separately, but if you are going to catch it separately, its
+ * catch block must appear before the more general <code> IOException </code>
+ * catch block. When you encounter an
+ * {@link org.apache.commons.net.ftp.FTPConnectionClosedException}
+ * , you must disconnect the connection with
+ * {@link #disconnect disconnect() } to properly clean up the
+ * system resources used by FTP. Before disconnecting, you may check the
+ * last reply code and text with
+ * {@link #getReplyCode getReplyCode },
+ * {@link #getReplyString getReplyString },
+ * and {@link #getReplyStrings getReplyStrings}.
+ * You may avoid server disconnections while the client is idle by
+ * periodicaly sending NOOP commands to the server.
+ * <p>
+ * Rather than list it separately for each method, we mention here that
+ * every method communicating with the server and throwing an IOException
+ * can also throw a
+ * {@link org.apache.commons.net.MalformedServerReplyException}
+ * , which is a subclass
+ * of IOException. A MalformedServerReplyException will be thrown when
+ * the reply received from the server deviates enough from the protocol
+ * specification that it cannot be interpreted in a useful manner despite
+ * attempts to be as lenient as possible.
+ * <p>
+ * <p>
+ * @author Daniel F. Savarese
+ * @author Rory Winston
+ * @author Joseph Hindsley
+ * @see FTPClient
+ * @see FTPConnectionClosedException
+ * @see org.apache.commons.net.MalformedServerReplyException
+ * @version $Id: FTP.java 658520 2008-05-21 01:14:11Z sebb $
+ ***/
+
+public class FTP extends SocketClient
+{
+ /*** The default FTP data port (20). ***/
+ public static final int DEFAULT_DATA_PORT = 20;
+ /*** The default FTP control port (21). ***/
+ public static final int DEFAULT_PORT = 21;
+
+ /***
+ * A constant used to indicate the file(s) being transfered should
+ * be treated as ASCII. This is the default file type. All constants
+ * ending in <code>FILE_TYPE</code> are used to indicate file types.
+ ***/
+ public static final int ASCII_FILE_TYPE = 0;
+
+ /***
+ * A constant used to indicate the file(s) being transfered should
+ * be treated as EBCDIC. Note however that there are several different
+ * EBCDIC formats. All constants ending in <code>FILE_TYPE</code>
+ * are used to indicate file types.
+ ***/
+ public static final int EBCDIC_FILE_TYPE = 1;
+
+
+ /***
+ * A constant used to indicate the file(s) being transfered should
+ * be treated as a binary image, i.e., no translations should be
+ * performed. All constants ending in <code>FILE_TYPE</code> are used to
+ * indicate file types.
+ ***/
+ public static final int BINARY_FILE_TYPE = 2;
+
+ /***
+ * A constant used to indicate the file(s) being transfered should
+ * be treated as a local type. All constants ending in
+ * <code>FILE_TYPE</code> are used to indicate file types.
+ ***/
+ public static final int LOCAL_FILE_TYPE = 3;
+
+ /***
+ * A constant used for text files to indicate a non-print text format.
+ * This is the default format.
+ * All constants ending in <code>TEXT_FORMAT</code> are used to indicate
+ * text formatting for text transfers (both ASCII and EBCDIC).
+ ***/
+ public static final int NON_PRINT_TEXT_FORMAT = 4;
+
+ /***
+ * A constant used to indicate a text file contains format vertical format
+ * control characters.
+ * All constants ending in <code>TEXT_FORMAT</code> are used to indicate
+ * text formatting for text transfers (both ASCII and EBCDIC).
+ ***/
+ public static final int TELNET_TEXT_FORMAT = 5;
+
+ /***
+ * A constant used to indicate a text file contains ASA vertical format
+ * control characters.
+ * All constants ending in <code>TEXT_FORMAT</code> are used to indicate
+ * text formatting for text transfers (both ASCII and EBCDIC).
+ ***/
+ public static final int CARRIAGE_CONTROL_TEXT_FORMAT = 6;
+
+ /***
+ * A constant used to indicate a file is to be treated as a continuous
+ * sequence of bytes. This is the default structure. All constants ending
+ * in <code>_STRUCTURE</code> are used to indicate file structure for
+ * file transfers.
+ ***/
+ public static final int FILE_STRUCTURE = 7;
+
+ /***
+ * A constant used to indicate a file is to be treated as a sequence
+ * of records. All constants ending in <code>_STRUCTURE</code>
+ * are used to indicate file structure for file transfers.
+ ***/
+ public static final int RECORD_STRUCTURE = 8;
+
+ /***
+ * A constant used to indicate a file is to be treated as a set of
+ * independent indexed pages. All constants ending in
+ * <code>_STRUCTURE</code> are used to indicate file structure for file
+ * transfers.
+ ***/
+ public static final int PAGE_STRUCTURE = 9;
+
+ /***
+ * A constant used to indicate a file is to be transfered as a stream
+ * of bytes. This is the default transfer mode. All constants ending
+ * in <code>TRANSFER_MODE</code> are used to indicate file transfer
+ * modes.
+ ***/
+ public static final int STREAM_TRANSFER_MODE = 10;
+
+ /***
+ * A constant used to indicate a file is to be transfered as a series
+ * of blocks. All constants ending in <code>TRANSFER_MODE</code> are used
+ * to indicate file transfer modes.
+ ***/
+ public static final int BLOCK_TRANSFER_MODE = 11;
+
+ /***
+ * A constant used to indicate a file is to be transfered as FTP
+ * compressed data. All constants ending in <code>TRANSFER_MODE</code>
+ * are used to indicate file transfer modes.
+ ***/
+ public static final int COMPRESSED_TRANSFER_MODE = 12;
+
+ // We have to ensure that the protocol communication is in ASCII
+ // but we use ISO-8859-1 just in case 8-bit characters cross
+ // the wire.
+ /**
+ * The default character encoding used for communicating over an
+ * FTP control connection. The default encoding is an
+ * ASCII-compatible encoding. Some FTP servers expect other
+ * encodings. You can change the encoding used by an FTP instance
+ * with {@link #setControlEncoding setControlEncoding}.
+ */
+ public static final String DEFAULT_CONTROL_ENCODING = "ISO-8859-1";
+ private static final String __modes = "AEILNTCFRPSBC";
+
+ private StringBuilder __commandBuffer = new StringBuilder();
+
+ protected int _replyCode;
+ protected ArrayList<String> _replyLines;
+ protected boolean _newReplyString;
+ protected String _replyString;
+ protected String _controlEncoding;
+
+ /**
+ * This is used to signal whether a block of multiline responses beginning
+ * with xxx must be terminated by the same numeric code xxx
+ * See section 4.2 of RFX 959 for details.
+ */
+ protected boolean strictMultilineParsing = false;
+
+ /**
+ * Wraps SocketClient._input_ to facilitate the writing of text
+ * to the FTP control connection. Do not access the control
+ * connection via SocketClient._input_. This member starts
+ * with a null value, is initialized in {@link #_connectAction_},
+ * and set to null in {@link #disconnect}.
+ */
+ protected BufferedReader _controlInput_;
+
+ /**
+ * Wraps SocketClient._output_ to facilitate the reading of text
+ * from the FTP control connection. Do not access the control
+ * connection via SocketClient._output_. This member starts
+ * with a null value, is initialized in {@link #_connectAction_},
+ * and set to null in {@link #disconnect}.
+ */
+ protected BufferedWriter _controlOutput_;
+
+ /***
+ * A ProtocolCommandSupport object used to manage the registering of
+ * ProtocolCommandListeners and te firing of ProtocolCommandEvents.
+ ***/
+ protected ProtocolCommandSupport _commandSupport_;
+
+ /***
+ * The default FTP constructor. Sets the default port to
+ * <code>DEFAULT_PORT</code> and initializes internal data structures
+ * for saving FTP reply information.
+ ***/
+ public FTP()
+ {
+ super();
+ setDefaultPort(DEFAULT_PORT);
+ _replyLines = new ArrayList<String>();
+ _newReplyString = false;
+ _replyString = null;
+ _commandSupport_ = new ProtocolCommandSupport(this);
+ _controlEncoding = DEFAULT_CONTROL_ENCODING;
+ }
+
+ // The RFC-compliant multiline termination check
+ private boolean __strictCheck(String line, String code) {
+ return (!(line.startsWith(code) && line.charAt(3) == ' '));
+ }
+
+ // The strict check is too strong a condition because of non-conforming ftp
+ // servers like ftp.funet.fi which sent 226 as the last line of a
+ // 426 multi-line reply in response to ls /. We relax the condition to
+ // test that the line starts with a digit rather than starting with
+ // the code.
+ private boolean __lenientCheck(String line) {
+ return (!(line.length() >= 4 && line.charAt(3) != '-' &&
+ Character.isDigit(line.charAt(0))));
+ }
+
+ private void __getReply() throws IOException
+ {
+ int length;
+
+ _newReplyString = true;
+ _replyLines.clear();
+
+ String line = _controlInput_.readLine();
+
+ if (line == null)
+ throw new FTPConnectionClosedException(
+ "Connection closed without indication.");
+
+ // In case we run into an anomaly we don't want fatal index exceptions
+ // to be thrown.
+ length = line.length();
+ if (length < 3)
+ throw new MalformedServerReplyException(
+ "Truncated server reply: " + line);
+
+ String code = null;
+ try
+ {
+ code = line.substring(0, 3);
+ _replyCode = Integer.parseInt(code);
+ }
+ catch (NumberFormatException e)
+ {
+ throw new MalformedServerReplyException(
+ "Could not parse response code.\nServer Reply: " + line);
+ }
+
+ _replyLines.add(line);
+
+ // Get extra lines if message continues.
+ if (length > 3 && line.charAt(3) == '-')
+ {
+ do
+ {
+ line = _controlInput_.readLine();
+
+ if (line == null)
+ throw new FTPConnectionClosedException(
+ "Connection closed without indication.");
+
+ _replyLines.add(line);
+
+ // The length() check handles problems that could arise from readLine()
+ // returning too soon after encountering a naked CR or some other
+ // anomaly.
+ }
+ while ( isStrictMultilineParsing() ? __strictCheck(line, code) : __lenientCheck(line));
+ }
+
+ if (_commandSupport_.getListenerCount() > 0) {
+ _commandSupport_.fireReplyReceived(_replyCode, getReplyString());
+ }
+
+ if (_replyCode == FTPReply.SERVICE_NOT_AVAILABLE) {
+ throw new FTPConnectionClosedException("FTP response 421 received. Server closed connection.");
+ }
+ }
+
+ /**
+ * Initiates control connections and gets initial reply.
+ * Initializes {@link #_controlInput_} and {@link #_controlOutput_}.
+ */
+ @Override
+ protected void _connectAction_() throws IOException
+ {
+ super._connectAction_();
+ _controlInput_ =
+ new BufferedReader(new InputStreamReader(_socket_.getInputStream(),
+ getControlEncoding()));
+ _controlOutput_ =
+ new BufferedWriter(new OutputStreamWriter(_socket_.getOutputStream(),
+ getControlEncoding()));
+ __getReply();
+ // If we received code 120, we have to fetch completion reply.
+ if (FTPReply.isPositivePreliminary(_replyCode))
+ __getReply();
+ }
+
+
+ /**
+ * Sets the character encoding used by the FTP control connection.
+ * Some FTP servers require that commands be issued in a non-ASCII
+ * encoding like UTF-8 so that filenames with multi-byte character
+ * representations (e.g, Big 8) can be specified.
+ *
+ * @param encoding The new character encoding for the control connection.
+ */
+ public void setControlEncoding(String encoding) {
+ _controlEncoding = encoding;
+ }
+
+
+ /**
+ * @return The character encoding used to communicate over the
+ * control connection.
+ */
+ public String getControlEncoding() {
+ return _controlEncoding;
+ }
+
+
+ /***
+ * Adds a ProtocolCommandListener. Delegates this task to
+ * {@link #_commandSupport_ _commandSupport_ }.
+ * <p>
+ * @param listener The ProtocolCommandListener to add.
+ ***/
+ public void addProtocolCommandListener(ProtocolCommandListener listener)
+ {
+ _commandSupport_.addProtocolCommandListener(listener);
+ }
+
+ /***
+ * Removes a ProtocolCommandListener. Delegates this task to
+ * {@link #_commandSupport_ _commandSupport_ }.
+ * <p>
+ * @param listener The ProtocolCommandListener to remove.
+ ***/
+ public void removeProtocolCommandListener(ProtocolCommandListener listener)
+ {
+ _commandSupport_.removeProtocolCommandListener(listener);
+ }
+
+
+ /***
+ * Closes the control connection to the FTP server and sets to null
+ * some internal data so that the memory may be reclaimed by the
+ * garbage collector. The reply text and code information from the
+ * last command is voided so that the memory it used may be reclaimed.
+ * Also sets {@link #_controlInput_} and {@link #_controlOutput_} to null.
+ * <p>
+ * @exception IOException If an error occurs while disconnecting.
+ ***/
+ @Override
+ public void disconnect() throws IOException
+ {
+ super.disconnect();
+ _controlInput_ = null;
+ _controlOutput_ = null;
+ _newReplyString = false;
+ _replyString = null;
+ }
+
+
+ /***
+ * Sends an FTP command to the server, waits for a reply and returns the
+ * numerical response code. After invocation, for more detailed
+ * information, the actual reply text can be accessed by calling
+ * {@link #getReplyString getReplyString } or
+ * {@link #getReplyStrings getReplyStrings }.
+ * <p>
+ * @param command The text representation of the FTP command to send.
+ * @param args The arguments to the FTP command. If this parameter is
+ * set to null, then the command is sent with no argument.
+ * @return The integer value of the FTP reply code returned by the server
+ * in response to the command.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending the
+ * command or receiving the server reply.
+ ***/
+ public int sendCommand(String command, String args) throws IOException
+ {
+ String message;
+
+ __commandBuffer.setLength(0);
+ __commandBuffer.append(command);
+
+ if (args != null)
+ {
+ __commandBuffer.append(' ');
+ __commandBuffer.append(args);
+ }
+ __commandBuffer.append(SocketClient.NETASCII_EOL);
+
+ try{
+ _controlOutput_.write(message = __commandBuffer.toString());
+ _controlOutput_.flush();
+ }
+ catch (SocketException e)
+ {
+ if (!isConnected() || !socketIsConnected(_socket_))
+ {
+ throw new FTPConnectionClosedException("Connection unexpectedly closed.");
+ }
+ else
+ {
+ throw e;
+ }
+ }
+
+
+ if (_commandSupport_.getListenerCount() > 0)
+ _commandSupport_.fireCommandSent(command, message);
+
+ __getReply();
+ return _replyCode;
+ }
+
+ /**
+ * Checks if the socket is connected
+ *
+ * @param socket
+ * @return true if connected
+ */
+ private boolean socketIsConnected(Socket socket)
+ {
+ if (socket == null)
+ {
+ return false;
+ }
+
+ return socket.isConnected();
+
+ }
+
+ /***
+ * Sends an FTP command to the server, waits for a reply and returns the
+ * numerical response code. After invocation, for more detailed
+ * information, the actual reply text can be accessed by calling
+ * {@link #getReplyString getReplyString } or
+ * {@link #getReplyStrings getReplyStrings }.
+ * <p>
+ * @param command The FTPCommand constant corresponding to the FTP command
+ * to send.
+ * @param args The arguments to the FTP command. If this parameter is
+ * set to null, then the command is sent with no argument.
+ * @return The integer value of the FTP reply code returned by the server
+ * in response to the command.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending the
+ * command or receiving the server reply.
+ ***/
+ public int sendCommand(int command, String args) throws IOException
+ {
+ return sendCommand(FTPCommand._commands[command], args);
+ }
+
+
+ /***
+ * Sends an FTP command with no arguments to the server, waits for a
+ * reply and returns the numerical response code. After invocation, for
+ * more detailed information, the actual reply text can be accessed by
+ * calling {@link #getReplyString getReplyString } or
+ * {@link #getReplyStrings getReplyStrings }.
+ * <p>
+ * @param command The text representation of the FTP command to send.
+ * @return The integer value of the FTP reply code returned by the server
+ * in response to the command.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending the
+ * command or receiving the server reply.
+ ***/
+ public int sendCommand(String command) throws IOException
+ {
+ return sendCommand(command, null);
+ }
+
+
+ /***
+ * Sends an FTP command with no arguments to the server, waits for a
+ * reply and returns the numerical response code. After invocation, for
+ * more detailed information, the actual reply text can be accessed by
+ * calling {@link #getReplyString getReplyString } or
+ * {@link #getReplyStrings getReplyStrings }.
+ * <p>
+ * @param command The FTPCommand constant corresponding to the FTP command
+ * to send.
+ * @return The integer value of the FTP reply code returned by the server
+ * in response to the command.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending the
+ * command or receiving the server reply.
+ ***/
+ public int sendCommand(int command) throws IOException
+ {
+ return sendCommand(command, null);
+ }
+
+
+ /***
+ * Returns the integer value of the reply code of the last FTP reply.
+ * You will usually only use this method after you connect to the
+ * FTP server to check that the connection was successful since
+ * <code> connect </code> is of type void.
+ * <p>
+ * @return The integer value of the reply code of the last FTP reply.
+ ***/
+ public int getReplyCode()
+ {
+ return _replyCode;
+ }
+
+ /***
+ * Fetches a reply from the FTP server and returns the integer reply
+ * code. After calling this method, the actual reply text can be accessed
+ * from either calling {@link #getReplyString getReplyString } or
+ * {@link #getReplyStrings getReplyStrings }. Only use this
+ * method if you are implementing your own FTP client or if you need to
+ * fetch a secondary response from the FTP server.
+ * <p>
+ * @return The integer value of the reply code of the fetched FTP reply.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while receiving the
+ * server reply.
+ ***/
+ public int getReply() throws IOException
+ {
+ __getReply();
+ return _replyCode;
+ }
+
+
+ /***
+ * Returns the lines of text from the last FTP server response as an array
+ * of strings, one entry per line. The end of line markers of each are
+ * stripped from each line.
+ * <p>
+ * @return The lines of text from the last FTP response as an array.
+ ***/
+ public String[] getReplyStrings()
+ {
+ String[] lines;
+ lines = new String[_replyLines.size()];
+ _replyLines.addAll(Arrays.asList(lines));
+ return lines;
+ }
+
+ /***
+ * Returns the entire text of the last FTP server response exactly
+ * as it was received, including all end of line markers in NETASCII
+ * format.
+ * <p>
+ * @return The entire text from the last FTP response as a String.
+ ***/
+ public String getReplyString()
+ {
+ StringBuilder buffer;
+
+ if (!_newReplyString) {
+ return _replyString;
+ }
+
+ buffer = new StringBuilder(256);
+
+ for (String line : _replyLines) {
+ buffer.append(line);
+ buffer.append(SocketClient.NETASCII_EOL);
+ }
+
+ _newReplyString = false;
+
+ return (_replyString = buffer.toString());
+ }
+
+
+ /***
+ * A convenience method to send the FTP USER command to the server,
+ * receive the reply, and return the reply code.
+ * <p>
+ * @param username The username to login under.
+ * @return The reply code received from the server.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending the
+ * command or receiving the server reply.
+ ***/
+ public int user(String username) throws IOException
+ {
+ return sendCommand(FTPCommand.USER, username);
+ }
+
+ /**
+ * A convenience method to send the FTP PASS command to the server,
+ * receive the reply, and return the reply code.
+ * @param password The plain text password of the username being logged into.
+ * @return The reply code received from the server.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending the
+ * command or receiving the server reply.
+ */
+ public int pass(String password) throws IOException
+ {
+ return sendCommand(FTPCommand.PASS, password);
+ }
+
+ /***
+ * A convenience method to send the FTP ACCT command to the server,
+ * receive the reply, and return the reply code.
+ * <p>
+ * @param account The account name to access.
+ * @return The reply code received from the server.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending the
+ * command or receiving the server reply.
+ ***/
+ public int acct(String account) throws IOException
+ {
+ return sendCommand(FTPCommand.ACCT, account);
+ }
+
+
+ /***
+ * A convenience method to send the FTP ABOR command to the server,
+ * receive the reply, and return the reply code.
+ * <p>
+ * @return The reply code received from the server.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending the
+ * command or receiving the server reply.
+ ***/
+ public int abor() throws IOException
+ {
+ return sendCommand(FTPCommand.ABOR);
+ }
+
+ /***
+ * A convenience method to send the FTP CWD command to the server,
+ * receive the reply, and return the reply code.
+ * <p>
+ * @param directory The new working directory.
+ * @return The reply code received from the server.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending the
+ * command or receiving the server reply.
+ ***/
+ public int cwd(String directory) throws IOException
+ {
+ return sendCommand(FTPCommand.CWD, directory);
+ }
+
+ /***
+ * A convenience method to send the FTP CDUP command to the server,
+ * receive the reply, and return the reply code.
+ * <p>
+ * @return The reply code received from the server.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending the
+ * command or receiving the server reply.
+ ***/
+ public int cdup() throws IOException
+ {
+ return sendCommand(FTPCommand.CDUP);
+ }
+
+ /***
+ * A convenience method to send the FTP QUIT command to the server,
+ * receive the reply, and return the reply code.
+ * <p>
+ * @return The reply code received from the server.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending the
+ * command or receiving the server reply.
+ ***/
+ public int quit() throws IOException
+ {
+ return sendCommand(FTPCommand.QUIT);
+ }
+
+ /***
+ * A convenience method to send the FTP REIN command to the server,
+ * receive the reply, and return the reply code.
+ * <p>
+ * @return The reply code received from the server.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending the
+ * command or receiving the server reply.
+ ***/
+ public int rein() throws IOException
+ {
+ return sendCommand(FTPCommand.REIN);
+ }
+
+ /***
+ * A convenience method to send the FTP SMNT command to the server,
+ * receive the reply, and return the reply code.
+ * <p>
+ * @param dir The directory name.
+ * @return The reply code received from the server.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending the
+ * command or receiving the server reply.
+ ***/
+ public int smnt(String dir) throws IOException
+ {
+ return sendCommand(FTPCommand.SMNT, dir);
+ }
+
+ /***
+ * A convenience method to send the FTP PORT command to the server,
+ * receive the reply, and return the reply code.
+ * <p>
+ * @param host The host owning the port.
+ * @param port The new port.
+ * @return The reply code received from the server.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending the
+ * command or receiving the server reply.
+ ***/
+ public int port(InetAddress host, int port) throws IOException
+ {
+ int num;
+ StringBuffer info = new StringBuffer(24);
+
+ info.append(host.getHostAddress().replace('.', ','));
+ num = port >>> 8;
+ info.append(',');
+ info.append(num);
+ info.append(',');
+ num = port & 0xff;
+ info.append(num);
+
+ return sendCommand(FTPCommand.PORT, info.toString());
+ }
+
+ /***
+ * A convenience method to send the FTP PASV command to the server,
+ * receive the reply, and return the reply code. Remember, it's up
+ * to you to interpret the reply string containing the host/port
+ * information.
+ * <p>
+ * @return The reply code received from the server.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending the
+ * command or receiving the server reply.
+ ***/
+ public int pasv() throws IOException
+ {
+ return sendCommand(FTPCommand.PASV);
+ }
+
+ /**
+ * A convenience method to send the FTP TYPE command for text files
+ * to the server, receive the reply, and return the reply code.
+ * @param fileType The type of the file (one of the <code>FILE_TYPE</code>
+ * constants).
+ * @param formatOrByteSize The format of the file (one of the
+ * <code>_FORMAT</code> constants. In the case of
+ * <code>LOCAL_FILE_TYPE</code>, the byte size.
+ * @return The reply code received from the server.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending the
+ * command or receiving the server reply.
+ */
+ public int type(int fileType, int formatOrByteSize) throws IOException
+ {
+ StringBuffer arg = new StringBuffer();
+
+ arg.append(__modes.charAt(fileType));
+ arg.append(' ');
+ if (fileType == LOCAL_FILE_TYPE)
+ arg.append(formatOrByteSize);
+ else
+ arg.append(__modes.charAt(formatOrByteSize));
+
+ return sendCommand(FTPCommand.TYPE, arg.toString());
+ }
+
+
+ /**
+ * A convenience method to send the FTP TYPE command to the server,
+ * receive the reply, and return the reply code.
+ * <p>
+ * @param fileType The type of the file (one of the <code>FILE_TYPE</code>
+ * constants).
+ * @return The reply code received from the server.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending the
+ * command or receiving the server reply.
+ */
+ public int type(int fileType) throws IOException
+ {
+ return sendCommand(FTPCommand.TYPE,
+ __modes.substring(fileType, fileType + 1));
+ }
+
+ /***
+ * A convenience method to send the FTP STRU command to the server,
+ * receive the reply, and return the reply code.
+ * <p>
+ * @param structure The structure of the file (one of the
+ * <code>_STRUCTURE</code> constants).
+ * @return The reply code received from the server.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending the
+ * command or receiving the server reply.
+ ***/
+ public int stru(int structure) throws IOException
+ {
+ return sendCommand(FTPCommand.STRU,
+ __modes.substring(structure, structure + 1));
+ }
+
+ /***
+ * A convenience method to send the FTP MODE command to the server,
+ * receive the reply, and return the reply code.
+ * <p>
+ * @param mode The transfer mode to use (one of the
+ * <code>TRANSFER_MODE</code> constants).
+ * @return The reply code received from the server.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending the
+ * command or receiving the server reply.
+ ***/
+ public int mode(int mode) throws IOException
+ {
+ return sendCommand(FTPCommand.MODE,
+ __modes.substring(mode, mode + 1));
+ }
+
+ /***
+ * A convenience method to send the FTP RETR command to the server,
+ * receive the reply, and return the reply code. Remember, it is up
+ * to you to manage the data connection. If you don't need this low
+ * level of access, use {@link org.apache.commons.net.ftp.FTPClient}
+ * , which will handle all low level details for you.
+ * <p>
+ * @param pathname The pathname of the file to retrieve.
+ * @return The reply code received from the server.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending the
+ * command or receiving the server reply.
+ ***/
+ public int retr(String pathname) throws IOException
+ {
+ return sendCommand(FTPCommand.RETR, pathname);
+ }
+
+ /***
+ * A convenience method to send the FTP STOR command to the server,
+ * receive the reply, and return the reply code. Remember, it is up
+ * to you to manage the data connection. If you don't need this low
+ * level of access, use {@link org.apache.commons.net.ftp.FTPClient}
+ * , which will handle all low level details for you.
+ * <p>
+ * @param pathname The pathname to use for the file when stored at
+ * the remote end of the transfer.
+ * @return The reply code received from the server.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending the
+ * command or receiving the server reply.
+ ***/
+ public int stor(String pathname) throws IOException
+ {
+ return sendCommand(FTPCommand.STOR, pathname);
+ }
+
+ /***
+ * A convenience method to send the FTP STOU command to the server,
+ * receive the reply, and return the reply code. Remember, it is up
+ * to you to manage the data connection. If you don't need this low
+ * level of access, use {@link org.apache.commons.net.ftp.FTPClient}
+ * , which will handle all low level details for you.
+ * <p>
+ * @return The reply code received from the server.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending the
+ * command or receiving the server reply.
+ ***/
+ public int stou() throws IOException
+ {
+ return sendCommand(FTPCommand.STOU);
+ }
+
+ /***
+ * A convenience method to send the FTP STOU command to the server,
+ * receive the reply, and return the reply code. Remember, it is up
+ * to you to manage the data connection. If you don't need this low
+ * level of access, use {@link org.apache.commons.net.ftp.FTPClient}
+ * , which will handle all low level details for you.
+ * @param pathname The base pathname to use for the file when stored at
+ * the remote end of the transfer. Some FTP servers
+ * require this.
+ * @return The reply code received from the server.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending the
+ * command or receiving the server reply.
+ */
+ public int stou(String pathname) throws IOException
+ {
+ return sendCommand(FTPCommand.STOU, pathname);
+ }
+
+ /***
+ * A convenience method to send the FTP APPE command to the server,
+ * receive the reply, and return the reply code. Remember, it is up
+ * to you to manage the data connection. If you don't need this low
+ * level of access, use {@link org.apache.commons.net.ftp.FTPClient}
+ * , which will handle all low level details for you.
+ * <p>
+ * @param pathname The pathname to use for the file when stored at
+ * the remote end of the transfer.
+ * @return The reply code received from the server.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending the
+ * command or receiving the server reply.
+ ***/
+ public int appe(String pathname) throws IOException
+ {
+ return sendCommand(FTPCommand.APPE, pathname);
+ }
+
+ /***
+ * A convenience method to send the FTP ALLO command to the server,
+ * receive the reply, and return the reply code.
+ * <p>
+ * @param bytes The number of bytes to allocate.
+ * @return The reply code received from the server.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending the
+ * command or receiving the server reply.
+ ***/
+ public int allo(int bytes) throws IOException
+ {
+ return sendCommand(FTPCommand.ALLO, Integer.toString(bytes));
+ }
+
+ /***
+ * A convenience method to send the FTP ALLO command to the server,
+ * receive the reply, and return the reply code.
+ * <p>
+ * @param bytes The number of bytes to allocate.
+ * @param recordSize The size of a record.
+ * @return The reply code received from the server.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending the
+ * command or receiving the server reply.
+ ***/
+ public int allo(int bytes, int recordSize) throws IOException
+ {
+ return sendCommand(FTPCommand.ALLO, Integer.toString(bytes) + " R " +
+ Integer.toString(recordSize));
+ }
+
+ /***
+ * A convenience method to send the FTP REST command to the server,
+ * receive the reply, and return the reply code.
+ * <p>
+ * @param marker The marker at which to restart a transfer.
+ * @return The reply code received from the server.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending the
+ * command or receiving the server reply.
+ ***/
+ public int rest(String marker) throws IOException
+ {
+ return sendCommand(FTPCommand.REST, marker);
+ }
+
+
+ /**
+ * @since 2.0
+ **/
+ public int mdtm(String file) throws IOException
+ {
+ return sendCommand(FTPCommand.MDTM, file);
+ }
+
+ /***
+ * A convenience method to send the FTP RNFR command to the server,
+ * receive the reply, and return the reply code.
+ * <p>
+ * @param pathname The pathname to rename from.
+ * @return The reply code received from the server.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending the
+ * command or receiving the server reply.
+ ***/
+ public int rnfr(String pathname) throws IOException
+ {
+ return sendCommand(FTPCommand.RNFR, pathname);
+ }
+
+ /***
+ * A convenience method to send the FTP RNTO command to the server,
+ * receive the reply, and return the reply code.
+ * <p>
+ * @param pathname The pathname to rename to
+ * @return The reply code received from the server.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending the
+ * command or receiving the server reply.
+ ***/
+ public int rnto(String pathname) throws IOException
+ {
+ return sendCommand(FTPCommand.RNTO, pathname);
+ }
+
+ /***
+ * A convenience method to send the FTP DELE command to the server,
+ * receive the reply, and return the reply code.
+ * <p>
+ * @param pathname The pathname to delete.
+ * @return The reply code received from the server.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending the
+ * command or receiving the server reply.
+ ***/
+ public int dele(String pathname) throws IOException
+ {
+ return sendCommand(FTPCommand.DELE, pathname);
+ }
+
+ /***
+ * A convenience method to send the FTP RMD command to the server,
+ * receive the reply, and return the reply code.
+ * <p>
+ * @param pathname The pathname of the directory to remove.
+ * @return The reply code received from the server.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending the
+ * command or receiving the server reply.
+ ***/
+ public int rmd(String pathname) throws IOException
+ {
+ return sendCommand(FTPCommand.RMD, pathname);
+ }
+
+ /***
+ * A convenience method to send the FTP MKD command to the server,
+ * receive the reply, and return the reply code.
+ * <p>
+ * @param pathname The pathname of the new directory to create.
+ * @return The reply code received from the server.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending the
+ * command or receiving the server reply.
+ ***/
+ public int mkd(String pathname) throws IOException
+ {
+ return sendCommand(FTPCommand.MKD, pathname);
+ }
+
+ /***
+ * A convenience method to send the FTP PWD command to the server,
+ * receive the reply, and return the reply code.
+ * <p>
+ * @return The reply code received from the server.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending the
+ * command or receiving the server reply.
+ ***/
+ public int pwd() throws IOException
+ {
+ return sendCommand(FTPCommand.PWD);
+ }
+
+ /***
+ * A convenience method to send the FTP LIST command to the server,
+ * receive the reply, and return the reply code. Remember, it is up
+ * to you to manage the data connection. If you don't need this low
+ * level of access, use {@link org.apache.commons.net.ftp.FTPClient}
+ * , which will handle all low level details for you.
+ * <p>
+ * @return The reply code received from the server.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending the
+ * command or receiving the server reply.
+ ***/
+ public int list() throws IOException
+ {
+ return sendCommand(FTPCommand.LIST);
+ }
+
+ /***
+ * A convenience method to send the FTP LIST command to the server,
+ * receive the reply, and return the reply code. Remember, it is up
+ * to you to manage the data connection. If you don't need this low
+ * level of access, use {@link org.apache.commons.net.ftp.FTPClient}
+ * , which will handle all low level details for you.
+ * <p>
+ * @param pathname The pathname to list.
+ * @return The reply code received from the server.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending the
+ * command or receiving the server reply.
+ ***/
+ public int list(String pathname) throws IOException
+ {
+ return sendCommand(FTPCommand.LIST, pathname);
+ }
+
+ /***
+ * A convenience method to send the FTP NLST command to the server,
+ * receive the reply, and return the reply code. Remember, it is up
+ * to you to manage the data connection. If you don't need this low
+ * level of access, use {@link org.apache.commons.net.ftp.FTPClient}
+ * , which will handle all low level details for you.
+ * <p>
+ * @return The reply code received from the server.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending the
+ * command or receiving the server reply.
+ ***/
+ public int nlst() throws IOException
+ {
+ return sendCommand(FTPCommand.NLST);
+ }
+
+ /***
+ * A convenience method to send the FTP NLST command to the server,
+ * receive the reply, and return the reply code. Remember, it is up
+ * to you to manage the data connection. If you don't need this low
+ * level of access, use {@link org.apache.commons.net.ftp.FTPClient}
+ * , which will handle all low level details for you.
+ * <p>
+ * @param pathname The pathname to list.
+ * @return The reply code received from the server.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending the
+ * command or receiving the server reply.
+ ***/
+ public int nlst(String pathname) throws IOException
+ {
+ return sendCommand(FTPCommand.NLST, pathname);
+ }
+
+ /***
+ * A convenience method to send the FTP SITE command to the server,
+ * receive the reply, and return the reply code.
+ * <p>
+ * @param parameters The site parameters to send.
+ * @return The reply code received from the server.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending the
+ * command or receiving the server reply.
+ ***/
+ public int site(String parameters) throws IOException
+ {
+ return sendCommand(FTPCommand.SITE, parameters);
+ }
+
+ /***
+ * A convenience method to send the FTP SYST command to the server,
+ * receive the reply, and return the reply code.
+ * <p>
+ * @return The reply code received from the server.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending the
+ * command or receiving the server reply.
+ ***/
+ public int syst() throws IOException
+ {
+ return sendCommand(FTPCommand.SYST);
+ }
+
+ /***
+ * A convenience method to send the FTP STAT command to the server,
+ * receive the reply, and return the reply code.
+ * <p>
+ * @return The reply code received from the server.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending the
+ * command or receiving the server reply.
+ ***/
+ public int stat() throws IOException
+ {
+ return sendCommand(FTPCommand.STAT);
+ }
+
+ /***
+ * A convenience method to send the FTP STAT command to the server,
+ * receive the reply, and return the reply code.
+ * <p>
+ * @param pathname A pathname to list.
+ * @return The reply code received from the server.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending the
+ * command or receiving the server reply.
+ ***/
+ public int stat(String pathname) throws IOException
+ {
+ return sendCommand(FTPCommand.STAT, pathname);
+ }
+
+ /***
+ * A convenience method to send the FTP HELP command to the server,
+ * receive the reply, and return the reply code.
+ * <p>
+ * @return The reply code received from the server.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending the
+ * command or receiving the server reply.
+ ***/
+ public int help() throws IOException
+ {
+ return sendCommand(FTPCommand.HELP);
+ }
+
+ /***
+ * A convenience method to send the FTP HELP command to the server,
+ * receive the reply, and return the reply code.
+ * <p>
+ * @param command The command name on which to request help.
+ * @return The reply code received from the server.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending the
+ * command or receiving the server reply.
+ ***/
+ public int help(String command) throws IOException
+ {
+ return sendCommand(FTPCommand.HELP, command);
+ }
+
+ /***
+ * A convenience method to send the FTP NOOP command to the server,
+ * receive the reply, and return the reply code.
+ * <p>
+ * @return The reply code received from the server.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending the
+ * command or receiving the server reply.
+ ***/
+ public int noop() throws IOException
+ {
+ return sendCommand(FTPCommand.NOOP);
+ }
+
+ /**
+ * Return whether strict multiline parsing is enabled, as per RFX 959, section 4.2.
+ * @return True if strict, false if lenient
+ * @since 2.0
+ */
+ public boolean isStrictMultilineParsing() {
+ return strictMultilineParsing;
+ }
+
+ /**
+ * Set strict multiline parsing.
+ * @param strictMultilineParsing
+ * @since 2.0
+ */
+ public void setStrictMultilineParsing(boolean strictMultilineParsing) {
+ this.strictMultilineParsing = strictMultilineParsing;
+ }
+}
+
+/* Emacs configuration
+ * Local variables: **
+ * mode: java **
+ * c-basic-offset: 4 **
+ * indent-tabs-mode: nil **
+ * End: **
+ */
diff --git a/app/src/main/java/org/apache/commons/net/ftp/FTPClient.java b/app/src/main/java/org/apache/commons/net/ftp/FTPClient.java
new file mode 100644
index 0000000..8e547d7
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/ftp/FTPClient.java
@@ -0,0 +1,2448 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.ftp;
+
+import org.apache.commons.net.MalformedServerReplyException;
+import org.apache.commons.net.ftp.parser.DefaultFTPFileEntryParserFactory;
+import org.apache.commons.net.ftp.parser.FTPFileEntryParserFactory;
+import org.apache.commons.net.ftp.parser.ParserInitializationException;
+import org.apache.commons.net.io.CopyStreamEvent;
+import org.apache.commons.net.io.CopyStreamException;
+import org.apache.commons.net.io.FromNetASCIIInputStream;
+import org.apache.commons.net.io.ToNetASCIIOutputStream;
+import org.apache.commons.net.io.Util;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.ArrayList;
+
+/***
+ * FTPClient encapsulates all the functionality necessary to store and
+ * retrieve files from an FTP server. This class takes care of all
+ * low level details of interacting with an FTP server and provides
+ * a convenient higher level interface. As with all classes derived
+ * from {@link org.apache.commons.net.SocketClient},
+ * you must first connect to the server with
+ * {@link org.apache.commons.net.SocketClient#connect connect }
+ * before doing anything, and finally
+ * {@link org.apache.commons.net.SocketClient#disconnect disconnect }
+ * after you're completely finished interacting with the server.
+ * Then you need to check the FTP reply code to see if the connection
+ * was successful. For example:
+ * <pre>
+ * boolean error = false;
+ * try {
+ * int reply;
+ * ftp.connect("ftp.foobar.com");
+ * System.out.println("Connected to " + server + ".");
+ * System.out.print(ftp.getReplyString());
+ *
+ * // After connection attempt, you should check the reply code to verify
+ * // success.
+ * reply = ftp.getReplyCode();
+ *
+ * if(!FTPReply.isPositiveCompletion(reply)) {
+ * ftp.disconnect();
+ * System.err.println("FTP server refused connection.");
+ * System.exit(1);
+ * }
+ * ... // transfer files
+ * ftp.logout();
+ * } catch(IOException e) {
+ * error = true;
+ * e.printStackTrace();
+ * } finally {
+ * if(ftp.isConnected()) {
+ * try {
+ * ftp.disconnect();
+ * } catch(IOException ioe) {
+ * // do nothing
+ * }
+ * }
+ * System.exit(error ? 1 : 0);
+ * }
+ * </pre>
+ * <p>
+ * Immediately after connecting is the only real time you need to check the
+ * reply code (because connect is of type void). The convention for all the
+ * FTP command methods in FTPClient is such that they either return a
+ * boolean value or some other value.
+ * The boolean methods return true on a successful completion reply from
+ * the FTP server and false on a reply resulting in an error condition or
+ * failure. The methods returning a value other than boolean return a value
+ * containing the higher level data produced by the FTP command, or null if a
+ * reply resulted in an error condition or failure. If you want to access
+ * the exact FTP reply code causing a success or failure, you must call
+ * {@link org.apache.commons.net.ftp.FTP#getReplyCode getReplyCode } after
+ * a success or failure.
+ * <p>
+ * The default settings for FTPClient are for it to use
+ * <code> FTP.ASCII_FILE_TYPE </code>,
+ * <code> FTP.NON_PRINT_TEXT_FORMAT </code>,
+ * <code> FTP.STREAM_TRANSFER_MODE </code>, and
+ * <code> FTP.FILE_STRUCTURE </code>. The only file types directly supported
+ * are <code> FTP.ASCII_FILE_TYPE </code> and
+ * <code> FTP.BINARY_FILE_TYPE </code>. Because there are at least 4
+ * different EBCDIC encodings, we have opted not to provide direct support
+ * for EBCDIC. To transfer EBCDIC and other unsupported file types you
+ * must create your own filter InputStreams and OutputStreams and wrap
+ * them around the streams returned or required by the FTPClient methods.
+ * FTPClient uses the {@link ToNetASCIIOutputStream NetASCII}
+ * filter streams to provide transparent handling of ASCII files. We will
+ * consider incorporating EBCDIC support if there is enough demand.
+ * <p>
+ * <code> FTP.NON_PRINT_TEXT_FORMAT </code>,
+ * <code> FTP.STREAM_TRANSFER_MODE </code>, and
+ * <code> FTP.FILE_STRUCTURE </code> are the only supported formats,
+ * transfer modes, and file structures.
+ * <p>
+ * Because the handling of sockets on different platforms can differ
+ * significantly, the FTPClient automatically issues a new PORT command
+ * prior to every transfer requiring that the server connect to the client's
+ * data port. This ensures identical problem-free behavior on Windows, Unix,
+ * and Macintosh platforms. Additionally, it relieves programmers from
+ * having to issue the PORT command themselves and dealing with platform
+ * dependent issues.
+ * <p>
+ * Additionally, for security purposes, all data connections to the
+ * client are verified to ensure that they originated from the intended
+ * party (host and port). If a data connection is initiated by an unexpected
+ * party, the command will close the socket and throw an IOException. You
+ * may disable this behavior with
+ * {@link #setRemoteVerificationEnabled setRemoteVerificationEnabled()}.
+ * <p>
+ * You should keep in mind that the FTP server may choose to prematurely
+ * close a connection if the client has been idle for longer than a
+ * given time period (usually 900 seconds). The FTPClient class will detect a
+ * premature FTP server connection closing when it receives a
+ * {@link org.apache.commons.net.ftp.FTPReply#SERVICE_NOT_AVAILABLE FTPReply.SERVICE_NOT_AVAILABLE }
+ * response to a command.
+ * When that occurs, the FTP class method encountering that reply will throw
+ * an {@link org.apache.commons.net.ftp.FTPConnectionClosedException}
+ * .
+ * <code>FTPConnectionClosedException</code>
+ * is a subclass of <code> IOException </code> and therefore need not be
+ * caught separately, but if you are going to catch it separately, its
+ * catch block must appear before the more general <code> IOException </code>
+ * catch block. When you encounter an
+ * {@link org.apache.commons.net.ftp.FTPConnectionClosedException}
+ * , you must disconnect the connection with
+ * {@link #disconnect disconnect() } to properly clean up the
+ * system resources used by FTPClient. Before disconnecting, you may check the
+ * last reply code and text with
+ * {@link org.apache.commons.net.ftp.FTP#getReplyCode getReplyCode },
+ * {@link org.apache.commons.net.ftp.FTP#getReplyString getReplyString },
+ * and
+ * {@link org.apache.commons.net.ftp.FTP#getReplyStrings getReplyStrings}.
+ * You may avoid server disconnections while the client is idle by
+ * periodicaly sending NOOP commands to the server.
+ * <p>
+ * Rather than list it separately for each method, we mention here that
+ * every method communicating with the server and throwing an IOException
+ * can also throw a
+ * {@link org.apache.commons.net.MalformedServerReplyException}
+ * , which is a subclass
+ * of IOException. A MalformedServerReplyException will be thrown when
+ * the reply received from the server deviates enough from the protocol
+ * specification that it cannot be interpreted in a useful manner despite
+ * attempts to be as lenient as possible.
+ * <p>
+ * Listing API Examples
+ * Both paged and unpaged examples of directory listings are available,
+ * as follows:
+ * <p>
+ * Unpaged (whole list) access, using a parser accessible by auto-detect:
+ * <pre>
+ * FTPClient f=FTPClient();
+ * f.connect(server);
+ * f.login(username, password);
+ * FTPFile[] files = listFiles(directory);
+ * </pre>
+ * <p>
+ * Paged access, using a parser not accessible by auto-detect. The class
+ * defined in the first parameter of initateListParsing should be derived
+ * from org.apache.commons.net.FTPFileEntryParser:
+ * <pre>
+ * FTPClient f=FTPClient();
+ * f.connect(server);
+ * f.login(username, password);
+ * FTPListParseEngine engine =
+ * f.initiateListParsing("com.whatever.YourOwnParser", directory);
+ *
+ * while (engine.hasNext()) {
+ * FTPFile[] files = engine.getNext(25); // "page size" you want
+ * //do whatever you want with these files, display them, etc.
+ * //expensive FTPFile objects not created until needed.
+ * }
+ * </pre>
+ * <p>
+ * Paged access, using a parser accessible by auto-detect:
+ * <pre>
+ * FTPClient f=FTPClient();
+ * f.connect(server);
+ * f.login(username, password);
+ * FTPListParseEngine engine = f.initiateListParsing(directory);
+ *
+ * while (engine.hasNext()) {
+ * FTPFile[] files = engine.getNext(25); // "page size" you want
+ * //do whatever you want with these files, display them, etc.
+ * //expensive FTPFile objects not created until needed.
+ * }
+ * </pre>
+ * <p>
+ * For examples of using FTPClient on servers whose directory listings
+ * <ul>
+ * <li>use languages other than English</li>
+ * <li>use date formats other than the American English "standard" <code>MM d yyyy</code></li>
+ * <li>are in different timezones and you need accurate timestamps for dependency checking
+ * as in Ant</li>
+ * </ul>see {@link FTPClientConfig FTPClientConfig}.
+ * <p>
+ * @author Daniel F. Savarese
+ * @author Rory Winston
+ * @see FTP
+ * @see FTPConnectionClosedException
+ * @see FTPFileEntryParser
+ * @see FTPFileEntryParserFactory
+ * @see DefaultFTPFileEntryParserFactory
+ * @see FTPClientConfig
+ *
+ * @see org.apache.commons.net.MalformedServerReplyException
+ **/
+public class FTPClient extends FTP
+implements Configurable
+{
+ /***
+ * A constant indicating the FTP session is expecting all transfers
+ * to occur between the client (local) and server and that the server
+ * should connect to the client's data port to initiate a data transfer.
+ * This is the default data connection mode when and FTPClient instance
+ * is created.
+ ***/
+ public static final int ACTIVE_LOCAL_DATA_CONNECTION_MODE = 0;
+ /***
+ * A constant indicating the FTP session is expecting all transfers
+ * to occur between two remote servers and that the server
+ * the client is connected to should connect to the other server's
+ * data port to initiate a data transfer.
+ ***/
+ public static final int ACTIVE_REMOTE_DATA_CONNECTION_MODE = 1;
+ /***
+ * A constant indicating the FTP session is expecting all transfers
+ * to occur between the client (local) and server and that the server
+ * is in passive mode, requiring the client to connect to the
+ * server's data port to initiate a transfer.
+ ***/
+ public static final int PASSIVE_LOCAL_DATA_CONNECTION_MODE = 2;
+ /***
+ * A constant indicating the FTP session is expecting all transfers
+ * to occur between two remote servers and that the server
+ * the client is connected to is in passive mode, requiring the other
+ * server to connect to the first server's data port to initiate a data
+ * transfer.
+ ***/
+ public static final int PASSIVE_REMOTE_DATA_CONNECTION_MODE = 3;
+
+ private int __dataConnectionMode, __dataTimeout;
+ private int __passivePort;
+ private String __passiveHost;
+ private int __fileType, __fileFormat, __fileStructure, __fileTransferMode;
+ private boolean __remoteVerificationEnabled;
+ private long __restartOffset;
+ private FTPFileEntryParserFactory __parserFactory;
+ private int __bufferSize;
+ private boolean __listHiddenFiles;
+
+ // __systemName is a cached value that should not be referenced directly
+ // except when assigned in getSystemName and __initDefaults.
+ private String __systemName;
+
+ // __entryParser is a cached value that should not be referenced directly
+ // except when assigned in listFiles(String, String) and __initDefaults.
+ private FTPFileEntryParser __entryParser;
+
+ private FTPClientConfig __configuration;
+
+ /** Pattern for PASV mode responses */
+ private static String __parms = "\\d{1,3},\\d{1,3},\\d{1,3},\\d{1,3},\\d{1,3},\\d{1,3}";
+ private static java.util.regex.Pattern __parms_pat;
+ static {
+ __parms_pat = java.util.regex.Pattern.compile(__parms);
+ }
+
+ /***
+ * Default FTPClient constructor. Creates a new FTPClient instance
+ * with the data connection mode set to
+ * <code> ACTIVE_LOCAL_DATA_CONNECTION_MODE </code>, the file type
+ * set to <code> FTP.ASCII_FILE_TYPE </code>, the
+ * file format set to <code> FTP.NON_PRINT_TEXT_FORMAT </code>,
+ * the file structure set to <code> FTP.FILE_STRUCTURE </code>, and
+ * the transfer mode set to <code> FTP.STREAM_TRANSFER_MODE </code>.
+ ***/
+ public FTPClient()
+ {
+ __initDefaults();
+ __dataTimeout = -1;
+ __remoteVerificationEnabled = true;
+ __parserFactory = new DefaultFTPFileEntryParserFactory();
+ __configuration = null;
+ __listHiddenFiles = false;
+ }
+
+
+ private void __initDefaults()
+ {
+ __dataConnectionMode = ACTIVE_LOCAL_DATA_CONNECTION_MODE;
+ __passiveHost = null;
+ __passivePort = -1;
+ __fileType = FTP.ASCII_FILE_TYPE;
+ __fileStructure = FTP.FILE_STRUCTURE;
+ __fileFormat = FTP.NON_PRINT_TEXT_FORMAT;
+ __fileTransferMode = FTP.STREAM_TRANSFER_MODE;
+ __restartOffset = 0;
+ __systemName = null;
+ __entryParser = null;
+ __bufferSize = Util.DEFAULT_COPY_BUFFER_SIZE;
+ }
+
+ private String __parsePathname(String reply)
+ {
+ int begin, end;
+
+ begin = reply.indexOf('"') + 1;
+ end = reply.indexOf('"', begin);
+
+ return reply.substring(begin, end);
+ }
+
+
+ private void __parsePassiveModeReply(String reply)
+ throws MalformedServerReplyException
+ {
+ java.util.regex.Matcher m = __parms_pat.matcher(reply);
+ if (!m.find()) {
+ throw new MalformedServerReplyException(
+ "Could not parse passive host information.\nServer Reply: " + reply);
+ }
+ reply = m.group();
+ String parts[] = m.group().split(",");
+
+ __passiveHost = parts[0] + '.' + parts[1] + '.' + parts[2] + '.' + parts[3];
+
+ try
+ {
+ int oct1 = Integer.parseInt(parts[4]);
+ int oct2 = Integer.parseInt(parts[5]);
+ __passivePort = (oct1 << 8) | oct2;
+ }
+ catch (NumberFormatException e)
+ {
+ throw new MalformedServerReplyException(
+ "Could not parse passive host information.\nServer Reply: " + reply);
+ }
+
+ }
+
+ private boolean __storeFile(int command, String remote, InputStream local)
+ throws IOException
+ {
+ OutputStream output;
+ Socket socket;
+
+ if ((socket = _openDataConnection_(command, remote)) == null)
+ return false;
+
+ output = new BufferedOutputStream(socket.getOutputStream(),
+ getBufferSize()
+ );
+ if (__fileType == ASCII_FILE_TYPE)
+ output = new ToNetASCIIOutputStream(output);
+ // Treat everything else as binary for now
+ try
+ {
+ Util.copyStream(local, output, getBufferSize(),
+ CopyStreamEvent.UNKNOWN_STREAM_SIZE, null,
+ false);
+ }
+ catch (IOException e)
+ {
+ try
+ {
+ socket.close();
+ }
+ catch (IOException f)
+ {}
+ throw e;
+ }
+ output.close();
+ socket.close();
+ return completePendingCommand();
+ }
+
+ private OutputStream __storeFileStream(int command, String remote)
+ throws IOException
+ {
+ OutputStream output;
+ Socket socket;
+
+ if ((socket = _openDataConnection_(command, remote)) == null)
+ return null;
+
+ output = socket.getOutputStream();
+ if (__fileType == ASCII_FILE_TYPE) {
+ // We buffer ascii transfers because the buffering has to
+ // be interposed between ToNetASCIIOutputSream and the underlying
+ // socket output stream. We don't buffer binary transfers
+ // because we don't want to impose a buffering policy on the
+ // programmer if possible. Programmers can decide on their
+ // own if they want to wrap the SocketOutputStream we return
+ // for file types other than ASCII.
+ output = new BufferedOutputStream(output,
+ getBufferSize());
+ output = new ToNetASCIIOutputStream(output);
+
+ }
+ return new org.apache.commons.net.io.SocketOutputStream(socket, output);
+ }
+
+
+ /**
+ * Establishes a data connection with the FTP server, returning
+ * a Socket for the connection if successful. If a restart
+ * offset has been set with {@link #setRestartOffset(long)},
+ * a REST command is issued to the server with the offset as
+ * an argument before establishing the data connection. Active
+ * mode connections also cause a local PORT command to be issued.
+ * <p>
+ * @param command The text representation of the FTP command to send.
+ * @param arg The arguments to the FTP command. If this parameter is
+ * set to null, then the command is sent with no argument.
+ * @return A Socket corresponding to the established data connection.
+ * Null is returned if an FTP protocol error is reported at
+ * any point during the establishment and initialization of
+ * the connection.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ */
+ protected Socket _openDataConnection_(int command, String arg)
+ throws IOException
+ {
+ Socket socket;
+
+ if (__dataConnectionMode != ACTIVE_LOCAL_DATA_CONNECTION_MODE &&
+ __dataConnectionMode != PASSIVE_LOCAL_DATA_CONNECTION_MODE)
+ return null;
+
+ if (__dataConnectionMode == ACTIVE_LOCAL_DATA_CONNECTION_MODE)
+ {
+ ServerSocket server;
+ server = _serverSocketFactory_.createServerSocket(0, 1, getLocalAddress());
+
+ if (!FTPReply.isPositiveCompletion(port(getLocalAddress(),
+ server.getLocalPort())))
+ {
+ server.close();
+ return null;
+ }
+
+ if ((__restartOffset > 0) && !restart(__restartOffset))
+ {
+ server.close();
+ return null;
+ }
+
+ if (!FTPReply.isPositivePreliminary(sendCommand(command, arg)))
+ {
+ server.close();
+ return null;
+ }
+
+ // For now, let's just use the data timeout value for waiting for
+ // the data connection. It may be desirable to let this be a
+ // separately configurable value. In any case, we really want
+ // to allow preventing the accept from blocking indefinitely.
+ if (__dataTimeout >= 0)
+ server.setSoTimeout(__dataTimeout);
+ try {
+ socket = server.accept();
+ } finally {
+ server.close();
+ }
+ }
+ else
+ { // We must be in PASSIVE_LOCAL_DATA_CONNECTION_MODE
+
+ if (pasv() != FTPReply.ENTERING_PASSIVE_MODE)
+ return null;
+
+ __parsePassiveModeReply(_replyLines.get(_replyLines.size() - 1));
+
+ socket = _socketFactory_.createSocket(__passiveHost, __passivePort);
+ if ((__restartOffset > 0) && !restart(__restartOffset))
+ {
+ socket.close();
+ return null;
+ }
+
+ if (!FTPReply.isPositivePreliminary(sendCommand(command, arg)))
+ {
+ socket.close();
+ return null;
+ }
+ }
+
+ if (__remoteVerificationEnabled && !verifyRemote(socket))
+ {
+ InetAddress host1, host2;
+
+ host1 = socket.getInetAddress();
+ host2 = getRemoteAddress();
+
+ socket.close();
+
+ throw new IOException(
+ "Host attempting data connection " + host1.getHostAddress() +
+ " is not same as server " + host2.getHostAddress());
+ }
+
+ if (__dataTimeout >= 0)
+ socket.setSoTimeout(__dataTimeout);
+
+ return socket;
+ }
+
+
+ @Override
+ protected void _connectAction_() throws IOException
+ {
+ super._connectAction_();
+ __initDefaults();
+ }
+
+
+ /***
+ * Sets the timeout in milliseconds to use when reading from the
+ * data connection. This timeout will be set immediately after
+ * opening the data connection.
+ * <p>
+ * @param timeout The default timeout in milliseconds that is used when
+ * opening a data connection socket.
+ ***/
+ public void setDataTimeout(int timeout)
+ {
+ __dataTimeout = timeout;
+ }
+
+ /**
+ * set the factory used for parser creation to the supplied factory object.
+ *
+ * @param parserFactory
+ * factory object used to create FTPFileEntryParsers
+ *
+ * @see org.apache.commons.net.ftp.parser.FTPFileEntryParserFactory
+ * @see org.apache.commons.net.ftp.parser.DefaultFTPFileEntryParserFactory
+ */
+ public void setParserFactory(FTPFileEntryParserFactory parserFactory) {
+ __parserFactory = parserFactory;
+ }
+
+
+ /***
+ * Closes the connection to the FTP server and restores
+ * connection parameters to the default values.
+ * <p>
+ * @exception IOException If an error occurs while disconnecting.
+ ***/
+ @Override
+ public void disconnect() throws IOException
+ {
+ super.disconnect();
+ __initDefaults();
+ }
+
+
+ /***
+ * Enable or disable verification that the remote host taking part
+ * of a data connection is the same as the host to which the control
+ * connection is attached. The default is for verification to be
+ * enabled. You may set this value at any time, whether the
+ * FTPClient is currently connected or not.
+ * <p>
+ * @param enable True to enable verification, false to disable verification.
+ ***/
+ public void setRemoteVerificationEnabled(boolean enable)
+ {
+ __remoteVerificationEnabled = enable;
+ }
+
+ /***
+ * Return whether or not verification of the remote host participating
+ * in data connections is enabled. The default behavior is for
+ * verification to be enabled.
+ * <p>
+ * @return True if verification is enabled, false if not.
+ ***/
+ public boolean isRemoteVerificationEnabled()
+ {
+ return __remoteVerificationEnabled;
+ }
+
+ /***
+ * Login to the FTP server using the provided username and password.
+ * <p>
+ * @param username The username to login under.
+ * @param password The password to use.
+ * @return True if successfully completed, false if not.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ ***/
+ public boolean login(String username, String password) throws IOException
+ {
+ user(username);
+
+ if (FTPReply.isPositiveCompletion(_replyCode))
+ return true;
+
+ // If we get here, we either have an error code, or an intermmediate
+ // reply requesting password.
+ if (!FTPReply.isPositiveIntermediate(_replyCode))
+ return false;
+
+ return FTPReply.isPositiveCompletion(pass(password));
+ }
+
+
+ /***
+ * Login to the FTP server using the provided username, password,
+ * and account. If no account is required by the server, only
+ * the username and password, the account information is not used.
+ * <p>
+ * @param username The username to login under.
+ * @param password The password to use.
+ * @param account The account to use.
+ * @return True if successfully completed, false if not.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ ***/
+ public boolean login(String username, String password, String account)
+ throws IOException
+ {
+ user(username);
+
+ if (FTPReply.isPositiveCompletion(_replyCode))
+ return true;
+
+ // If we get here, we either have an error code, or an intermmediate
+ // reply requesting password.
+ if (!FTPReply.isPositiveIntermediate(_replyCode))
+ return false;
+
+ pass(password);
+
+ if (FTPReply.isPositiveCompletion(_replyCode))
+ return true;
+
+ if (!FTPReply.isPositiveIntermediate(_replyCode))
+ return false;
+
+ return FTPReply.isPositiveCompletion(acct(account));
+ }
+
+ /***
+ * Logout of the FTP server by sending the QUIT command.
+ * <p>
+ * @return True if successfully completed, false if not.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ ***/
+ public boolean logout() throws IOException
+ {
+ return FTPReply.isPositiveCompletion(quit());
+ }
+
+
+ /***
+ * Change the current working directory of the FTP session.
+ * <p>
+ * @param pathname The new current working directory.
+ * @return True if successfully completed, false if not.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ ***/
+ public boolean changeWorkingDirectory(String pathname) throws IOException
+ {
+ return FTPReply.isPositiveCompletion(cwd(pathname));
+ }
+
+
+ /***
+ * Change to the parent directory of the current working directory.
+ * <p>
+ * @return True if successfully completed, false if not.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ ***/
+ public boolean changeToParentDirectory() throws IOException
+ {
+ return FTPReply.isPositiveCompletion(cdup());
+ }
+
+
+ /***
+ * Issue the FTP SMNT command.
+ * <p>
+ * @param pathname The pathname to mount.
+ * @return True if successfully completed, false if not.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ ***/
+ public boolean structureMount(String pathname) throws IOException
+ {
+ return FTPReply.isPositiveCompletion(smnt(pathname));
+ }
+
+ /***
+ * Reinitialize the FTP session. Not all FTP servers support this
+ * command, which issues the FTP REIN command.
+ * <p>
+ * @return True if successfully completed, false if not.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ ***/
+ boolean reinitialize() throws IOException
+ {
+ rein();
+
+ if (FTPReply.isPositiveCompletion(_replyCode) ||
+ (FTPReply.isPositivePreliminary(_replyCode) &&
+ FTPReply.isPositiveCompletion(getReply())))
+ {
+
+ __initDefaults();
+
+ return true;
+ }
+
+ return false;
+ }
+
+
+ /***
+ * Set the current data connection mode to
+ * <code>ACTIVE_LOCAL_DATA_CONNECTION_MODE</code>. No communication
+ * with the FTP server is conducted, but this causes all future data
+ * transfers to require the FTP server to connect to the client's
+ * data port. Additionally, to accommodate differences between socket
+ * implementations on different platforms, this method causes the
+ * client to issue a PORT command before every data transfer.
+ ***/
+ public void enterLocalActiveMode()
+ {
+ __dataConnectionMode = ACTIVE_LOCAL_DATA_CONNECTION_MODE;
+ __passiveHost = null;
+ __passivePort = -1;
+ }
+
+
+ /***
+ * Set the current data connection mode to
+ * <code> PASSIVE_LOCAL_DATA_CONNECTION_MODE </code>. Use this
+ * method only for data transfers between the client and server.
+ * This method causes a PASV command to be issued to the server
+ * before the opening of every data connection, telling the server to
+ * open a data port to which the client will connect to conduct
+ * data transfers. The FTPClient will stay in
+ * <code> PASSIVE_LOCAL_DATA_CONNECTION_MODE </code> until the
+ * mode is changed by calling some other method such as
+ * {@link #enterLocalActiveMode enterLocalActiveMode() }
+ ***/
+ public void enterLocalPassiveMode()
+ {
+ __dataConnectionMode = PASSIVE_LOCAL_DATA_CONNECTION_MODE;
+ // These will be set when just before a data connection is opened
+ // in _openDataConnection_()
+ __passiveHost = null;
+ __passivePort = -1;
+ }
+
+
+ /***
+ * Set the current data connection mode to
+ * <code> ACTIVE_REMOTE_DATA_CONNECTION </code>. Use this method only
+ * for server to server data transfers. This method issues a PORT
+ * command to the server, indicating the other server and port to which
+ * it should connect for data transfers. You must call this method
+ * before EVERY server to server transfer attempt. The FTPClient will
+ * NOT automatically continue to issue PORT commands. You also
+ * must remember to call
+ * {@link #enterLocalActiveMode enterLocalActiveMode() } if you
+ * wish to return to the normal data connection mode.
+ * <p>
+ * @param host The passive mode server accepting connections for data
+ * transfers.
+ * @param port The passive mode server's data port.
+ * @return True if successfully completed, false if not.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ ***/
+ public boolean enterRemoteActiveMode(InetAddress host, int port)
+ throws IOException
+ {
+ if (FTPReply.isPositiveCompletion(port(host, port)))
+ {
+ __dataConnectionMode = ACTIVE_REMOTE_DATA_CONNECTION_MODE;
+ __passiveHost = null;
+ __passivePort = -1;
+ return true;
+ }
+ return false;
+ }
+
+ /***
+ * Set the current data connection mode to
+ * <code> PASSIVE_REMOTE_DATA_CONNECTION_MODE </code>. Use this
+ * method only for server to server data transfers.
+ * This method issues a PASV command to the server, telling it to
+ * open a data port to which the active server will connect to conduct
+ * data transfers. You must call this method
+ * before EVERY server to server transfer attempt. The FTPClient will
+ * NOT automatically continue to issue PASV commands. You also
+ * must remember to call
+ * {@link #enterLocalActiveMode enterLocalActiveMode() } if you
+ * wish to return to the normal data connection mode.
+ * <p>
+ * @return True if successfully completed, false if not.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ ***/
+ public boolean enterRemotePassiveMode() throws IOException
+ {
+ if (pasv() != FTPReply.ENTERING_PASSIVE_MODE)
+ return false;
+
+ __dataConnectionMode = PASSIVE_REMOTE_DATA_CONNECTION_MODE;
+ __parsePassiveModeReply(_replyLines.get(0));
+
+ return true;
+ }
+
+ /***
+ * Returns the hostname or IP address (in the form of a string) returned
+ * by the server when entering passive mode. If not in passive mode,
+ * returns null. This method only returns a valid value AFTER a
+ * data connection has been opened after a call to
+ * {@link #enterLocalPassiveMode enterLocalPassiveMode()}.
+ * This is because FTPClient sends a PASV command to the server only
+ * just before opening a data connection, and not when you call
+ * {@link #enterLocalPassiveMode enterLocalPassiveMode()}.
+ * <p>
+ * @return The passive host name if in passive mode, otherwise null.
+ ***/
+ public String getPassiveHost()
+ {
+ return __passiveHost;
+ }
+
+ /***
+ * If in passive mode, returns the data port of the passive host.
+ * This method only returns a valid value AFTER a
+ * data connection has been opened after a call to
+ * {@link #enterLocalPassiveMode enterLocalPassiveMode()}.
+ * This is because FTPClient sends a PASV command to the server only
+ * just before opening a data connection, and not when you call
+ * {@link #enterLocalPassiveMode enterLocalPassiveMode()}.
+ * <p>
+ * @return The data port of the passive server. If not in passive
+ * mode, undefined.
+ ***/
+ public int getPassivePort()
+ {
+ return __passivePort;
+ }
+
+
+ /***
+ * Returns the current data connection mode (one of the
+ * <code> _DATA_CONNECTION_MODE </code> constants.
+ * <p>
+ * @return The current data connection mode (one of the
+ * <code> _DATA_CONNECTION_MODE </code> constants.
+ ***/
+ public int getDataConnectionMode()
+ {
+ return __dataConnectionMode;
+ }
+
+
+ /***
+ * Sets the file type to be transferred. This should be one of
+ * <code> FTP.ASCII_FILE_TYPE </code>, <code> FTP.BINARY_FILE_TYPE</code>,
+ * etc. The file type only needs to be set when you want to change the
+ * type. After changing it, the new type stays in effect until you change
+ * it again. The default file type is <code> FTP.ASCII_FILE_TYPE </code>
+ * if this method is never called.
+ * <p>
+ * @param fileType The <code> _FILE_TYPE </code> constant indcating the
+ * type of file.
+ * @return True if successfully completed, false if not.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ ***/
+ public boolean setFileType(int fileType) throws IOException
+ {
+ if (FTPReply.isPositiveCompletion(type(fileType)))
+ {
+ __fileType = fileType;
+ __fileFormat = FTP.NON_PRINT_TEXT_FORMAT;
+ return true;
+ }
+ return false;
+ }
+
+
+ /***
+ * Sets the file type to be transferred and the format. The type should be
+ * one of <code> FTP.ASCII_FILE_TYPE </code>,
+ * <code> FTP.BINARY_FILE_TYPE </code>, etc. The file type only needs to
+ * be set when you want to change the type. After changing it, the new
+ * type stays in effect until you change it again. The default file type
+ * is <code> FTP.ASCII_FILE_TYPE </code> if this method is never called.
+ * The format should be one of the FTP class <code> TEXT_FORMAT </code>
+ * constants, or if the type is <code> FTP.LOCAL_FILE_TYPE </code>, the
+ * format should be the byte size for that type. The default format
+ * is <code> FTP.NON_PRINT_TEXT_FORMAT </code> if this method is never
+ * called.
+ * <p>
+ * @param fileType The <code> _FILE_TYPE </code> constant indcating the
+ * type of file.
+ * @param formatOrByteSize The format of the file (one of the
+ * <code>_FORMAT</code> constants. In the case of
+ * <code>LOCAL_FILE_TYPE</code>, the byte size.
+ * <p>
+ * @return True if successfully completed, false if not.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ ***/
+ public boolean setFileType(int fileType, int formatOrByteSize)
+ throws IOException
+ {
+ if (FTPReply.isPositiveCompletion(type(fileType, formatOrByteSize)))
+ {
+ __fileType = fileType;
+ __fileFormat = formatOrByteSize;
+ return true;
+ }
+ return false;
+ }
+
+
+ /***
+ * Sets the file structure. The default structure is
+ * <code> FTP.FILE_STRUCTURE </code> if this method is never called.
+ * <p>
+ * @param structure The structure of the file (one of the FTP class
+ * <code>_STRUCTURE</code> constants).
+ * @return True if successfully completed, false if not.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ ***/
+ public boolean setFileStructure(int structure) throws IOException
+ {
+ if (FTPReply.isPositiveCompletion(stru(structure)))
+ {
+ __fileStructure = structure;
+ return true;
+ }
+ return false;
+ }
+
+
+ /***
+ * Sets the transfer mode. The default transfer mode
+ * <code> FTP.STREAM_TRANSFER_MODE </code> if this method is never called.
+ * <p>
+ * @param mode The new transfer mode to use (one of the FTP class
+ * <code>_TRANSFER_MODE</code> constants).
+ * @return True if successfully completed, false if not.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ ***/
+ public boolean setFileTransferMode(int mode) throws IOException
+ {
+ if (FTPReply.isPositiveCompletion(mode(mode)))
+ {
+ __fileTransferMode = mode;
+ return true;
+ }
+ return false;
+ }
+
+
+ /***
+ * Initiate a server to server file transfer. This method tells the
+ * server to which the client is connected to retrieve a given file from
+ * the other server.
+ * <p>
+ * @param filename The name of the file to retrieve.
+ * @return True if successfully completed, false if not.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ ***/
+ public boolean remoteRetrieve(String filename) throws IOException
+ {
+ if (__dataConnectionMode == ACTIVE_REMOTE_DATA_CONNECTION_MODE ||
+ __dataConnectionMode == PASSIVE_REMOTE_DATA_CONNECTION_MODE)
+ return FTPReply.isPositivePreliminary(retr(filename));
+ return false;
+ }
+
+
+ /***
+ * Initiate a server to server file transfer. This method tells the
+ * server to which the client is connected to store a file on
+ * the other server using the given filename. The other server must
+ * have had a <code> remoteRetrieve </code> issued to it by another
+ * FTPClient.
+ * <p>
+ * @param filename The name to call the file that is to be stored.
+ * @return True if successfully completed, false if not.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ ***/
+ public boolean remoteStore(String filename) throws IOException
+ {
+ if (__dataConnectionMode == ACTIVE_REMOTE_DATA_CONNECTION_MODE ||
+ __dataConnectionMode == PASSIVE_REMOTE_DATA_CONNECTION_MODE)
+ return FTPReply.isPositivePreliminary(stor(filename));
+ return false;
+ }
+
+
+ /***
+ * Initiate a server to server file transfer. This method tells the
+ * server to which the client is connected to store a file on
+ * the other server using a unique filename based on the given filename.
+ * The other server must have had a <code> remoteRetrieve </code> issued
+ * to it by another FTPClient.
+ * <p>
+ * @param filename The name on which to base the filename of the file
+ * that is to be stored.
+ * @return True if successfully completed, false if not.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ ***/
+ public boolean remoteStoreUnique(String filename) throws IOException
+ {
+ if (__dataConnectionMode == ACTIVE_REMOTE_DATA_CONNECTION_MODE ||
+ __dataConnectionMode == PASSIVE_REMOTE_DATA_CONNECTION_MODE)
+ return FTPReply.isPositivePreliminary(stou(filename));
+ return false;
+ }
+
+
+ /***
+ * Initiate a server to server file transfer. This method tells the
+ * server to which the client is connected to store a file on
+ * the other server using a unique filename.
+ * The other server must have had a <code> remoteRetrieve </code> issued
+ * to it by another FTPClient. Many FTP servers require that a base
+ * filename be given from which the unique filename can be derived. For
+ * those servers use the other version of <code> remoteStoreUnique</code>
+ * <p>
+ * @return True if successfully completed, false if not.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ ***/
+ public boolean remoteStoreUnique() throws IOException
+ {
+ if (__dataConnectionMode == ACTIVE_REMOTE_DATA_CONNECTION_MODE ||
+ __dataConnectionMode == PASSIVE_REMOTE_DATA_CONNECTION_MODE)
+ return FTPReply.isPositivePreliminary(stou());
+ return false;
+ }
+
+ // For server to server transfers
+ /***
+ * Initiate a server to server file transfer. This method tells the
+ * server to which the client is connected to append to a given file on
+ * the other server. The other server must have had a
+ * <code> remoteRetrieve </code> issued to it by another FTPClient.
+ * <p>
+ * @param filename The name of the file to be appended to, or if the
+ * file does not exist, the name to call the file being stored.
+ * <p>
+ * @return True if successfully completed, false if not.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ ***/
+ public boolean remoteAppend(String filename) throws IOException
+ {
+ if (__dataConnectionMode == ACTIVE_REMOTE_DATA_CONNECTION_MODE ||
+ __dataConnectionMode == PASSIVE_REMOTE_DATA_CONNECTION_MODE)
+ return FTPReply.isPositivePreliminary(stor(filename));
+ return false;
+ }
+
+ /***
+ * There are a few FTPClient methods that do not complete the
+ * entire sequence of FTP commands to complete a transaction. These
+ * commands require some action by the programmer after the reception
+ * of a positive intermediate command. After the programmer's code
+ * completes its actions, it must call this method to receive
+ * the completion reply from the server and verify the success of the
+ * entire transaction.
+ * <p>
+ * For example,
+ * <pre>
+ * InputStream input;
+ * OutputStream output;
+ * input = new FileInputStream("foobaz.txt");
+ * output = ftp.storeFileStream("foobar.txt")
+ * if(!FTPReply.isPositiveIntermediate(ftp.getReplyCode())) {
+ * input.close();
+ * output.close();
+ * ftp.logout();
+ * ftp.disconnect();
+ * System.err.println("File transfer failed.");
+ * System.exit(1);
+ * }
+ * Util.copyStream(input, output);
+ * input.close();
+ * output.close();
+ * // Must call completePendingCommand() to finish command.
+ * if(!ftp.completePendingCommand()) {
+ * ftp.logout();
+ * ftp.disconnect();
+ * System.err.println("File transfer failed.");
+ * System.exit(1);
+ * }
+ * </pre>
+ * <p>
+ * @return True if successfully completed, false if not.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ ***/
+ public boolean completePendingCommand() throws IOException
+ {
+ return FTPReply.isPositiveCompletion(getReply());
+ }
+
+
+ /***
+ * Retrieves a named file from the server and writes it to the given
+ * OutputStream. This method does NOT close the given OutputStream.
+ * If the current file type is ASCII, line separators in the file are
+ * converted to the local representation.
+ * <p>
+ * @param remote The name of the remote file.
+ * @param local The local OutputStream to which to write the file.
+ * @return True if successfully completed, false if not.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception CopyStreamException If an I/O error occurs while actually
+ * transferring the file. The CopyStreamException allows you to
+ * determine the number of bytes transferred and the IOException
+ * causing the error. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ ***/
+ public boolean retrieveFile(String remote, OutputStream local)
+ throws IOException
+ {
+ InputStream input;
+ Socket socket;
+
+ if ((socket = _openDataConnection_(FTPCommand.RETR, remote)) == null)
+ return false;
+
+ input = new BufferedInputStream(socket.getInputStream(),
+ getBufferSize());
+ if (__fileType == ASCII_FILE_TYPE)
+ input = new FromNetASCIIInputStream(input);
+ // Treat everything else as binary for now
+ try
+ {
+ Util.copyStream(input, local, getBufferSize(),
+ CopyStreamEvent.UNKNOWN_STREAM_SIZE, null,
+ false);
+ }
+ catch (IOException e)
+ {
+ try
+ {
+ socket.close();
+ }
+ catch (IOException f)
+ {}
+ throw e;
+ }
+ socket.close();
+ return completePendingCommand();
+ }
+
+ /***
+ * Returns an InputStream from which a named file from the server
+ * can be read. If the current file type is ASCII, the returned
+ * InputStream will convert line separators in the file to
+ * the local representation. You must close the InputStream when you
+ * finish reading from it. The InputStream itself will take care of
+ * closing the parent data connection socket upon being closed. To
+ * finalize the file transfer you must call
+ * {@link #completePendingCommand completePendingCommand } and
+ * check its return value to verify success.
+ * <p>
+ * @param remote The name of the remote file.
+ * @return An InputStream from which the remote file can be read. If
+ * the data connection cannot be opened (e.g., the file does not
+ * exist), null is returned (in which case you may check the reply
+ * code to determine the exact reason for failure).
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ ***/
+ public InputStream retrieveFileStream(String remote) throws IOException
+ {
+ InputStream input;
+ Socket socket;
+
+ if ((socket = _openDataConnection_(FTPCommand.RETR, remote)) == null)
+ return null;
+
+ input = socket.getInputStream();
+ if (__fileType == ASCII_FILE_TYPE) {
+ // We buffer ascii transfers because the buffering has to
+ // be interposed between FromNetASCIIOutputSream and the underlying
+ // socket input stream. We don't buffer binary transfers
+ // because we don't want to impose a buffering policy on the
+ // programmer if possible. Programmers can decide on their
+ // own if they want to wrap the SocketInputStream we return
+ // for file types other than ASCII.
+ input = new BufferedInputStream(input,
+ getBufferSize());
+ input = new FromNetASCIIInputStream(input);
+ }
+ return new org.apache.commons.net.io.SocketInputStream(socket, input);
+ }
+
+
+ /***
+ * Stores a file on the server using the given name and taking input
+ * from the given InputStream. This method does NOT close the given
+ * InputStream. If the current file type is ASCII, line separators in
+ * the file are transparently converted to the NETASCII format (i.e.,
+ * you should not attempt to create a special InputStream to do this).
+ * <p>
+ * @param remote The name to give the remote file.
+ * @param local The local InputStream from which to read the file.
+ * @return True if successfully completed, false if not.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception CopyStreamException If an I/O error occurs while actually
+ * transferring the file. The CopyStreamException allows you to
+ * determine the number of bytes transferred and the IOException
+ * causing the error. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ ***/
+ public boolean storeFile(String remote, InputStream local)
+ throws IOException
+ {
+ return __storeFile(FTPCommand.STOR, remote, local);
+ }
+
+
+ /***
+ * Returns an OutputStream through which data can be written to store
+ * a file on the server using the given name. If the current file type
+ * is ASCII, the returned OutputStream will convert line separators in
+ * the file to the NETASCII format (i.e., you should not attempt to
+ * create a special OutputStream to do this). You must close the
+ * OutputStream when you finish writing to it. The OutputStream itself
+ * will take care of closing the parent data connection socket upon being
+ * closed. To finalize the file transfer you must call
+ * {@link #completePendingCommand completePendingCommand } and
+ * check its return value to verify success.
+ * <p>
+ * @param remote The name to give the remote file.
+ * @return An OutputStream through which the remote file can be written. If
+ * the data connection cannot be opened (e.g., the file does not
+ * exist), null is returned (in which case you may check the reply
+ * code to determine the exact reason for failure).
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ ***/
+ public OutputStream storeFileStream(String remote) throws IOException
+ {
+ return __storeFileStream(FTPCommand.STOR, remote);
+ }
+
+ /***
+ * Appends to a file on the server with the given name, taking input
+ * from the given InputStream. This method does NOT close the given
+ * InputStream. If the current file type is ASCII, line separators in
+ * the file are transparently converted to the NETASCII format (i.e.,
+ * you should not attempt to create a special InputStream to do this).
+ * <p>
+ * @param remote The name of the remote file.
+ * @param local The local InputStream from which to read the data to
+ * be appended to the remote file.
+ * @return True if successfully completed, false if not.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception CopyStreamException If an I/O error occurs while actually
+ * transferring the file. The CopyStreamException allows you to
+ * determine the number of bytes transferred and the IOException
+ * causing the error. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ ***/
+ public boolean appendFile(String remote, InputStream local)
+ throws IOException
+ {
+ return __storeFile(FTPCommand.APPE, remote, local);
+ }
+
+ /***
+ * Returns an OutputStream through which data can be written to append
+ * to a file on the server with the given name. If the current file type
+ * is ASCII, the returned OutputStream will convert line separators in
+ * the file to the NETASCII format (i.e., you should not attempt to
+ * create a special OutputStream to do this). You must close the
+ * OutputStream when you finish writing to it. The OutputStream itself
+ * will take care of closing the parent data connection socket upon being
+ * closed. To finalize the file transfer you must call
+ * {@link #completePendingCommand completePendingCommand } and
+ * check its return value to verify success.
+ * <p>
+ * @param remote The name of the remote file.
+ * @return An OutputStream through which the remote file can be appended.
+ * If the data connection cannot be opened (e.g., the file does not
+ * exist), null is returned (in which case you may check the reply
+ * code to determine the exact reason for failure).
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ ***/
+ public OutputStream appendFileStream(String remote) throws IOException
+ {
+ return __storeFileStream(FTPCommand.APPE, remote);
+ }
+
+ /***
+ * Stores a file on the server using a unique name derived from the
+ * given name and taking input
+ * from the given InputStream. This method does NOT close the given
+ * InputStream. If the current file type is ASCII, line separators in
+ * the file are transparently converted to the NETASCII format (i.e.,
+ * you should not attempt to create a special InputStream to do this).
+ * <p>
+ * @param remote The name on which to base the unique name given to
+ * the remote file.
+ * @param local The local InputStream from which to read the file.
+ * @return True if successfully completed, false if not.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception CopyStreamException If an I/O error occurs while actually
+ * transferring the file. The CopyStreamException allows you to
+ * determine the number of bytes transferred and the IOException
+ * causing the error. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ ***/
+ public boolean storeUniqueFile(String remote, InputStream local)
+ throws IOException
+ {
+ return __storeFile(FTPCommand.STOU, remote, local);
+ }
+
+
+ /***
+ * Returns an OutputStream through which data can be written to store
+ * a file on the server using a unique name derived from the given name.
+ * If the current file type
+ * is ASCII, the returned OutputStream will convert line separators in
+ * the file to the NETASCII format (i.e., you should not attempt to
+ * create a special OutputStream to do this). You must close the
+ * OutputStream when you finish writing to it. The OutputStream itself
+ * will take care of closing the parent data connection socket upon being
+ * closed. To finalize the file transfer you must call
+ * {@link #completePendingCommand completePendingCommand } and
+ * check its return value to verify success.
+ * <p>
+ * @param remote The name on which to base the unique name given to
+ * the remote file.
+ * @return An OutputStream through which the remote file can be written. If
+ * the data connection cannot be opened (e.g., the file does not
+ * exist), null is returned (in which case you may check the reply
+ * code to determine the exact reason for failure).
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ ***/
+ public OutputStream storeUniqueFileStream(String remote) throws IOException
+ {
+ return __storeFileStream(FTPCommand.STOU, remote);
+ }
+
+ /**
+ * Stores a file on the server using a unique name assigned by the
+ * server and taking input from the given InputStream. This method does
+ * NOT close the given
+ * InputStream. If the current file type is ASCII, line separators in
+ * the file are transparently converted to the NETASCII format (i.e.,
+ * you should not attempt to create a special InputStream to do this).
+ * <p>
+ * @param local The local InputStream from which to read the file.
+ * @return True if successfully completed, false if not.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception CopyStreamException If an I/O error occurs while actually
+ * transferring the file. The CopyStreamException allows you to
+ * determine the number of bytes transferred and the IOException
+ * causing the error. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ */
+ public boolean storeUniqueFile(InputStream local) throws IOException
+ {
+ return __storeFile(FTPCommand.STOU, null, local);
+ }
+
+ /**
+ * Returns an OutputStream through which data can be written to store
+ * a file on the server using a unique name assigned by the server.
+ * If the current file type
+ * is ASCII, the returned OutputStream will convert line separators in
+ * the file to the NETASCII format (i.e., you should not attempt to
+ * create a special OutputStream to do this). You must close the
+ * OutputStream when you finish writing to it. The OutputStream itself
+ * will take care of closing the parent data connection socket upon being
+ * closed. To finalize the file transfer you must call
+ * {@link #completePendingCommand completePendingCommand } and
+ * check its return value to verify success.
+ * <p>
+ * @return An OutputStream through which the remote file can be written. If
+ * the data connection cannot be opened (e.g., the file does not
+ * exist), null is returned (in which case you may check the reply
+ * code to determine the exact reason for failure).
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ */
+ public OutputStream storeUniqueFileStream() throws IOException
+ {
+ return __storeFileStream(FTPCommand.STOU, null);
+ }
+
+ /***
+ * Reserve a number of bytes on the server for the next file transfer.
+ * <p>
+ * @param bytes The number of bytes which the server should allocate.
+ * @return True if successfully completed, false if not.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ ***/
+ public boolean allocate(int bytes) throws IOException
+ {
+ return FTPReply.isPositiveCompletion(allo(bytes));
+ }
+
+
+ /**
+ * Reserve space on the server for the next file transfer.
+ * <p>
+ * @param bytes The number of bytes which the server should allocate.
+ * @param recordSize The size of a file record.
+ * @return True if successfully completed, false if not.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ */
+ public boolean allocate(int bytes, int recordSize) throws IOException
+ {
+ return FTPReply.isPositiveCompletion(allo(bytes, recordSize));
+ }
+
+
+ /***
+ * Restart a <code>STREAM_TRANSFER_MODE</code> file transfer starting
+ * from the given offset. This will only work on FTP servers supporting
+ * the REST comand for the stream transfer mode. However, most FTP
+ * servers support this. Any subsequent file transfer will start
+ * reading or writing the remote file from the indicated offset.
+ * <p>
+ * @param offset The offset into the remote file at which to start the
+ * next file transfer.
+ * @return True if successfully completed, false if not.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ ***/
+ private boolean restart(long offset) throws IOException
+ {
+ __restartOffset = 0;
+ return FTPReply.isPositiveIntermediate(rest(Long.toString(offset)));
+ }
+
+ /***
+ * Sets the restart offset. The restart command is sent to the server
+ * only before sending the file transfer command. When this is done,
+ * the restart marker is reset to zero.
+ * <p>
+ * @param offset The offset into the remote file at which to start the
+ * next file transfer. This must be a value greater than or
+ * equal to zero.
+ ***/
+ public void setRestartOffset(long offset)
+ {
+ if (offset >= 0)
+ __restartOffset = offset;
+ }
+
+ /***
+ * Fetches the restart offset.
+ * <p>
+ * @return offset The offset into the remote file at which to start the
+ * next file transfer.
+ ***/
+ public long getRestartOffset()
+ {
+ return __restartOffset;
+ }
+
+
+
+ /***
+ * Renames a remote file.
+ * <p>
+ * @param from The name of the remote file to rename.
+ * @param to The new name of the remote file.
+ * @return True if successfully completed, false if not.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ ***/
+ public boolean rename(String from, String to) throws IOException
+ {
+ if (!FTPReply.isPositiveIntermediate(rnfr(from)))
+ return false;
+
+ return FTPReply.isPositiveCompletion(rnto(to));
+ }
+
+
+ /***
+ * Abort a transfer in progress.
+ * <p>
+ * @return True if successfully completed, false if not.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ ***/
+ public boolean abort() throws IOException
+ {
+ return FTPReply.isPositiveCompletion(abor());
+ }
+
+ /***
+ * Deletes a file on the FTP server.
+ * <p>
+ * @param pathname The pathname of the file to be deleted.
+ * @return True if successfully completed, false if not.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ ***/
+ public boolean deleteFile(String pathname) throws IOException
+ {
+ return FTPReply.isPositiveCompletion(dele(pathname));
+ }
+
+
+ /***
+ * Removes a directory on the FTP server (if empty).
+ * <p>
+ * @param pathname The pathname of the directory to remove.
+ * @return True if successfully completed, false if not.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ ***/
+ public boolean removeDirectory(String pathname) throws IOException
+ {
+ return FTPReply.isPositiveCompletion(rmd(pathname));
+ }
+
+
+ /***
+ * Creates a new subdirectory on the FTP server in the current directory
+ * (if a relative pathname is given) or where specified (if an absolute
+ * pathname is given).
+ * <p>
+ * @param pathname The pathname of the directory to create.
+ * @return True if successfully completed, false if not.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ ***/
+ public boolean makeDirectory(String pathname) throws IOException
+ {
+ return FTPReply.isPositiveCompletion(mkd(pathname));
+ }
+
+
+ /***
+ * Returns the pathname of the current working directory.
+ * <p>
+ * @return The pathname of the current working directory. If it cannot
+ * be obtained, returns null.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ ***/
+ public String printWorkingDirectory() throws IOException
+ {
+ if (pwd() != FTPReply.PATHNAME_CREATED)
+ return null;
+
+ return __parsePathname(_replyLines.get( _replyLines.size() - 1));
+ }
+
+
+ /**
+ * Send a site specific command.
+ * @param arguments The site specific command and arguments.
+ * @return True if successfully completed, false if not.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ */
+ public boolean sendSiteCommand(String arguments) throws IOException
+ {
+ return FTPReply.isPositiveCompletion(site(arguments));
+ }
+
+
+ /***
+ * Fetches the system type name from the server and returns the string.
+ * This value is cached for the duration of the connection after the
+ * first call to this method. In other words, only the first time
+ * that you invoke this method will it issue a SYST command to the
+ * FTP server. FTPClient will remember the value and return the
+ * cached value until a call to disconnect.
+ * <p>
+ * @return The system type name obtained from the server. null if the
+ * information could not be obtained.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ ***/
+ public String getSystemName() throws IOException
+ {
+ //if (syst() == FTPReply.NAME_SYSTEM_TYPE)
+ // Technically, we should expect a NAME_SYSTEM_TYPE response, but
+ // in practice FTP servers deviate, so we soften the condition to
+ // a positive completion.
+ if (__systemName == null && FTPReply.isPositiveCompletion(syst()))
+ __systemName = _replyLines.get(_replyLines.size() - 1).substring(4);
+
+ return __systemName;
+ }
+
+
+ /***
+ * Fetches the system help information from the server and returns the
+ * full string.
+ * <p>
+ * @return The system help string obtained from the server. null if the
+ * information could not be obtained.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ ***/
+ public String listHelp() throws IOException
+ {
+ if (FTPReply.isPositiveCompletion(help()))
+ return getReplyString();
+ return null;
+ }
+
+
+ /**
+ * Fetches the help information for a given command from the server and
+ * returns the full string.
+ * @param command The command on which to ask for help.
+ * @return The command help string obtained from the server. null if the
+ * information could not be obtained.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ */
+ public String listHelp(String command) throws IOException
+ {
+ if (FTPReply.isPositiveCompletion(help(command)))
+ return getReplyString();
+ return null;
+ }
+
+
+ /***
+ * Sends a NOOP command to the FTP server. This is useful for preventing
+ * server timeouts.
+ * <p>
+ * @return True if successfully completed, false if not.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ ***/
+ public boolean sendNoOp() throws IOException
+ {
+ return FTPReply.isPositiveCompletion(noop());
+ }
+
+
+ /***
+ * Obtain a list of filenames in a directory (or just the name of a given
+ * file, which is not particularly useful). This information is obtained
+ * through the NLST command. If the given pathname is a directory and
+ * contains no files, a zero length array is returned only
+ * if the FTP server returned a positive completion code, otherwise
+ * null is returned (the FTP server returned a 550 error No files found.).
+ * If the directory is not empty, an array of filenames in the directory is
+ * returned. If the pathname corresponds
+ * to a file, only that file will be listed. The server may or may not
+ * expand glob expressions.
+ * <p>
+ * @param pathname The file or directory to list.
+ * @return The list of filenames contained in the given path. null if
+ * the list could not be obtained. If there are no filenames in
+ * the directory, a zero-length array is returned.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ ***/
+ public String[] listNames(String pathname) throws IOException
+ {
+ String line;
+ Socket socket;
+ BufferedReader reader;
+ ArrayList<String> results;
+
+ if ((socket = _openDataConnection_(FTPCommand.NLST, pathname)) == null)
+ return null;
+
+ reader =
+ new BufferedReader(new InputStreamReader(socket.getInputStream(), getControlEncoding()));
+
+ results = new ArrayList<String>();
+ while ((line = reader.readLine()) != null)
+ results.add(line);
+
+ reader.close();
+ socket.close();
+
+ if (completePendingCommand())
+ {
+ String[] names = new String[ results.size() ];
+ return results.toArray(names);
+ }
+
+ return null;
+ }
+
+
+ /***
+ * Obtain a list of filenames in the current working directory
+ * This information is obtained through the NLST command. If the current
+ * directory contains no files, a zero length array is returned only
+ * if the FTP server returned a positive completion code, otherwise,
+ * null is returned (the FTP server returned a 550 error No files found.).
+ * If the directory is not empty, an array of filenames in the directory is
+ * returned.
+ * <p>
+ * @return The list of filenames contained in the current working
+ * directory. null if the list could not be obtained.
+ * If there are no filenames in the directory, a zero-length array
+ * is returned.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ ***/
+ public String[] listNames() throws IOException
+ {
+ return listNames(null);
+ }
+
+
+
+ /**
+ * Using the default system autodetect mechanism, obtain a
+ * list of file information for the current working directory
+ * or for just a single file.
+ * <p>
+ * This information is obtained through the LIST command. The contents of
+ * the returned array is determined by the<code> FTPFileEntryParser </code>
+ * used.
+ * <p>
+ * @param pathname The file or directory to list. Since the server may
+ * or may not expand glob expressions, using them here
+ * is not recommended and may well cause this method to
+ * fail.
+ *
+ * @return The list of file information contained in the given path in
+ * the format determined by the autodetection mechanism
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection
+ * as a result of the client being idle or some other
+ * reason causing the server to send FTP reply code 421.
+ * This exception may be caught either as an IOException
+ * or independently as itself.
+ * @exception IOException
+ * If an I/O error occurs while either sending a
+ * command to the server or receiving a reply
+ * from the server.
+ * @exception ParserInitializationException
+ * Thrown if the parserKey parameter cannot be
+ * resolved by the selected parser factory.
+ * In the DefaultFTPEntryParserFactory, this will
+ * happen when parserKey is neither
+ * the fully qualified class name of a class
+ * implementing the interface
+ * org.apache.commons.net.ftp.FTPFileEntryParser
+ * nor a string containing one of the recognized keys
+ * mapping to such a parser or if class loader
+ * security issues prevent its being loaded.
+ * @see org.apache.commons.net.ftp.parser.DefaultFTPFileEntryParserFactory
+ * @see org.apache.commons.net.ftp.parser.FTPFileEntryParserFactory
+ * @see org.apache.commons.net.ftp.FTPFileEntryParser
+ */
+ public FTPFile[] listFiles(String pathname)
+ throws IOException
+ {
+ String key = null;
+ FTPListParseEngine engine =
+ initiateListParsing(key, pathname);
+ return engine.getFiles();
+
+ }
+ /**
+ * Using the default system autodetect mechanism, obtain a
+ * list of file information for the current working directory.
+ * <p>
+ * This information is obtained through the LIST command. The contents of
+ * the returned array is determined by the<code> FTPFileEntryParser </code>
+ * used.
+ * <p>
+ * @return The list of file information contained in the current directory
+ * in the format determined by the autodetection mechanism.
+ * <p><b>
+ * NOTE:</b> This array may contain null members if any of the
+ * individual file listings failed to parse. The caller should
+ * check each entry for null before referencing it.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection
+ * as a result of the client being idle or some other
+ * reason causing the server to send FTP reply code 421.
+ * This exception may be caught either as an IOException
+ * or independently as itself.
+ * @exception IOException
+ * If an I/O error occurs while either sending a
+ * command to the server or receiving a reply
+ * from the server.
+ * @exception ParserInitializationException
+ * Thrown if the parserKey parameter cannot be
+ * resolved by the selected parser factory.
+ * In the DefaultFTPEntryParserFactory, this will
+ * happen when parserKey is neither
+ * the fully qualified class name of a class
+ * implementing the interface
+ * org.apache.commons.net.ftp.FTPFileEntryParser
+ * nor a string containing one of the recognized keys
+ * mapping to such a parser or if class loader
+ * security issues prevent its being loaded.
+ * @see org.apache.commons.net.ftp.parser.DefaultFTPFileEntryParserFactory
+ * @see org.apache.commons.net.ftp.parser.FTPFileEntryParserFactory
+ * @see org.apache.commons.net.ftp.FTPFileEntryParser
+ */
+ public FTPFile[] listFiles()
+ throws IOException
+ {
+ return listFiles((String) null);
+ }
+
+ /**
+ * Using the default autodetect mechanism, initialize an FTPListParseEngine
+ * object containing a raw file information for the current working
+ * directory on the server
+ * This information is obtained through the LIST command. This object
+ * is then capable of being iterated to return a sequence of FTPFile
+ * objects with information filled in by the
+ * <code> FTPFileEntryParser </code> used.
+ * <p>
+ * This method differs from using the listFiles() methods in that
+ * expensive FTPFile objects are not created until needed which may be
+ * an advantage on large lists.
+ *
+ * @return A FTPListParseEngine object that holds the raw information and
+ * is capable of providing parsed FTPFile objects, one for each file
+ * containing information contained in the given path in the format
+ * determined by the <code> parser </code> parameter. Null will be
+ * returned if a data connection cannot be opened. If the current working
+ * directory contains no files, an empty array will be the return.
+ *
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException
+ * If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ * @exception ParserInitializationException
+ * Thrown if the autodetect mechanism cannot
+ * resolve the type of system we are connected with.
+ * @see FTPListParseEngine
+ */
+ public FTPListParseEngine initiateListParsing()
+ throws IOException
+ {
+ return initiateListParsing((String) null);
+ }
+
+ /**
+ * Using the default autodetect mechanism, initialize an FTPListParseEngine
+ * object containing a raw file information for the supplied directory.
+ * This information is obtained through the LIST command. This object
+ * is then capable of being iterated to return a sequence of FTPFile
+ * objects with information filled in by the
+ * <code> FTPFileEntryParser </code> used.
+ * <p>
+ * The server may or may not expand glob expressions. You should avoid
+ * using glob expressions because the return format for glob listings
+ * differs from server to server and will likely cause this method to fail.
+ * <p>
+ * This method differs from using the listFiles() methods in that
+ * expensive FTPFile objects are not created until needed which may be
+ * an advantage on large lists.
+ * <p>
+ * <pre>
+ * FTPClient f=FTPClient();
+ * f.connect(server);
+ * f.login(username, password);
+ * FTPListParseEngine engine = f.initiateListParsing(directory);
+ *
+ * while (engine.hasNext()) {
+ * FTPFile[] files = engine.getNext(25); // "page size" you want
+ * //do whatever you want with these files, display them, etc.
+ * //expensive FTPFile objects not created until needed.
+ * }
+ * </pre>
+ *
+ * @return A FTPListParseEngine object that holds the raw information and
+ * is capable of providing parsed FTPFile objects, one for each file
+ * containing information contained in the given path in the format
+ * determined by the <code> parser </code> parameter. Null will be
+ * returned if a data connection cannot be opened. If the current working
+ * directory contains no files, an empty array will be the return.
+ *
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException
+ * If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ * @exception ParserInitializationException
+ * Thrown if the autodetect mechanism cannot
+ * resolve the type of system we are connected with.
+ * @see FTPListParseEngine
+ */
+ public FTPListParseEngine initiateListParsing(
+ String pathname)
+ throws IOException
+ {
+ String key = null;
+ return initiateListParsing(key, pathname);
+ }
+
+ /**
+ * Using the supplied parser key, initialize an FTPListParseEngine
+ * object containing a raw file information for the supplied directory.
+ * This information is obtained through the LIST command. This object
+ * is then capable of being iterated to return a sequence of FTPFile
+ * objects with information filled in by the
+ * <code> FTPFileEntryParser </code> used.
+ * <p>
+ * The server may or may not expand glob expressions. You should avoid
+ * using glob expressions because the return format for glob listings
+ * differs from server to server and will likely cause this method to fail.
+ * <p>
+ * This method differs from using the listFiles() methods in that
+ * expensive FTPFile objects are not created until needed which may be
+ * an advantage on large lists.
+ *
+ * @param parserKey A string representing a designated code or fully-qualified
+ * class name of an <code> FTPFileEntryParser </code> that should be
+ * used to parse each server file listing.
+ *
+ * @return A FTPListParseEngine object that holds the raw information and
+ * is capable of providing parsed FTPFile objects, one for each file
+ * containing information contained in the given path in the format
+ * determined by the <code> parser </code> parameter. Null will be
+ * returned if a data connection cannot be opened. If the current working
+ * directory contains no files, an empty array will be the return.
+ *
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException
+ * If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ * @exception ParserInitializationException
+ * Thrown if the parserKey parameter cannot be
+ * resolved by the selected parser factory.
+ * In the DefaultFTPEntryParserFactory, this will
+ * happen when parserKey is neither
+ * the fully qualified class name of a class
+ * implementing the interface
+ * org.apache.commons.net.ftp.FTPFileEntryParser
+ * nor a string containing one of the recognized keys
+ * mapping to such a parser or if class loader
+ * security issues prevent its being loaded.
+ * @see FTPListParseEngine
+ */
+ public FTPListParseEngine initiateListParsing(
+ String parserKey, String pathname)
+ throws IOException
+ {
+ // We cache the value to avoid creation of a new object every
+ // time a file listing is generated.
+ if(__entryParser == null) {
+ if (null != parserKey) {
+ // if a parser key was supplied in the parameters,
+ // use that to create the paraser
+ __entryParser =
+ __parserFactory.createFileEntryParser(parserKey);
+
+ } else {
+ // if no parserKey was supplied, check for a configuration
+ // in the params, and if non-null, use that.
+ if (null != __configuration) {
+ __entryParser =
+ __parserFactory.createFileEntryParser(__configuration);
+ } else {
+ // if a parserKey hasn't been supplied, and a configuration
+ // hasn't been supplied, then autodetect by calling
+ // the SYST command and use that to choose the parser.
+ __entryParser =
+ __parserFactory.createFileEntryParser(getSystemName());
+ }
+ }
+ }
+
+ return initiateListParsing(__entryParser, pathname);
+
+ }
+
+
+ /**
+ * private method through which all listFiles() and
+ * initiateListParsing methods pass once a parser is determined.
+ *
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException
+ * If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ * @see FTPListParseEngine
+ */
+ private FTPListParseEngine initiateListParsing(
+ FTPFileEntryParser parser, String pathname)
+ throws IOException
+ {
+ Socket socket;
+
+ FTPListParseEngine engine = new FTPListParseEngine(parser);
+
+ if ((socket = _openDataConnection_(FTPCommand.LIST, getListArguments(pathname))) == null)
+ {
+ return engine;
+ }
+
+
+ try {
+ engine.readServerList(socket.getInputStream(), getControlEncoding());
+ }
+ finally {
+ socket.close();
+ }
+
+ completePendingCommand();
+ return engine;
+ }
+
+ /**
+ * @since 2.0
+ */
+ protected String getListArguments(String pathname) {
+ if (getListHiddenFiles())
+ {
+ StringBuffer sb = new StringBuffer(pathname.length() + 3);
+ sb.append("-a ");
+ sb.append(pathname);
+ return sb.toString();
+ }
+
+ return pathname;
+ }
+
+
+ /***
+ * Issue the FTP STAT command to the server.
+ * <p>
+ * @return The status information returned by the server.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ ***/
+ public String getStatus() throws IOException
+ {
+ if (FTPReply.isPositiveCompletion(stat()))
+ return getReplyString();
+ return null;
+ }
+
+
+ /***
+ * Issue the FTP STAT command to the server for a given pathname. This
+ * should produce a listing of the file or directory.
+ * <p>
+ * @return The status information returned by the server.
+ * @exception FTPConnectionClosedException
+ * If the FTP server prematurely closes the connection as a result
+ * of the client being idle or some other reason causing the server
+ * to send FTP reply code 421. This exception may be caught either
+ * as an IOException or independently as itself.
+ * @exception IOException If an I/O error occurs while either sending a
+ * command to the server or receiving a reply from the server.
+ ***/
+ public String getStatus(String pathname) throws IOException
+ {
+ if (FTPReply.isPositiveCompletion(stat(pathname)))
+ return getReplyString();
+ return null;
+ }
+
+
+ /**
+ * Issue the FTP MDTM command (not supported by all servers to retrieve the last
+ * modification time of a file. The modification string should be in the
+ * ISO 3077 form "YYYYMMDDhhmmss(.xxx)?". The timestamp represented should also be in
+ * GMT, but not all FTP servers honour this.
+ *
+ * @param pathname The file path to query.
+ * @return A string representing the last file modification time in <code>YYYYMMDDhhmmss</code> format.
+ * @throws IOException if an I/O error occurs.
+ * @since 2.0
+ */
+ public String getModificationTime(String pathname) throws IOException {
+ if (FTPReply.isPositiveCompletion(mdtm(pathname)))
+ return getReplyString();
+ return null;
+ }
+
+
+ /**
+ * Set the internal buffer size.
+ *
+ * @param bufSize The size of the buffer
+ */
+ public void setBufferSize(int bufSize) {
+ __bufferSize = bufSize;
+ }
+
+ /**
+ * Retrieve the current internal buffer size.
+ * @return The current buffer size.
+ */
+ public int getBufferSize() {
+ return __bufferSize;
+ }
+
+
+ /**
+ * Implementation of the {@link Configurable Configurable} interface.
+ * In the case of this class, configuring merely makes the config object available for the
+ * factory methods that construct parsers.
+ * @param config {@link FTPClientConfig FTPClientConfig} object used to
+ * provide non-standard configurations to the parser.
+ * @since 1.4
+ */
+ public void configure(FTPClientConfig config) {
+ this.__configuration = config;
+ }
+
+ /**
+ * You can set this to true if you would like to get hidden files when {@link #listFiles} too.
+ * A <code>LIST -a</code> will be issued to the ftp server.
+ * It depends on your ftp server if you need to call this method, also dont expect to get rid
+ * of hidden files if you call this method with "false".
+ *
+ * @param listHiddenFiles true if hidden files should be listed
+ * @since 2.0
+ */
+ public void setListHiddenFiles(boolean listHiddenFiles) {
+ this.__listHiddenFiles = listHiddenFiles;
+ }
+
+ /**
+ * @see #setListHiddenFiles(boolean)
+ * @return the current state
+ * @since 2.0
+ */
+ public boolean getListHiddenFiles() {
+ return this.__listHiddenFiles;
+ }
+}
+
+/* Emacs configuration
+ * Local variables: **
+ * mode: java **
+ * c-basic-offset: 4 **
+ * indent-tabs-mode: nil **
+ * End: **
+ */
diff --git a/app/src/main/java/org/apache/commons/net/ftp/FTPClientConfig.java b/app/src/main/java/org/apache/commons/net/ftp/FTPClientConfig.java
new file mode 100644
index 0000000..450eddc
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/ftp/FTPClientConfig.java
@@ -0,0 +1,580 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.ftp;
+
+import java.text.DateFormatSymbols;
+import java.util.Collection;
+import java.util.Locale;
+import java.util.Map;
+import java.util.StringTokenizer;
+import java.util.TreeMap;
+
+/**
+ * <p>
+ * This class implements an alternate means of configuring the
+ * {@link org.apache.commons.net.ftp.FTPClient FTPClient} object and
+ * also subordinate objects which it uses. Any class implementing the
+ * {@link org.apache.commons.net.ftp.Configurable Configurable }
+ * interface can be configured by this object.
+ * </p><p>
+ * In particular this class was designed primarily to support configuration
+ * of FTP servers which express file timestamps in formats and languages
+ * other than those for the US locale, which although it is the most common
+ * is not universal. Unfortunately, nothing in the FTP spec allows this to
+ * be determined in an automated way, so manual configuration such as this
+ * is necessary.
+ * </p><p>
+ * This functionality was designed to allow existing clients to work exactly
+ * as before without requiring use of this component. This component should
+ * only need to be explicitly invoked by the user of this package for problem
+ * cases that previous implementations could not solve.
+ * </p>
+ * <h3>Examples of use of FTPClientConfig</h3>
+ * Use cases:
+ * You are trying to access a server that
+ * <ul>
+ * <li>lists files with timestamps that use month names in languages other
+ * than English</li>
+ * <li>lists files with timestamps that use date formats other
+ * than the American English "standard" <code>MM dd yyyy</code></li>
+ * <li>is in different timezone and you need accurate timestamps for
+ * dependency checking as in Ant</li>
+ * </ul>
+ * <p>
+ * Unpaged (whole list) access on a UNIX server that uses French month names
+ * but uses the "standard" <code>MMM d yyyy</code> date formatting
+ * <pre>
+ * FTPClient f=FTPClient();
+ * FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_UNIX);
+ * conf.setServerLanguageCode("fr");
+ * f.configure(conf);
+ * f.connect(server);
+ * f.login(username, password);
+ * FTPFile[] files = listFiles(directory);
+ * </pre>
+ * </p>
+ * <p>
+ * Paged access on a UNIX server that uses Danish month names
+ * and "European" date formatting in Denmark's time zone, when you
+ * are in some other time zone.
+ * <pre>
+ * FTPClient f=FTPClient();
+ * FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_UNIX);
+ * conf.setServerLanguageCode("da");
+ * conf.setDefaultDateFormat("d MMM yyyy");
+ * conf.setRecentDateFormat("d MMM HH:mm");
+ * conf.setTimeZoneId("Europe/Copenhagen");
+ * f.configure(conf);
+ * f.connect(server);
+ * f.login(username, password);
+ * FTPListParseEngine engine =
+ * f.initiateListParsing("com.whatever.YourOwnParser", directory);
+ *
+ * while (engine.hasNext()) {
+ * FTPFile[] files = engine.getNext(25); // "page size" you want
+ * //do whatever you want with these files, display them, etc.
+ * //expensive FTPFile objects not created until needed.
+ * }
+ * </pre>
+ * </p>
+ * <p>
+ * Unpaged (whole list) access on a VMS server that uses month names
+ * in a language not {@link #getSupportedLanguageCodes() supported} by the system.
+ * but uses the "standard" <code>MMM d yyyy</code> date formatting
+ * <pre>
+ * FTPClient f=FTPClient();
+ * FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_VMS);
+ * conf.setShortMonthNames(
+ * "jan|feb|mar|apr|ma\u00ED|j\u00FAn|j\u00FAl|\u00e1g\u00FA|sep|okt|n\u00F3v|des");
+ * f.configure(conf);
+ * f.connect(server);
+ * f.login(username, password);
+ * FTPFile[] files = listFiles(directory);
+ * </pre>
+ * </p>
+ * <p>
+ * Unpaged (whole list) access on a Windows-NT server in a different time zone.
+ * (Note, since the NT Format uses numeric date formatting, language issues
+ * are irrelevant here).
+ * <pre>
+ * FTPClient f=FTPClient();
+ * FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_NT);
+ * conf.setTimeZoneId("America/Denver");
+ * f.configure(conf);
+ * f.connect(server);
+ * f.login(username, password);
+ * FTPFile[] files = listFiles(directory);
+ * </pre>
+ * </p>
+ * Unpaged (whole list) access on a Windows-NT server in a different time zone
+ * but which has been configured to use a unix-style listing format.
+ * <pre>
+ * FTPClient f=FTPClient();
+ * FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_UNIX);
+ * conf.setTimeZoneId("America/Denver");
+ * f.configure(conf);
+ * f.connect(server);
+ * f.login(username, password);
+ * FTPFile[] files = listFiles(directory);
+ * </pre>
+ * </p>
+ * @since 1.4
+ * @see org.apache.commons.net.ftp.Configurable
+ * @see org.apache.commons.net.ftp.FTPClient
+ * @see org.apache.commons.net.ftp.parser.FTPTimestampParserImpl#configure(FTPClientConfig)
+ * @see org.apache.commons.net.ftp.parser.ConfigurableFTPFileEntryParserImpl
+ */
+public class FTPClientConfig
+{
+
+ /**
+ * Identifier by which a unix-based ftp server is known throughout
+ * the commons-net ftp system.
+ */
+ public static final String SYST_UNIX = "UNIX";
+
+ /**
+ * Identifier by which a vms-based ftp server is known throughout
+ * the commons-net ftp system.
+ */
+ public static final String SYST_VMS = "VMS";
+
+ /**
+ * Identifier by which a WindowsNT-based ftp server is known throughout
+ * the commons-net ftp system.
+ */
+ public static final String SYST_NT = "WINDOWS";
+
+ /**
+ * Identifier by which an OS/2-based ftp server is known throughout
+ * the commons-net ftp system.
+ */
+ public static final String SYST_OS2 = "OS/2";
+
+ /**
+ * Identifier by which an OS/400-based ftp server is known throughout
+ * the commons-net ftp system.
+ */
+ public static final String SYST_OS400 = "OS/400";
+
+ /**
+ * Identifier by which an AS/400-based ftp server is known throughout
+ * the commons-net ftp system.
+ */
+ public static final String SYST_AS400 = "AS/400";
+
+ /**
+ * Identifier by which an MVS-based ftp server is known throughout
+ * the commons-net ftp system.
+ */
+ public static final String SYST_MVS = "MVS";
+
+ /**
+ * Some servers return an "UNKNOWN Type: L8" message
+ * in response to the SYST command. We set these to be a Unix-type system.
+ * This may happen if the ftpd in question was compiled without system
+ * information.
+ *
+ * NET-230 - Updated to be UPPERCASE so that the check done in
+ * createFileEntryParser will succeed.
+ *
+ * @since 1.5
+ */
+ public static final String SYST_L8 = "TYPE: L8";
+
+ /**
+ * Identifier by which an Netware-based ftp server is known throughout
+ * the commons-net ftp system.
+ *
+ * @since 1.5
+ */
+ public static final String SYST_NETWARE = "NETWARE";
+
+ private final String serverSystemKey;
+ private String defaultDateFormatStr = null;
+ private String recentDateFormatStr = null;
+ private boolean lenientFutureDates = false;
+ private String serverLanguageCode = null;
+ private String shortMonthNames = null;
+ private String serverTimeZoneId = null;
+
+
+ /**
+ * The main constructor for an FTPClientConfig object
+ * @param systemKey key representing system type of the server being
+ * connected to. See {@link #getServerSystemKey() serverSystemKey}
+ */
+ public FTPClientConfig(String systemKey) {
+ this.serverSystemKey = systemKey;
+ }
+
+ /**
+ * Convenience constructor mainly for use in testing.
+ * Constructs a UNIX configuration.
+ */
+ public FTPClientConfig() {
+ this(SYST_UNIX);
+ }
+
+ /**
+ * Constructor which allows setting of all member fields
+ * @param systemKey key representing system type of the server being
+ * connected to. See
+ * {@link #getServerSystemKey() serverSystemKey}
+ * @param defaultDateFormatStr See
+ * {@link #setDefaultDateFormatStr(String) defaultDateFormatStr}
+ * @param recentDateFormatStr See
+ * {@link #setRecentDateFormatStr(String) recentDateFormatStr}
+ * @param serverLanguageCode See
+ * {@link #setServerLanguageCode(String) serverLanguageCode}
+ * @param shortMonthNames See
+ * {@link #setShortMonthNames(String) shortMonthNames}
+ * @param serverTimeZoneId See
+ * {@link #setServerTimeZoneId(String) serverTimeZoneId}
+ */
+ public FTPClientConfig(String systemKey,
+ String defaultDateFormatStr,
+ String recentDateFormatStr,
+ String serverLanguageCode,
+ String shortMonthNames,
+ String serverTimeZoneId)
+ {
+ this(systemKey);
+ this.defaultDateFormatStr = defaultDateFormatStr;
+ this.recentDateFormatStr = recentDateFormatStr;
+ this.serverLanguageCode = serverLanguageCode;
+ this.shortMonthNames = shortMonthNames;
+ this.serverTimeZoneId = serverTimeZoneId;
+ }
+
+ private static Map<String, Object> LANGUAGE_CODE_MAP = new TreeMap<String, Object>();
+ static {
+
+ // if there are other commonly used month name encodings which
+ // correspond to particular locales, please add them here.
+
+
+
+ // many locales code short names for months as all three letters
+ // these we handle simply.
+ LANGUAGE_CODE_MAP.put("en", Locale.ENGLISH);
+ LANGUAGE_CODE_MAP.put("de",Locale.GERMAN);
+ LANGUAGE_CODE_MAP.put("it",Locale.ITALIAN);
+ LANGUAGE_CODE_MAP.put("es", new Locale("es", "", "")); // spanish
+ LANGUAGE_CODE_MAP.put("pt", new Locale("pt", "", "")); // portuguese
+ LANGUAGE_CODE_MAP.put("da", new Locale("da", "", "")); // danish
+ LANGUAGE_CODE_MAP.put("sv", new Locale("sv", "", "")); // swedish
+ LANGUAGE_CODE_MAP.put("no", new Locale("no", "", "")); // norwegian
+ LANGUAGE_CODE_MAP.put("nl", new Locale("nl", "", "")); // dutch
+ LANGUAGE_CODE_MAP.put("ro", new Locale("ro", "", "")); // romanian
+ LANGUAGE_CODE_MAP.put("sq", new Locale("sq", "", "")); // albanian
+ LANGUAGE_CODE_MAP.put("sh", new Locale("sh", "", "")); // serbo-croatian
+ LANGUAGE_CODE_MAP.put("sk", new Locale("sk", "", "")); // slovak
+ LANGUAGE_CODE_MAP.put("sl", new Locale("sl", "", "")); // slovenian
+
+
+ // some don't
+ LANGUAGE_CODE_MAP.put("fr",
+ "jan|f\u00e9v|mar|avr|mai|jun|jui|ao\u00fb|sep|oct|nov|d\u00e9c"); //french
+
+ }
+
+ /**
+ * Getter for the serverSystemKey property. This property
+ * specifies the general type of server to which the client connects.
+ * Should be either one of the <code>FTPClientConfig.SYST_*</code> codes
+ * or else the fully qualified class name of a parser implementing both
+ * the <code>FTPFileEntryParser</code> and <code>Configurable</code>
+ * interfaces.
+ * @return Returns the serverSystemKey property.
+ */
+ public String getServerSystemKey() {
+ return serverSystemKey;
+ }
+
+ /**
+ * getter for the {@link #setDefaultDateFormatStr(String) defaultDateFormatStr}
+ * property.
+ * @return Returns the defaultDateFormatStr property.
+ */
+ public String getDefaultDateFormatStr() {
+ return defaultDateFormatStr;
+ }
+
+ /**
+ * getter for the {@link #setRecentDateFormatStr(String) recentDateFormatStr} property.
+ * @return Returns the recentDateFormatStr property.
+ */
+
+ public String getRecentDateFormatStr() {
+ return recentDateFormatStr;
+ }
+
+ /**
+ * getter for the {@link #setServerTimeZoneId(String) serverTimeZoneId} property.
+ * @return Returns the serverTimeZoneId property.
+ */
+ public String getServerTimeZoneId() {
+ return serverTimeZoneId;
+ }
+
+ /**
+ * <p>
+ * getter for the {@link #setShortMonthNames(String) shortMonthNames}
+ * property.
+ * </p>
+ * @return Returns the shortMonthNames.
+ */
+ public String getShortMonthNames() {
+ return shortMonthNames;
+ }
+
+ /**
+ * <p>
+ * getter for the {@link #setServerLanguageCode(String) serverLanguageCode} property.
+ * </p>
+ * @return Returns the serverLanguageCode property.
+ */
+ public String getServerLanguageCode() {
+ return serverLanguageCode;
+ }
+
+ /**
+ * <p>
+ * getter for the {@link #setLenientFutureDates(boolean) lenientFutureDates} property.
+ * </p>
+ * @return Returns the lenientFutureDates.
+ * @since 1.5
+ */
+ public boolean isLenientFutureDates() {
+ return lenientFutureDates;
+ }
+ /**
+ * <p>
+ * setter for the defaultDateFormatStr property. This property
+ * specifies the main date format that will be used by a parser configured
+ * by this configuration to parse file timestamps. If this is not
+ * specified, such a parser will use as a default value, the most commonly
+ * used format which will be in as used in <code>en_US</code> locales.
+ * </p><p>
+ * This should be in the format described for
+ * <code>java.text.SimpleDateFormat</code>.
+ * property.
+ * </p>
+ * @param defaultDateFormatStr The defaultDateFormatStr to set.
+ */
+ public void setDefaultDateFormatStr(String defaultDateFormatStr) {
+ this.defaultDateFormatStr = defaultDateFormatStr;
+ }
+
+ /**
+ * <p>
+ * setter for the recentDateFormatStr property. This property
+ * specifies a secondary date format that will be used by a parser
+ * configured by this configuration to parse file timestamps, typically
+ * those less than a year old. If this is not specified, such a parser
+ * will not attempt to parse using an alternate format.
+ * </p>
+ * This is used primarily in unix-based systems.
+ * </p>
+ * This should be in the format described for
+ * <code>java.text.SimpleDateFormat</code>.
+ * </p>
+ * @param recentDateFormatStr The recentDateFormatStr to set.
+ */
+ public void setRecentDateFormatStr(String recentDateFormatStr) {
+ this.recentDateFormatStr = recentDateFormatStr;
+ }
+
+ /**
+ * <p>
+ * setter for the lenientFutureDates property. This boolean property
+ * (default: false) only has meaning when a
+ * {@link #setRecentDateFormatStr(String) recentDateFormatStr} property
+ * has been set. In that case, if this property is set true, then the
+ * parser, when it encounters a listing parseable with the recent date
+ * format, will only consider a date to belong to the previous year if
+ * it is more than one day in the future. This will allow all
+ * out-of-synch situations (whether based on "slop" - i.e. servers simply
+ * out of synch with one another or because of time zone differences -
+ * but in the latter case it is highly recommended to use the
+ * {@link #setServerTimeZoneId(String) serverTimeZoneId} property
+ * instead) to resolve correctly.
+ * </p><p>
+ * This is used primarily in unix-based systems.
+ * </p>
+ * @param lenientFutureDates set true to compensate for out-of-synch
+ * conditions.
+ */
+ public void setLenientFutureDates(boolean lenientFutureDates) {
+ this.lenientFutureDates = lenientFutureDates;
+ }
+ /**
+ * <p>
+ * setter for the serverTimeZoneId property. This property
+ * allows a time zone to be specified corresponding to that known to be
+ * used by an FTP server in file listings. This might be particularly
+ * useful to clients such as Ant that try to use these timestamps for
+ * dependency checking.
+ * </p><p>
+ * This should be one of the identifiers used by
+ * <code>java.util.TimeZone</code> to refer to time zones, for example,
+ * <code>America/Chicago</code> or <code>Asia/Rangoon</code>.
+ * </p>
+ * @param serverTimeZoneId The serverTimeZoneId to set.
+ */
+ public void setServerTimeZoneId(String serverTimeZoneId) {
+ this.serverTimeZoneId = serverTimeZoneId;
+ }
+
+ /**
+ * <p>
+ * setter for the shortMonthNames property.
+ * This property allows the user to specify a set of month names
+ * used by the server that is different from those that may be
+ * specified using the {@link #setServerLanguageCode(String) serverLanguageCode}
+ * property.
+ * </p><p>
+ * This should be a string containing twelve strings each composed of
+ * three characters, delimited by pipe (|) characters. Currently,
+ * only 8-bit ASCII characters are known to be supported. For example,
+ * a set of month names used by a hypothetical Icelandic FTP server might
+ * conceivably be specified as
+ * <code>"jan|feb|mar|apr|ma&#xED;|j&#xFA;n|j&#xFA;l|&#xE1;g&#xFA;|sep|okt|n&#xF3;v|des"</code>.
+ * </p>
+ * @param shortMonthNames The value to set to the shortMonthNames property.
+ */
+ public void setShortMonthNames(String shortMonthNames) {
+ this.shortMonthNames = shortMonthNames;
+ }
+
+ /**
+ * <p>
+ * setter for the serverLanguageCode property. This property allows
+ * user to specify a
+ * <a href="http://www.ics.uci.edu/pub/ietf/http/related/iso639.txt">
+ * two-letter ISO-639 language code</a> that will be used to
+ * configure the set of month names used by the file timestamp parser.
+ * If neither this nor the {@link #setShortMonthNames(String) shortMonthNames}
+ * is specified, parsing will assume English month names, which may or
+ * may not be significant, depending on whether the date format(s)
+ * specified via {@link #setDefaultDateFormatStr(String) defaultDateFormatStr}
+ * and/or {@link #setRecentDateFormatStr(String) recentDateFormatStr} are using
+ * numeric or alphabetic month names.
+ * </p>
+ * <p>If the code supplied is not supported here, <code>en_US</code>
+ * month names will be used. We are supporting here those language
+ * codes which, when a <code> java.util.Locale</code> is constucted
+ * using it, and a <code>java.text.SimpleDateFormat</code> is
+ * constructed using that Locale, the array returned by the
+ * SimpleDateFormat's <code>getShortMonths()</code> method consists
+ * solely of three 8-bit ASCII character strings. Additionally,
+ * languages which do not meet this requirement are included if a
+ * common alternative set of short month names is known to be used.
+ * This means that users who can tell us of additional such encodings
+ * may get them added to the list of supported languages by contacting
+ * the jakarta-commons-net team.
+ * </p>
+ * <p><strong>
+ * Please note that this attribute will NOT be used to determine a
+ * locale-based date format for the language. </strong>
+ * Experience has shown that many if not most FTP servers outside the
+ * United States employ the standard <code>en_US</code> date format
+ * orderings of <code>MMM d yyyy</code> and <code>MMM d HH:mm</code>
+ * and attempting to deduce this automatically here would cause more
+ * problems than it would solve. The date format must be changed
+ * via the {@link #setDefaultDateFormatStr(String) defaultDateFormatStr} and/or
+ * {@link #setRecentDateFormatStr(String) recentDateFormatStr} parameters.
+ * </p>
+ * @param serverLanguageCode The value to set to the serverLanguageCode property.
+ */
+ public void setServerLanguageCode(String serverLanguageCode) {
+ this.serverLanguageCode = serverLanguageCode;
+ }
+
+ /**
+ * Looks up the supplied language code in the internally maintained table of
+ * language codes. Returns a DateFormatSymbols object configured with
+ * short month names corresponding to the code. If there is no corresponding
+ * entry in the table, the object returned will be that for
+ * <code>Locale.US</code>
+ * @param languageCode See {@link #setServerLanguageCode(String) serverLanguageCode}
+ * @return a DateFormatSymbols object configured with short month names
+ * corresponding to the supplied code, or with month names for
+ * <code>Locale.US</code> if there is no corresponding entry in the internal
+ * table.
+ */
+ public static DateFormatSymbols lookupDateFormatSymbols(String languageCode)
+ {
+ Object lang = LANGUAGE_CODE_MAP.get(languageCode);
+ if (lang != null) {
+ if (lang instanceof Locale) {
+ return new DateFormatSymbols((Locale) lang);
+ } else if (lang instanceof String){
+ return getDateFormatSymbols((String) lang);
+ }
+ }
+ return new DateFormatSymbols(Locale.US);
+ }
+
+ /**
+ * Returns a DateFormatSymbols object configured with short month names
+ * as in the supplied string
+ * @param shortmonths This should be as described in
+ * {@link #setShortMonthNames(String) shortMonthNames}
+ * @return a DateFormatSymbols object configured with short month names
+ * as in the supplied string
+ */
+ public static DateFormatSymbols getDateFormatSymbols(String shortmonths)
+ {
+ String[] months = splitShortMonthString(shortmonths);
+ DateFormatSymbols dfs = new DateFormatSymbols(Locale.US);
+ dfs.setShortMonths(months);
+ return dfs;
+ }
+
+ private static String[] splitShortMonthString(String shortmonths) {
+ StringTokenizer st = new StringTokenizer(shortmonths, "|");
+ int monthcnt = st.countTokens();
+ if (12 != monthcnt) {
+ throw new IllegalArgumentException(
+ "expecting a pipe-delimited string containing 12 tokens");
+ }
+ String[] months = new String[13];
+ int pos = 0;
+ while(st.hasMoreTokens()) {
+ months[pos++] = st.nextToken();
+ }
+ months[pos]="";
+ return months;
+ }
+
+ /**
+ * Returns a Collection of all the language codes currently supported
+ * by this class. See {@link #setServerLanguageCode(String) serverLanguageCode}
+ * for a functional descrption of language codes within this system.
+ *
+ * @return a Collection of all the language codes currently supported
+ * by this class
+ */
+ public static Collection<String> getSupportedLanguageCodes() {
+ return LANGUAGE_CODE_MAP.keySet();
+ }
+
+
+}
diff --git a/app/src/main/java/org/apache/commons/net/ftp/FTPCommand.java b/app/src/main/java/org/apache/commons/net/ftp/FTPCommand.java
new file mode 100644
index 0000000..d016dff
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/ftp/FTPCommand.java
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.ftp;
+
+/***
+ * FTPCommand stores a set of constants for FTP command codes. To interpret
+ * the meaning of the codes, familiarity with RFC 959 is assumed.
+ * The mnemonic constant names are transcriptions from the code descriptions
+ * of RFC 959. For those who think in terms of the actual FTP commands,
+ * a set of constants such as {@link #USER USER } are provided
+ * where the constant name is the same as the FTP command.
+ * <p>
+ * <p>
+ * @author Daniel F. Savarese
+ ***/
+
+public final class FTPCommand
+{
+
+
+ public static final int USER = 0;
+ public static final int PASS = 1;
+ public static final int ACCT = 2;
+ public static final int CWD = 3;
+ public static final int CDUP = 4;
+ public static final int SMNT = 5;
+ public static final int REIN = 6;
+ public static final int QUIT = 7;
+ public static final int PORT = 8;
+ public static final int PASV = 9;
+ public static final int TYPE = 10;
+ public static final int STRU = 11;
+ public static final int MODE = 12;
+ public static final int RETR = 13;
+ public static final int STOR = 14;
+ public static final int STOU = 15;
+ public static final int APPE = 16;
+ public static final int ALLO = 17;
+ public static final int REST = 18;
+ public static final int RNFR = 19;
+ public static final int RNTO = 20;
+ public static final int ABOR = 21;
+ public static final int DELE = 22;
+ public static final int RMD = 23;
+ public static final int MKD = 24;
+ public static final int PWD = 25;
+ public static final int LIST = 26;
+ public static final int NLST = 27;
+ public static final int SITE = 28;
+ public static final int SYST = 29;
+ public static final int STAT = 30;
+ public static final int HELP = 31;
+ public static final int NOOP = 32;
+ /** @since 2.0 */
+ public static final int MDTM = 33;
+
+ public static final int USERNAME = USER;
+ public static final int PASSWORD = PASS;
+ public static final int ACCOUNT = ACCT;
+ public static final int CHANGE_WORKING_DIRECTORY = CWD;
+ public static final int CHANGE_TO_PARENT_DIRECTORY = CDUP;
+ public static final int STRUCTURE_MOUNT = SMNT;
+ public static final int REINITIALIZE = REIN;
+ public static final int LOGOUT = QUIT;
+ public static final int DATA_PORT = PORT;
+ public static final int PASSIVE = PASV;
+ public static final int REPRESENTATION_TYPE = TYPE;
+ public static final int FILE_STRUCTURE = STRU;
+ public static final int TRANSFER_MODE = MODE;
+ public static final int RETRIEVE = RETR;
+ public static final int STORE = STOR;
+ public static final int STORE_UNIQUE = STOU;
+ public static final int APPEND = APPE;
+ public static final int ALLOCATE = ALLO;
+ public static final int RESTART = REST;
+ public static final int RENAME_FROM = RNFR;
+ public static final int RENAME_TO = RNTO;
+ public static final int ABORT = ABOR;
+ public static final int DELETE = DELE;
+ public static final int REMOVE_DIRECTORY = RMD;
+ public static final int MAKE_DIRECTORY = MKD;
+ public static final int PRINT_WORKING_DIRECTORY = PWD;
+ // public static final int LIST = LIST;
+ public static final int NAME_LIST = NLST;
+ public static final int SITE_PARAMETERS = SITE;
+ public static final int SYSTEM = SYST;
+ public static final int STATUS = STAT;
+ //public static final int HELP = HELP;
+ //public static final int NOOP = NOOP;
+ /** @since 2.0 */
+ public static final int MOD_TIME = MDTM;
+
+ // Cannot be instantiated
+ private FTPCommand()
+ {}
+
+ static final String[] _commands = {
+ "USER", "PASS", "ACCT", "CWD", "CDUP", "SMNT", "REIN", "QUIT", "PORT",
+ "PASV", "TYPE", "STRU", "MODE", "RETR", "STOR", "STOU", "APPE", "ALLO",
+ "REST", "RNFR", "RNTO", "ABOR", "DELE", "RMD", "MKD", "PWD", "LIST",
+ "NLST", "SITE", "SYST", "STAT", "HELP", "NOOP"
+ };
+
+ /**
+ * Retrieve the FTP protocol command string corresponding to a specified
+ * command code.
+ * <p>
+ * @param command The command code.
+ * @return The FTP protcol command string corresponding to a specified
+ * command code.
+ */
+ public static final String getCommand(int command)
+ {
+ return _commands[command];
+ }
+}
diff --git a/app/src/main/java/org/apache/commons/net/ftp/FTPConnectionClosedException.java b/app/src/main/java/org/apache/commons/net/ftp/FTPConnectionClosedException.java
new file mode 100644
index 0000000..3eccbf4
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/ftp/FTPConnectionClosedException.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.ftp;
+import java.io.IOException;
+
+/***
+ * FTPConnectionClosedException is used to indicate the premature or
+ * unexpected closing of an FTP connection resulting from a
+ * {@link org.apache.commons.net.ftp.FTPReply#SERVICE_NOT_AVAILABLE FTPReply.SERVICE_NOT_AVAILABLE }
+ * response (FTP reply code 421) to a
+ * failed FTP command. This exception is derived from IOException and
+ * therefore may be caught either as an IOException or specifically as an
+ * FTPConnectionClosedException.
+ * <p>
+ * <p>
+ * @author Daniel F. Savarese
+ * @see FTP
+ * @see FTPClient
+ ***/
+
+public class FTPConnectionClosedException extends IOException
+{
+
+ /*** Constructs a FTPConnectionClosedException with no message ***/
+ public FTPConnectionClosedException()
+ {
+ super();
+ }
+
+ /***
+ * Constructs a FTPConnectionClosedException with a specified message.
+ * <p>
+ * @param message The message explaining the reason for the exception.
+ ***/
+ public FTPConnectionClosedException(String message)
+ {
+ super(message);
+ }
+
+}
diff --git a/app/src/main/java/org/apache/commons/net/ftp/FTPFile.java b/app/src/main/java/org/apache/commons/net/ftp/FTPFile.java
new file mode 100644
index 0000000..dd67904
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/ftp/FTPFile.java
@@ -0,0 +1,392 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.ftp;
+import java.io.Serializable;
+import java.util.Calendar;
+
+/***
+ * The FTPFile class is used to represent information about files stored
+ * on an FTP server. Because there is no standard representation for
+ * file information on FTP servers, it may not always be possible to
+ * extract all the information that can be represented by FTPFile, or
+ * it may even be possible to extract more information. In cases where
+ * more information can be extracted, you will want to subclass FTPFile
+ * and implement your own {@link org.apache.commons.net.ftp.FTPFileListParser}
+ * to extract the information.
+ * However, most FTP servers return file information in a format that
+ * can be completely parsed by
+ * {@link org.apache.commons.net.ftp.DefaultFTPFileListParser}
+ * and stored in FTPFile.
+ * <p>
+ * <p>
+ * @author Daniel F. Savarese
+ * @see FTPFileListParser
+ * @see DefaultFTPFileListParser
+ * @see FTPClient#listFiles
+ ***/
+
+public class FTPFile implements Serializable
+{
+ /** A constant indicating an FTPFile is a file. ***/
+ public static final int FILE_TYPE = 0;
+ /** A constant indicating an FTPFile is a directory. ***/
+ public static final int DIRECTORY_TYPE = 1;
+ /** A constant indicating an FTPFile is a symbolic link. ***/
+ public static final int SYMBOLIC_LINK_TYPE = 2;
+ /** A constant indicating an FTPFile is of unknown type. ***/
+ public static final int UNKNOWN_TYPE = 3;
+
+ /** A constant indicating user access permissions. ***/
+ public static final int USER_ACCESS = 0;
+ /** A constant indicating group access permissions. ***/
+ public static final int GROUP_ACCESS = 1;
+ /** A constant indicating world access permissions. ***/
+ public static final int WORLD_ACCESS = 2;
+
+ /** A constant indicating file/directory read permission. ***/
+ public static final int READ_PERMISSION = 0;
+ /** A constant indicating file/directory write permission. ***/
+ public static final int WRITE_PERMISSION = 1;
+ /**
+ * A constant indicating file execute permission or directory listing
+ * permission.
+ ***/
+ public static final int EXECUTE_PERMISSION = 2;
+
+ int _type, _hardLinkCount;
+ long _size;
+ String _rawListing, _user, _group, _name, _link;
+ Calendar _date;
+ boolean[] _permissions[];
+
+ /*** Creates an empty FTPFile. ***/
+ public FTPFile()
+ {
+ _permissions = new boolean[3][3];
+ _rawListing = null;
+ _type = UNKNOWN_TYPE;
+ _hardLinkCount = 0;
+ _size = 0;
+ _user = null;
+ _group = null;
+ _date = null;
+ _name = null;
+ }
+
+
+ /***
+ * Set the original FTP server raw listing from which the FTPFile was
+ * created.
+ * <p>
+ * @param rawListing The raw FTP server listing.
+ ***/
+ public void setRawListing(String rawListing)
+ {
+ _rawListing = rawListing;
+ }
+
+ /***
+ * Get the original FTP server raw listing used to initialize the FTPFile.
+ * <p>
+ * @return The original FTP server raw listing used to initialize the
+ * FTPFile.
+ ***/
+ public String getRawListing()
+ {
+ return _rawListing;
+ }
+
+
+ /***
+ * Determine if the file is a directory.
+ * <p>
+ * @return True if the file is of type <code>DIRECTORY_TYPE</code>, false if
+ * not.
+ ***/
+ public boolean isDirectory()
+ {
+ return (_type == DIRECTORY_TYPE);
+ }
+
+ /***
+ * Determine if the file is a regular file.
+ * <p>
+ * @return True if the file is of type <code>FILE_TYPE</code>, false if
+ * not.
+ ***/
+ public boolean isFile()
+ {
+ return (_type == FILE_TYPE);
+ }
+
+ /***
+ * Determine if the file is a symbolic link.
+ * <p>
+ * @return True if the file is of type <code>UNKNOWN_TYPE</code>, false if
+ * not.
+ ***/
+ public boolean isSymbolicLink()
+ {
+ return (_type == SYMBOLIC_LINK_TYPE);
+ }
+
+ /***
+ * Determine if the type of the file is unknown.
+ * <p>
+ * @return True if the file is of type <code>UNKNOWN_TYPE</code>, false if
+ * not.
+ ***/
+ public boolean isUnknown()
+ {
+ return (_type == UNKNOWN_TYPE);
+ }
+
+
+ /***
+ * Set the type of the file (<code>DIRECTORY_TYPE</code>,
+ * <code>FILE_TYPE</code>, etc.).
+ * <p>
+ * @param type The integer code representing the type of the file.
+ ***/
+ public void setType(int type)
+ {
+ _type = type;
+ }
+
+
+ /***
+ * Return the type of the file (one of the <code>_TYPE</code> constants),
+ * e.g., if it is a directory, a regular file, or a symbolic link.
+ * <p>
+ * @return The type of the file.
+ ***/
+ public int getType()
+ {
+ return _type;
+ }
+
+
+ /***
+ * Set the name of the file.
+ * <p>
+ * @param name The name of the file.
+ ***/
+ public void setName(String name)
+ {
+ _name = name;
+ }
+
+ /***
+ * Return the name of the file.
+ * <p>
+ * @return The name of the file.
+ ***/
+ public String getName()
+ {
+ return _name;
+ }
+
+
+ /**
+ * Set the file size in bytes.
+ * @param size The file size in bytes.
+ */
+ public void setSize(long size)
+ {
+ _size = size;
+ }
+
+
+ /***
+ * Return the file size in bytes.
+ * <p>
+ * @return The file size in bytes.
+ ***/
+ public long getSize()
+ {
+ return _size;
+ }
+
+
+ /***
+ * Set the number of hard links to this file. This is not to be
+ * confused with symbolic links.
+ * <p>
+ * @param links The number of hard links to this file.
+ ***/
+ public void setHardLinkCount(int links)
+ {
+ _hardLinkCount = links;
+ }
+
+
+ /***
+ * Return the number of hard links to this file. This is not to be
+ * confused with symbolic links.
+ * <p>
+ * @return The number of hard links to this file.
+ ***/
+ public int getHardLinkCount()
+ {
+ return _hardLinkCount;
+ }
+
+
+ /***
+ * Set the name of the group owning the file. This may be
+ * a string representation of the group number.
+ * <p>
+ * @param group The name of the group owning the file.
+ ***/
+ public void setGroup(String group)
+ {
+ _group = group;
+ }
+
+
+ /***
+ * Returns the name of the group owning the file. Sometimes this will be
+ * a string representation of the group number.
+ * <p>
+ * @return The name of the group owning the file.
+ ***/
+ public String getGroup()
+ {
+ return _group;
+ }
+
+
+ /***
+ * Set the name of the user owning the file. This may be
+ * a string representation of the user number;
+ * <p>
+ * @param user The name of the user owning the file.
+ ***/
+ public void setUser(String user)
+ {
+ _user = user;
+ }
+
+ /***
+ * Returns the name of the user owning the file. Sometimes this will be
+ * a string representation of the user number.
+ * <p>
+ * @return The name of the user owning the file.
+ ***/
+ public String getUser()
+ {
+ return _user;
+ }
+
+
+ /***
+ * If the FTPFile is a symbolic link, use this method to set the name of the
+ * file being pointed to by the symbolic link.
+ * <p>
+ * @param link The file pointed to by the symbolic link.
+ ***/
+ public void setLink(String link)
+ {
+ _link = link;
+ }
+
+
+ /***
+ * If the FTPFile is a symbolic link, this method returns the name of the
+ * file being pointed to by the symbolic link. Otherwise it returns null.
+ * <p>
+ * @return The file pointed to by the symbolic link (null if the FTPFile
+ * is not a symbolic link).
+ ***/
+ public String getLink()
+ {
+ return _link;
+ }
+
+
+ /***
+ * Set the file timestamp. This usually the last modification time.
+ * The parameter is not cloned, so do not alter its value after calling
+ * this method.
+ * <p>
+ * @param date A Calendar instance representing the file timestamp.
+ ***/
+ public void setTimestamp(Calendar date)
+ {
+ _date = date;
+ }
+
+
+ /***
+ * Returns the file timestamp. This usually the last modification time.
+ * <p>
+ * @return A Calendar instance representing the file timestamp.
+ ***/
+ public Calendar getTimestamp()
+ {
+ return _date;
+ }
+
+
+ /***
+ * Set if the given access group (one of the <code> _ACCESS </code>
+ * constants) has the given access permission (one of the
+ * <code> _PERMISSION </code> constants) to the file.
+ * <p>
+ * @param access The access group (one of the <code> _ACCESS </code>
+ * constants)
+ * @param permission The access permission (one of the
+ * <code> _PERMISSION </code> constants)
+ * @param value True if permission is allowed, false if not.
+ ***/
+ public void setPermission(int access, int permission, boolean value)
+ {
+ _permissions[access][permission] = value;
+ }
+
+
+ /***
+ * Determines if the given access group (one of the <code> _ACCESS </code>
+ * constants) has the given access permission (one of the
+ * <code> _PERMISSION </code> constants) to the file.
+ * <p>
+ * @param access The access group (one of the <code> _ACCESS </code>
+ * constants)
+ * @param permission The access permission (one of the
+ * <code> _PERMISSION </code> constants)
+ ***/
+ public boolean hasPermission(int access, int permission)
+ {
+ return _permissions[access][permission];
+ }
+
+
+ /***
+ * Returns a string representation of the FTPFile information. This
+ * will be the raw FTP server listing that was used to initialize the
+ * FTPFile instance.
+ * <p>
+ * @return A string representation of the FTPFile information.
+ ***/
+ @Override
+ public String toString()
+ {
+ return _rawListing;
+ }
+
+}
diff --git a/app/src/main/java/org/apache/commons/net/ftp/FTPFileEntryParser.java b/app/src/main/java/org/apache/commons/net/ftp/FTPFileEntryParser.java
new file mode 100644
index 0000000..8e6d09c
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/ftp/FTPFileEntryParser.java
@@ -0,0 +1,152 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.ftp;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * FTPFileEntryParser defines the interface for parsing a single FTP file
+ * listing and converting that information into an
+ * {@link org.apache.commons.net.ftp.FTPFile} instance.
+ * Sometimes you will want to parse unusual listing formats, in which
+ * case you would create your own implementation of FTPFileEntryParser and
+ * if necessary, subclass FTPFile.
+ * <p>
+ * Here are some examples showing how to use one of the classes that
+ * implement this interface.
+ * <p>
+ * The first example shows how to get an <b>iterable</b> list of files in which the
+ * more expensive <code>FTPFile</code> objects are not created until needed. This
+ * is suitable for paged displays. It requires that a parser object be created
+ * beforehand: <code>parser</code> is an object (in the package
+ * <code>org.apache.commons.net.ftp.parser</code>)
+ * implementing this inteface.
+ *
+ * <pre>
+ * FTPClient f=FTPClient();
+ * f.connect(server);
+ * f.login(username, password);
+ * FTPFileList list = f.createFileList(directory, parser);
+ * FTPFileIterator iter = list.iterator();
+ *
+ * while (iter.hasNext()) {
+ * FTPFile[] files = iter.getNext(25); // "page size" you want
+ * //do whatever you want with these files, display them, etc.
+ * //expensive FTPFile objects not created until needed.
+ * }
+ * </pre>
+ *
+ * The second example uses the revised <code>FTPClient.listFiles()</code>
+ * API to pull the whole list from the subfolder <code>subfolder</code> in
+ * one call, attempting to automatically detect the parser type. This
+ * method, without a parserKey parameter, indicates that autodection should
+ * be used.
+ *
+ * <pre>
+ * FTPClient f=FTPClient();
+ * f.connect(server);
+ * f.login(username, password);
+ * FTPFile[] files = f.listFiles("subfolder");
+ * </pre>
+ *
+ * The third example uses the revised <code>FTPClient.listFiles()</code>>
+ * API to pull the whole list from the current working directory in one call,
+ * but specifying by classname the parser to be used. For this particular
+ * parser class, this approach is necessary since there is no way to
+ * autodetect this server type.
+ *
+ * <pre>
+ * FTPClient f=FTPClient();
+ * f.connect(server);
+ * f.login(username, password);
+ * FTPFile[] files = f.listFiles(
+ * "org.apache.commons.net.ftp.parser.EnterpriseUnixFTPFileEntryParser",
+ * ".");
+ * </pre>
+ *
+ * The fourth example uses the revised <code>FTPClient.listFiles()</code>
+ * API to pull a single file listing in an arbitrary directory in one call,
+ * specifying by KEY the parser to be used, in this case, VMS.
+ *
+ * <pre>
+ * FTPClient f=FTPClient();
+ * f.connect(server);
+ * f.login(username, password);
+ * FTPFile[] files = f.listFiles("VMS", "subfolder/foo.java");
+ * </pre>
+ *
+ * @author <a href="mailto:scohen@apache.org">Steve Cohen</a>
+ * @version $Id: FTPFileEntryParser.java 636854 2008-03-13 19:55:01Z sebb $
+ * @see org.apache.commons.net.ftp.FTPFile
+ * @see org.apache.commons.net.ftp.FTPClient#createFileList
+ */
+public interface FTPFileEntryParser
+{
+ /**
+ * Parses a line of an FTP server file listing and converts it into a usable
+ * format in the form of an <code> FTPFile </code> instance. If the
+ * file listing line doesn't describe a file, <code> null </code> should be
+ * returned, otherwise a <code> FTPFile </code> instance representing the
+ * files in the directory is returned.
+ * <p>
+ * @param listEntry A line of text from the file listing
+ * @return An FTPFile instance corresponding to the supplied entry
+ */
+ FTPFile parseFTPEntry(String listEntry);
+
+ /**
+ * Reads the next entry using the supplied BufferedReader object up to
+ * whatever delemits one entry from the next. Implementors must define
+ * this for the particular ftp system being parsed. In many but not all
+ * cases, this can be defined simply by calling BufferedReader.readLine().
+ *
+ * @param reader The BufferedReader object from which entries are to be
+ * read.
+ *
+ * @return A string representing the next ftp entry or null if none found.
+ * @exception IOException thrown on any IO Error reading from the reader.
+ */
+ String readNextEntry(BufferedReader reader) throws IOException;
+
+
+ /**
+ * This method is a hook for those implementors (such as
+ * VMSVersioningFTPEntryParser, and possibly others) which need to
+ * perform some action upon the FTPFileList after it has been created
+ * from the server stream, but before any clients see the list.
+ *
+ * The default implementation can be a no-op.
+ *
+ * @param original Original list after it has been created from the server stream
+ *
+ * @return Original list as processed by this method.
+ */
+ List<String> preParse(List<String> original);
+
+
+}
+
+
+/* Emacs configuration
+ * Local variables: **
+ * mode: java **
+ * c-basic-offset: 4 **
+ * indent-tabs-mode: nil **
+ * End: **
+ */
diff --git a/app/src/main/java/org/apache/commons/net/ftp/FTPFileEntryParserImpl.java b/app/src/main/java/org/apache/commons/net/ftp/FTPFileEntryParserImpl.java
new file mode 100644
index 0000000..22214fe
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/ftp/FTPFileEntryParserImpl.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.ftp;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * This abstract class implements both the older FTPFileListParser and
+ * newer FTPFileEntryParser interfaces with default functionality.
+ * All the classes in the parser subpackage inherit from this.
+ *
+ */
+public abstract class FTPFileEntryParserImpl
+ implements FTPFileEntryParser
+{
+ /**
+ * The constructor for a FTPFileEntryParserImpl object.
+ */
+ public FTPFileEntryParserImpl()
+ {
+ }
+
+ /**
+ * Reads the next entry using the supplied BufferedReader object up to
+ * whatever delemits one entry from the next. This default implementation
+ * simply calls BufferedReader.readLine().
+ *
+ * @param reader The BufferedReader object from which entries are to be
+ * read.
+ *
+ * @return A string representing the next ftp entry or null if none found.
+ * @exception java.io.IOException thrown on any IO Error reading from the reader.
+ */
+ public String readNextEntry(BufferedReader reader) throws IOException
+ {
+ return reader.readLine();
+ }
+ /**
+ * This method is a hook for those implementors (such as
+ * VMSVersioningFTPEntryParser, and possibly others) which need to
+ * perform some action upon the FTPFileList after it has been created
+ * from the server stream, but before any clients see the list.
+ *
+ * This default implementation removes entries that do not parse as files.
+ *
+ * @param original Original list after it has been created from the server stream
+ *
+ * @return <code>original</code> unmodified.
+ */
+ public List<String> preParse(List<String> original) {
+ Iterator<String> it = original.iterator();
+ while (it.hasNext()){
+ String entry = it.next();
+ if (null == parseFTPEntry(entry)) {
+ it.remove();
+ }
+ }
+ return original;
+ }
+}
+
+/* Emacs configuration
+ * Local variables: **
+ * mode: java **
+ * c-basic-offset: 4 **
+ * indent-tabs-mode: nil **
+ * End: **
+ */
diff --git a/app/src/main/java/org/apache/commons/net/ftp/FTPListParseEngine.java b/app/src/main/java/org/apache/commons/net/ftp/FTPListParseEngine.java
new file mode 100644
index 0000000..468bf39
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/ftp/FTPListParseEngine.java
@@ -0,0 +1,290 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.ftp;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+
+
+/**
+ * This class handles the entire process of parsing a listing of
+ * file entries from the server.
+ * <p>
+ * This object defines a two-part parsing mechanism.
+ * <p>
+ * The first part is comprised of reading the raw input into an internal
+ * list of strings. Every item in this list corresponds to an actual
+ * file. All extraneous matter emitted by the server will have been
+ * removed by the end of this phase. This is accomplished in conjunction
+ * with the FTPFileEntryParser associated with this engine, by calling
+ * its methods <code>readNextEntry()</code> - which handles the issue of
+ * what delimits one entry from another, usually but not always a line
+ * feed and <code>preParse()</code> - which handles removal of
+ * extraneous matter such as the preliminary lines of a listing, removal
+ * of duplicates on versioning systems, etc.
+ * <p>
+ * The second part is composed of the actual parsing, again in conjunction
+ * with the particular parser used by this engine. This is controlled
+ * by an iterator over the internal list of strings. This may be done
+ * either in block mode, by calling the <code>getNext()</code> and
+ * <code>getPrevious()</code> methods to provide "paged" output of less
+ * than the whole list at one time, or by calling the
+ * <code>getFiles()</code> method to return the entire list.
+ * <p>
+ * Examples:
+ * <p>
+ * Paged access:
+ * <pre>
+ * FTPClient f=FTPClient();
+ * f.connect(server);
+ * f.login(username, password);
+ * FTPListParseEngine engine = f.initiateListParsing(directory);
+ *
+ * while (engine.hasNext()) {
+ * FTPFile[] files = engine.getNext(25); // "page size" you want
+ * //do whatever you want with these files, display them, etc.
+ * //expensive FTPFile objects not created until needed.
+ * }
+ * </pre>
+ * <p>
+ * For unpaged access, simply use FTPClient.listFiles(). That method
+ * uses this class transparently.
+ * @version $Id: FTPListParseEngine.java 658518 2008-05-21 01:04:30Z sebb $
+ */
+public class FTPListParseEngine {
+ private List<String> entries = new LinkedList<String>();
+ private ListIterator<String> _internalIterator = entries.listIterator();
+
+ FTPFileEntryParser parser = null;
+
+ public FTPListParseEngine(FTPFileEntryParser parser) {
+ this.parser = parser;
+ }
+
+ /**
+ * handle the iniitial reading and preparsing of the list returned by
+ * the server. After this method has completed, this object will contain
+ * a list of unparsed entries (Strings) each referring to a unique file
+ * on the server.
+ *
+ * @param stream input stream provided by the server socket.
+ *
+ * @exception IOException
+ * thrown on any failure to read from the sever.
+ */
+ public void readServerList(InputStream stream, String encoding)
+ throws IOException
+ {
+ this.entries = new LinkedList<String>();
+ readStream(stream, encoding);
+ this.parser.preParse(this.entries);
+ resetIterator();
+ }
+
+ /**
+ * handle the iniitial reading and preparsing of the list returned by
+ * the server. After this method has completed, this object will contain
+ * a list of unparsed entries (Strings) each referring to a unique file
+ * on the server.
+ *
+ * @param stream input stream provided by the server socket.
+ *
+ * @exception IOException
+ * thrown on any failure to read from the sever.
+ *
+ * @deprecated The version of this method which takes an encoding should be used.
+ */
+ public void readServerList(InputStream stream)
+ throws IOException
+ {
+ readServerList(stream, null);
+ }
+
+
+
+ /**
+ * Internal method for reading the input into the <code>entries</code> list.
+ * After this method has completed, <code>entries</code> will contain a
+ * collection of entries (as defined by
+ * <code>FTPFileEntryParser.readNextEntry()</code>), but this may contain
+ * various non-entry preliminary lines from the server output, duplicates,
+ * and other data that will not be part of the final listing.
+ *
+ * @param stream The socket stream on which the input will be read.
+ * @param encoding The encoding to use.
+ *
+ * @exception IOException
+ * thrown on any failure to read the stream
+ */
+ private void readStream(InputStream stream, String encoding) throws IOException
+ {
+ BufferedReader reader;
+ if (encoding == null)
+ {
+ reader = new BufferedReader(new InputStreamReader(stream));
+ }
+ else
+ {
+ reader = new BufferedReader(new InputStreamReader(stream, encoding));
+ }
+
+ String line = this.parser.readNextEntry(reader);
+
+ while (line != null)
+ {
+ this.entries.add(line);
+ line = this.parser.readNextEntry(reader);
+ }
+ reader.close();
+ }
+
+ /**
+ * Returns an array of at most <code>quantityRequested</code> FTPFile
+ * objects starting at this object's internal iterator's current position.
+ * If fewer than <code>quantityRequested</code> such
+ * elements are available, the returned array will have a length equal
+ * to the number of entries at and after after the current position.
+ * If no such entries are found, this array will have a length of 0.
+ *
+ * After this method is called this object's internal iterator is advanced
+ * by a number of positions equal to the size of the array returned.
+ *
+ * @param quantityRequested
+ * the maximum number of entries we want to get.
+ *
+ * @return an array of at most <code>quantityRequested</code> FTPFile
+ * objects starting at the current position of this iterator within its
+ * list and at least the number of elements which exist in the list at
+ * and after its current position.
+ * <p><b>
+ * NOTE:</b> This array may contain null members if any of the
+ * individual file listings failed to parse. The caller should
+ * check each entry for null before referencing it.
+ */
+ public FTPFile[] getNext(int quantityRequested) {
+ List<FTPFile> tmpResults = new LinkedList<FTPFile>();
+ int count = quantityRequested;
+ while (count > 0 && this._internalIterator.hasNext()) {
+ String entry = this._internalIterator.next();
+ FTPFile temp = this.parser.parseFTPEntry(entry);
+ tmpResults.add(temp);
+ count--;
+ }
+ return tmpResults.toArray(new FTPFile[0]);
+
+ }
+
+ /**
+ * Returns an array of at most <code>quantityRequested</code> FTPFile
+ * objects starting at this object's internal iterator's current position,
+ * and working back toward the beginning.
+ *
+ * If fewer than <code>quantityRequested</code> such
+ * elements are available, the returned array will have a length equal
+ * to the number of entries at and after after the current position.
+ * If no such entries are found, this array will have a length of 0.
+ *
+ * After this method is called this object's internal iterator is moved
+ * back by a number of positions equal to the size of the array returned.
+ *
+ * @param quantityRequested
+ * the maximum number of entries we want to get.
+ *
+ * @return an array of at most <code>quantityRequested</code> FTPFile
+ * objects starting at the current position of this iterator within its
+ * list and at least the number of elements which exist in the list at
+ * and after its current position. This array will be in the same order
+ * as the underlying list (not reversed).
+ * <p><b>
+ * NOTE:</b> This array may contain null members if any of the
+ * individual file listings failed to parse. The caller should
+ * check each entry for null before referencing it.
+ */
+ public FTPFile[] getPrevious(int quantityRequested) {
+ List<FTPFile> tmpResults = new LinkedList<FTPFile>();
+ int count = quantityRequested;
+ while (count > 0 && this._internalIterator.hasPrevious()) {
+ String entry = this._internalIterator.previous();
+ FTPFile temp = this.parser.parseFTPEntry(entry);
+ tmpResults.add(0,temp);
+ count--;
+ }
+ return tmpResults.toArray(new FTPFile[0]);
+ }
+
+ /**
+ * Returns an array of FTPFile objects containing the whole list of
+ * files returned by the server as read by this object's parser.
+ *
+ * @return an array of FTPFile objects containing the whole list of
+ * files returned by the server as read by this object's parser.
+ * <p><b>
+ * NOTE:</b> This array may contain null members if any of the
+ * individual file listings failed to parse. The caller should
+ * check each entry for null before referencing it.
+ * @exception IOException
+ */
+ public FTPFile[] getFiles()
+ throws IOException
+ {
+ List<FTPFile> tmpResults = new LinkedList<FTPFile>();
+ Iterator<String> iter = this.entries.iterator();
+ while (iter.hasNext()) {
+ String entry = iter.next();
+ FTPFile temp = this.parser.parseFTPEntry(entry);
+ tmpResults.add(temp);
+ }
+ return tmpResults.toArray(new FTPFile[0]);
+
+ }
+
+ /**
+ * convenience method to allow clients to know whether this object's
+ * internal iterator's current position is at the end of the list.
+ *
+ * @return true if internal iterator is not at end of list, false
+ * otherwise.
+ */
+ public boolean hasNext() {
+ return _internalIterator.hasNext();
+ }
+
+ /**
+ * convenience method to allow clients to know whether this object's
+ * internal iterator's current position is at the beginning of the list.
+ *
+ * @return true if internal iterator is not at beginning of list, false
+ * otherwise.
+ */
+ public boolean hasPrevious() {
+ return _internalIterator.hasPrevious();
+ }
+
+ /**
+ * resets this object's internal iterator to the beginning of the list.
+ */
+ public void resetIterator() {
+ this._internalIterator = this.entries.listIterator();
+ }
+}
diff --git a/app/src/main/java/org/apache/commons/net/ftp/FTPReply.java b/app/src/main/java/org/apache/commons/net/ftp/FTPReply.java
new file mode 100644
index 0000000..6386568
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/ftp/FTPReply.java
@@ -0,0 +1,240 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.ftp;
+
+/***
+ * FTPReply stores a set of constants for FTP reply codes. To interpret
+ * the meaning of the codes, familiarity with RFC 959 is assumed.
+ * The mnemonic constant names are transcriptions from the code descriptions
+ * of RFC 959. For those who think in terms of the actual reply code values,
+ * a set of CODE_NUM constants are provided where NUM is the numerical value
+ * of the code.
+ * <p>
+ * <p>
+ * @author Daniel F. Savarese
+ ***/
+
+public final class FTPReply
+{
+
+ public static final int CODE_110 = 110;
+ public static final int CODE_120 = 120;
+ public static final int CODE_125 = 125;
+ public static final int CODE_150 = 150;
+ public static final int CODE_200 = 200;
+ public static final int CODE_202 = 202;
+ public static final int CODE_211 = 211;
+ public static final int CODE_212 = 212;
+ public static final int CODE_213 = 213;
+ public static final int CODE_214 = 214;
+ public static final int CODE_215 = 215;
+ public static final int CODE_220 = 220;
+ public static final int CODE_221 = 221;
+ public static final int CODE_225 = 225;
+ public static final int CODE_226 = 226;
+ public static final int CODE_227 = 227;
+ public static final int CODE_230 = 230;
+ public static final int CODE_250 = 250;
+ public static final int CODE_257 = 257;
+ public static final int CODE_331 = 331;
+ public static final int CODE_332 = 332;
+ public static final int CODE_350 = 350;
+ public static final int CODE_421 = 421;
+ public static final int CODE_425 = 425;
+ public static final int CODE_426 = 426;
+ public static final int CODE_450 = 450;
+ public static final int CODE_451 = 451;
+ public static final int CODE_452 = 452;
+ public static final int CODE_500 = 500;
+ public static final int CODE_501 = 501;
+ public static final int CODE_502 = 502;
+ public static final int CODE_503 = 503;
+ public static final int CODE_504 = 504;
+ public static final int CODE_521 = 521;
+ public static final int CODE_530 = 530;
+ public static final int CODE_532 = 532;
+ public static final int CODE_550 = 550;
+ public static final int CODE_551 = 551;
+ public static final int CODE_552 = 552;
+ public static final int CODE_553 = 553;
+
+ public static final int RESTART_MARKER = CODE_110;
+ public static final int SERVICE_NOT_READY = CODE_120;
+ public static final int DATA_CONNECTION_ALREADY_OPEN = CODE_125;
+ public static final int FILE_STATUS_OK = CODE_150;
+ public static final int COMMAND_OK = CODE_200;
+ public static final int COMMAND_IS_SUPERFLUOUS = CODE_202;
+ public static final int SYSTEM_STATUS = CODE_211;
+ public static final int DIRECTORY_STATUS = CODE_212;
+ public static final int FILE_STATUS = CODE_213;
+ public static final int HELP_MESSAGE = CODE_214;
+ public static final int NAME_SYSTEM_TYPE = CODE_215;
+ public static final int SERVICE_READY = CODE_220;
+ public static final int SERVICE_CLOSING_CONTROL_CONNECTION = CODE_221;
+ public static final int DATA_CONNECTION_OPEN = CODE_225;
+ public static final int CLOSING_DATA_CONNECTION = CODE_226;
+ public static final int ENTERING_PASSIVE_MODE = CODE_227;
+ public static final int USER_LOGGED_IN = CODE_230;
+ public static final int FILE_ACTION_OK = CODE_250;
+ public static final int PATHNAME_CREATED = CODE_257;
+ public static final int NEED_PASSWORD = CODE_331;
+ public static final int NEED_ACCOUNT = CODE_332;
+ public static final int FILE_ACTION_PENDING = CODE_350;
+ public static final int SERVICE_NOT_AVAILABLE = CODE_421;
+ public static final int CANNOT_OPEN_DATA_CONNECTION = CODE_425;
+ public static final int TRANSFER_ABORTED = CODE_426;
+ public static final int FILE_ACTION_NOT_TAKEN = CODE_450;
+ public static final int ACTION_ABORTED = CODE_451;
+ public static final int INSUFFICIENT_STORAGE = CODE_452;
+ public static final int UNRECOGNIZED_COMMAND = CODE_500;
+ public static final int SYNTAX_ERROR_IN_ARGUMENTS = CODE_501;
+ public static final int COMMAND_NOT_IMPLEMENTED = CODE_502;
+ public static final int BAD_COMMAND_SEQUENCE = CODE_503;
+ public static final int COMMAND_NOT_IMPLEMENTED_FOR_PARAMETER = CODE_504;
+ public static final int NOT_LOGGED_IN = CODE_530;
+ public static final int NEED_ACCOUNT_FOR_STORING_FILES = CODE_532;
+ public static final int FILE_UNAVAILABLE = CODE_550;
+ public static final int PAGE_TYPE_UNKNOWN = CODE_551;
+ public static final int STORAGE_ALLOCATION_EXCEEDED = CODE_552;
+ public static final int FILE_NAME_NOT_ALLOWED = CODE_553;
+
+ // FTPS Reply Codes
+ /** @since 2.0 */
+ public static final int CODE_234 = 234;
+ /** @since 2.0 */
+ public static final int CODE_235 = 235;
+ /** @since 2.0 */
+ public static final int CODE_334 = 334;
+ /** @since 2.0 */
+ public static final int CODE_335 = 335;
+ /** @since 2.0 */
+ public static final int CODE_431 = 431;
+ /** @since 2.0 */
+ public static final int CODE_533 = 533;
+ /** @since 2.0 */
+ public static final int CODE_534 = 534;
+ /** @since 2.0 */
+ public static final int CODE_535 = 535;
+ /** @since 2.0 */
+ public static final int CODE_536 = 536;
+
+ /** @since 2.0 */
+ public static final int SECURITY_DATA_EXCHANGE_COMPLETE = CODE_234;
+ /** @since 2.0 */
+ public static final int SECURITY_DATA_EXCHANGE_SUCCESSFULLY = CODE_235;
+ /** @since 2.0 */
+ public static final int SECURITY_MECHANISM_IS_OK = CODE_334;
+ /** @since 2.0 */
+ public static final int SECURITY_DATA_IS_ACCEPTABLE = CODE_335;
+ /** @since 2.0 */
+ public static final int UNAVAILABLE_RESOURCE = CODE_431;
+ /** @since 2.0 */
+ public static final int DENIED_FOR_POLICY_REASONS = CODE_533;
+ /** @since 2.0 */
+ public static final int REQUEST_DENIED = CODE_534;
+ /** @since 2.0 */
+ public static final int FAILED_SECURITY_CHECK = CODE_535;
+ /** @since 2.0 */
+ public static final int REQUESTED_PROT_LEVEL_NOT_SUPPORTED = CODE_536;
+
+
+ // Cannot be instantiated
+ private FTPReply()
+ {}
+
+ /***
+ * Determine if a reply code is a positive preliminary response. All
+ * codes beginning with a 1 are positive preliminary responses.
+ * Postitive preliminary responses are used to indicate tentative success.
+ * No further commands can be issued to the FTP server after a positive
+ * preliminary response until a follow up response is received from the
+ * server.
+ * <p>
+ * @param reply The reply code to test.
+ * @return True if a reply code is a postive preliminary response, false
+ * if not.
+ ***/
+ public static boolean isPositivePreliminary(int reply)
+ {
+ return (reply >= 100 && reply < 200);
+ }
+
+ /***
+ * Determine if a reply code is a positive completion response. All
+ * codes beginning with a 2 are positive completion responses.
+ * The FTP server will send a positive completion response on the final
+ * successful completion of a command.
+ * <p>
+ * @param reply The reply code to test.
+ * @return True if a reply code is a postive completion response, false
+ * if not.
+ ***/
+ public static boolean isPositiveCompletion(int reply)
+ {
+ return (reply >= 200 && reply < 300);
+ }
+
+ /***
+ * Determine if a reply code is a positive intermediate response. All
+ * codes beginning with a 3 are positive intermediate responses.
+ * The FTP server will send a positive intermediate response on the
+ * successful completion of one part of a multi-part sequence of
+ * commands. For example, after a successful USER command, a positive
+ * intermediate response will be sent to indicate that the server is
+ * ready for the PASS command.
+ * <p>
+ * @param reply The reply code to test.
+ * @return True if a reply code is a postive intermediate response, false
+ * if not.
+ ***/
+ public static boolean isPositiveIntermediate(int reply)
+ {
+ return (reply >= 300 && reply < 400);
+ }
+
+ /***
+ * Determine if a reply code is a negative transient response. All
+ * codes beginning with a 4 are negative transient responses.
+ * The FTP server will send a negative transient response on the
+ * failure of a command that can be reattempted with success.
+ * <p>
+ * @param reply The reply code to test.
+ * @return True if a reply code is a negative transient response, false
+ * if not.
+ ***/
+ public static boolean isNegativeTransient(int reply)
+ {
+ return (reply >= 400 && reply < 500);
+ }
+
+ /***
+ * Determine if a reply code is a negative permanent response. All
+ * codes beginning with a 5 are negative permanent responses.
+ * The FTP server will send a negative permanent response on the
+ * failure of a command that cannot be reattempted with success.
+ * <p>
+ * @param reply The reply code to test.
+ * @return True if a reply code is a negative permanent response, false
+ * if not.
+ ***/
+ public static boolean isNegativePermanent(int reply)
+ {
+ return (reply >= 500 && reply < 600);
+ }
+
+}
diff --git a/app/src/main/java/org/apache/commons/net/ftp/FTPSClient.java b/app/src/main/java/org/apache/commons/net/ftp/FTPSClient.java
new file mode 100644
index 0000000..f809c41
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/ftp/FTPSClient.java
@@ -0,0 +1,533 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.ftp;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.net.Socket;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+
+/**
+ * FTP over SSL processing. If desired, the JVM property -Djavax.net.debug=all can be used to
+ * see wire-level SSL details.
+ *
+ * @version $Id: FTPSClient.java 658520 2008-05-21 01:14:11Z sebb $
+ * @since 2.0
+ */
+public class FTPSClient extends FTPClient {
+
+ /** keystore algorithm name. */
+ public static String KEYSTORE_ALGORITHM;
+ /** truststore algorithm name. */
+ public static String TRUSTSTORE_ALGORITHM;
+ /** provider name. */
+ public static String PROVIDER;
+ /** truststore type. */
+ public static String STORE_TYPE;
+
+ /** The value that I can set in PROT command */
+ private static final String[] PROT_COMMAND_VALUE = {"C","E","S","P"};
+ /** Default PROT Command */
+ private static final String DEFAULT_PROT = "C";
+ /** Default protocol name */
+ private static final String DEFAULT_PROTOCOL = "TLS";
+
+ /** The security mode. (True - Implicit Mode / False - Explicit Mode) */
+ private boolean isImplicit;
+ /** The use SSL/TLS protocol. */
+ private String protocol = DEFAULT_PROTOCOL;
+ /** The AUTH Command value */
+ private String auth = DEFAULT_PROTOCOL;
+ /** The context object. */
+ private SSLContext context;
+ /** The socket object. */
+ private Socket planeSocket;
+ /** The established socket flag. */
+ private boolean isCreation = true;
+ /** The use client mode flag. */
+ private boolean isClientMode = true;
+ /** The need client auth flag. */
+ private boolean isNeedClientAuth = false;
+ /** The want client auth flag. */
+ private boolean isWantClientAuth = false;
+ /** The cipher suites */
+ private String[] suites = null;
+ /** The protocol versions */
+ private String[] protocols = null;
+
+ /** The FTPS {@link TrustManager} implementation. */
+ private TrustManager trustManager = new FTPSTrustManager();
+
+ /** The {@link KeyManager} */
+ private KeyManager keyManager;
+
+ /**
+ * Constructor for FTPSClient.
+ * @throws NoSuchAlgorithmException A requested cryptographic algorithm
+ * is not available in the environment.
+ */
+ public FTPSClient() throws NoSuchAlgorithmException {
+ this.protocol = DEFAULT_PROTOCOL;
+ this.isImplicit = false;
+ }
+
+ /**
+ * Constructor for FTPSClient.
+ * @param isImplicit The secutiry mode(Implicit/Explicit).
+ * @throws NoSuchAlgorithmException A requested cryptographic algorithm
+ * is not available in the environment.
+ */
+ public FTPSClient(boolean isImplicit) throws NoSuchAlgorithmException {
+ this.protocol = DEFAULT_PROTOCOL;
+ this.isImplicit = isImplicit;
+ }
+
+ /**
+ * Constructor for FTPSClient.
+ * @param protocol the protocol
+ * @throws NoSuchAlgorithmException A requested cryptographic algorithm
+ * is not available in the environment.
+ */
+ public FTPSClient(String protocol) throws NoSuchAlgorithmException {
+ this.protocol = protocol;
+ this.isImplicit = false;
+ }
+
+ /**
+ * Constructor for FTPSClient.
+ * @param protocol the protocol
+ * @param isImplicit The secutiry mode(Implicit/Explicit).
+ * @throws NoSuchAlgorithmException A requested cryptographic algorithm
+ * is not available in the environment.
+ */
+ public FTPSClient(String protocol, boolean isImplicit)
+ throws NoSuchAlgorithmException {
+ this.protocol = protocol;
+ this.isImplicit = isImplicit;
+ }
+
+
+ /**
+ * Set AUTH command use value.
+ * This processing is done before connected processing.
+ * @param auth AUTH command use value.
+ */
+ public void setAuthValue(String auth) {
+ this.auth = auth;
+ }
+
+ /**
+ * Return AUTH command use value.
+ * @return AUTH command use value.
+ */
+ public String getAuthValue() {
+ return this.auth;
+ }
+
+
+ /**
+ * Because there are so many connect() methods,
+ * the _connectAction_() method is provided as a means of performing
+ * some action immediately after establishing a connection,
+ * rather than reimplementing all of the connect() methods.
+ * @throws IOException If it throw by _connectAction_.
+ * @see org.apache.commons.net.SocketClient#_connectAction_()
+ */
+ @Override
+ protected void _connectAction_() throws IOException {
+ // Implicit mode.
+ if (isImplicit) sslNegotiation();
+ super._connectAction_();
+ // Explicit mode.
+ if (!isImplicit) {
+ execAUTH();
+ sslNegotiation();
+ }
+ }
+
+ /**
+ * AUTH command.
+ * @throws SSLException If it server reply code not equal "234" and "334".
+ * @throws IOException If an I/O error occurs while either sending
+ * the command.
+ */
+ private void execAUTH() throws SSLException, IOException {
+ int replyCode = sendCommand(
+ FTPSCommand._commands[FTPSCommand.AUTH], auth);
+ if (FTPReply.SECURITY_MECHANISM_IS_OK == replyCode) {
+ // replyCode = 334
+ // I carry out an ADAT command.
+ } else if (FTPReply.SECURITY_DATA_EXCHANGE_COMPLETE != replyCode) {
+ throw new SSLException(getReplyString());
+ }
+ }
+
+ /**
+ * Performs a lazy init of the SSL context
+ * @throws IOException
+ */
+ private void initSslContext() throws IOException {
+ if(context == null) {
+ try {
+ context = SSLContext.getInstance(protocol);
+
+ context.init(new KeyManager[] { getKeyManager() } , new TrustManager[] { getTrustManager() } , null);
+ } catch (KeyManagementException e) {
+ IOException ioe = new IOException("Could not initialize SSL context");
+ ioe.initCause(e);
+ throw ioe;
+ } catch (NoSuchAlgorithmException e) {
+ IOException ioe = new IOException("Could not initialize SSL context");
+ ioe.initCause(e);
+ throw ioe;
+ }
+ }
+ }
+
+ /**
+ * SSL/TLS negotiation. Acquires an SSL socket of a control
+ * connection and carries out handshake processing.
+ * @throws IOException A handicap breaks out by sever negotiation.
+ */
+ private void sslNegotiation() throws IOException {
+ // Evacuation not ssl socket.
+ planeSocket = _socket_;
+
+ initSslContext();
+
+ SSLSocketFactory ssf = context.getSocketFactory();
+ String ip = _socket_.getInetAddress().getHostAddress();
+ int port = _socket_.getPort();
+ SSLSocket socket =
+ (SSLSocket) ssf.createSocket(_socket_, ip, port, true);
+ socket.setEnableSessionCreation(isCreation);
+ socket.setUseClientMode(isClientMode);
+ // server mode
+ if (!isClientMode) {
+ socket.setNeedClientAuth(isNeedClientAuth);
+ socket.setWantClientAuth(isWantClientAuth);
+ }
+ if (protocols != null) socket.setEnabledProtocols(protocols);
+ if (suites != null) socket.setEnabledCipherSuites(suites);
+
+ socket.startHandshake();
+
+ _socket_ = socket;
+ _controlInput_ = new BufferedReader(new InputStreamReader(
+ socket .getInputStream(), getControlEncoding()));
+ _controlOutput_ = new BufferedWriter(new OutputStreamWriter(
+ socket.getOutputStream(), getControlEncoding()));
+ }
+
+ /**
+ * Get the {@link KeyManager} instance.
+ * @return The {@link KeyManager} instance
+ */
+ private KeyManager getKeyManager() {
+ return keyManager;
+ }
+
+ /**
+ * Set a {@link KeyManager} to use
+ *
+ * @param keyManager The KeyManager implementation to set.
+ */
+ public void setKeyManager(KeyManager keyManager) {
+ this.keyManager = keyManager;
+ }
+
+ /**
+ * Controls whether new a SSL session may be established by this socket.
+ * @param isCreation The established socket flag.
+ */
+ public void setEnabledSessionCreation(boolean isCreation) {
+ this.isCreation = isCreation;
+ }
+
+ /**
+ * Returns true if new SSL sessions may be established by this socket.
+ * When a socket does not have a ssl socket, This return False.
+ * @return true - Indicates that sessions may be created;
+ * this is the default.
+ * false - indicates that an existing session must be resumed.
+ */
+ public boolean getEnableSessionCreation() {
+ if (_socket_ instanceof SSLSocket)
+ return ((SSLSocket)_socket_).getEnableSessionCreation();
+ return false;
+ }
+
+ /**
+ * Configures the socket to require client authentication.
+ * @param isNeedClientAuth The need client auth flag.
+ */
+ public void setNeedClientAuth(boolean isNeedClientAuth) {
+ this.isNeedClientAuth = isNeedClientAuth;
+ }
+
+ /**
+ * Returns true if the socket will require client authentication.
+ * When a socket does not have a ssl socket, This return False.
+ * @return true - If the server mode socket should request
+ * that the client authenticate itself.
+ */
+ public boolean getNeedClientAuth() {
+ if (_socket_ instanceof SSLSocket)
+ return ((SSLSocket)_socket_).getNeedClientAuth();
+ return false;
+ }
+
+ /**
+ * Configures the socket to request client authentication,
+ * but only if such a request is appropriate to the cipher
+ * suite negotiated.
+ * @param isWantClientAuth The want client auth flag.
+ */
+ public void setWantClientAuth(boolean isWantClientAuth) {
+ this.isWantClientAuth = isWantClientAuth;
+ }
+
+ /**
+ * Returns true if the socket will request client authentication.
+ * When a socket does not have a ssl socket, This return False.
+ * @return true - If the server mode socket should request
+ * that the client authenticate itself.
+ */
+ public boolean getWantClientAuth() {
+ if (_socket_ instanceof SSLSocket)
+ return ((SSLSocket)_socket_).getWantClientAuth();
+ return false;
+ }
+
+ /**
+ * Configures the socket to use client (or server) mode in its first
+ * handshake.
+ * @param isClientMode The use client mode flag.
+ */
+ public void setUseClientMode(boolean isClientMode) {
+ this.isClientMode = isClientMode;
+ }
+
+ /**
+ * Returns true if the socket is set to use client mode
+ * in its first handshake.
+ * When a socket does not have a ssl socket, This return False.
+ * @return true - If the socket should start its first handshake
+ * in "client" mode.
+ */
+ public boolean getUseClientMode() {
+ if (_socket_ instanceof SSLSocket)
+ return ((SSLSocket)_socket_).getUseClientMode();
+ return false;
+ }
+
+ /**
+ * Controls which particular cipher suites are enabled for use on this
+ * connection. I perform setting before a server negotiation.
+ * @param cipherSuites The cipher suites.
+ */
+ public void setEnabledCipherSuites(String[] cipherSuites) {
+ suites = new String[cipherSuites.length];
+ System.arraycopy(cipherSuites, 0, suites, 0, cipherSuites.length);
+ }
+
+ /**
+ * Returns the names of the cipher suites which could be enabled
+ * for use on this connection.
+ * When a socket does not have a ssl socket, This return null.
+ * @return An array of cipher suite names.
+ */
+ public String[] getEnabledCipherSuites() {
+ if (_socket_ instanceof SSLSocket)
+ return ((SSLSocket)_socket_).getEnabledCipherSuites();
+ return null;
+ }
+
+ /**
+ * Controls which particular protocol versions are enabled for use on this
+ * connection. I perform setting before a server negotiation.
+ * @param protocolVersions The protocol versions.
+ */
+ public void setEnabledProtocols(String[] protocolVersions) {
+ protocols = new String[protocolVersions.length];
+ System.arraycopy(protocolVersions, 0, protocols, 0, protocolVersions.length);
+ }
+
+ /**
+ * Returns the names of the protocol versions which are currently
+ * enabled for use on this connection.
+ * When a socket does not have a ssl socket, This return null.
+ * @return An array of protocols.
+ */
+ public String[] getEnabledProtocols() {
+ if (_socket_ instanceof SSLSocket)
+ return ((SSLSocket)_socket_).getEnabledProtocols();
+ return null;
+ }
+
+ /**
+ * PBSZ command. pbsz value: 0 to (2^32)-1 decimal integer.
+ * @param pbsz Protection Buffer Size.
+ * @throws SSLException If it server reply code not equal "200".
+ * @throws IOException If an I/O error occurs while either sending
+ * the command.
+ */
+ public void execPBSZ(long pbsz) throws SSLException, IOException {
+ if (pbsz < 0 || 4294967295L < pbsz)
+ throw new IllegalArgumentException();
+ if (FTPReply.COMMAND_OK != sendCommand(
+ FTPSCommand._commands[FTPSCommand.PBSZ],String.valueOf(pbsz)))
+ throw new SSLException(getReplyString());
+ }
+
+ /**
+ * PROT command.</br>
+ * C - Clear</br>
+ * S - Safe(SSL protocol only)</br>
+ * E - Confidential(SSL protocol only)</br>
+ * P - Private
+ * @param prot Data Channel Protection Level.
+ * @throws SSLException If it server reply code not equal "200".
+ * @throws IOException If an I/O error occurs while either sending
+ * the command.
+ */
+ public void execPROT(String prot) throws SSLException, IOException {
+ if (prot == null) prot = DEFAULT_PROT;
+ if (!checkPROTValue(prot)) throw new IllegalArgumentException();
+ if (FTPReply.COMMAND_OK != sendCommand(
+ FTPSCommand._commands[FTPSCommand.PROT], prot))
+ throw new SSLException(getReplyString());
+ if (DEFAULT_PROT.equals(prot)) {
+ setSocketFactory(null);
+ setServerSocketFactory(null);
+ } else {
+ setSocketFactory(new FTPSSocketFactory(context));
+
+ initSslContext();
+
+ SSLServerSocketFactory ssf = context.getServerSocketFactory();
+
+ setServerSocketFactory(ssf);
+ }
+ }
+
+ /**
+ * I check the value that I can set in PROT Command value.
+ * @param prot Data Channel Protection Level.
+ * @return True - A set point is right / False - A set point is not right
+ */
+ private boolean checkPROTValue(String prot) {
+ for (int p = 0; p < PROT_COMMAND_VALUE.length; p++) {
+ if (PROT_COMMAND_VALUE[p].equals(prot)) return true;
+ }
+ return false;
+ }
+
+ /**
+ * I carry out an ftp command.
+ * When a CCC command was carried out, I steep socket and SocketFactory
+ * in a state of not ssl.
+ * @parm command ftp command.
+ * @return server reply.
+ * @throws IOException If an I/O error occurs while either sending
+ * the command.
+ * @see org.apache.commons.net.ftp.FTP#sendCommand(java.lang.String)
+ */
+ @Override
+ public int sendCommand(String command, String args) throws IOException {
+ int repCode = super.sendCommand(command, args);
+ if (FTPSCommand._commands[FTPSCommand.CCC].equals(command)) {
+ if (FTPReply.COMMAND_OK == repCode) {
+ // TODO Check this - is this necessary at all?
+ _socket_ = planeSocket;
+ setSocketFactory(null);
+ } else {
+ throw new SSLException(getReplyString());
+ }
+ }
+ return repCode;
+ }
+
+ /**
+ * Returns a socket of the data connection.
+ * Wrapped as an {@link SSLSocket}, which carries out handshake processing.
+ * @pram command The text representation of the FTP command to send.
+ * @param arg The arguments to the FTP command.
+ * If this parameter is set to null, then the command is sent with
+ * no argument.
+ * @return A Socket corresponding to the established data connection.
+ * Null is returned if an FTP protocol error is reported at any point
+ * during the establishment and initialization of the connection.
+ * @throws IOException If there is any problem with the connection.
+ * @see org.apache.commons.net.ftp.FTPClient#_openDataConnection_(java.lang.String, int)
+ */
+ @Override
+ protected Socket _openDataConnection_(int command, String arg)
+ throws IOException {
+ Socket socket = super._openDataConnection_(command, arg);
+ if (socket != null && socket instanceof SSLSocket) {
+ SSLSocket sslSocket = (SSLSocket)socket;
+ sslSocket.setUseClientMode(isClientMode);
+ sslSocket.setEnableSessionCreation(isCreation);
+ // server mode
+ if (!isClientMode) {
+ sslSocket.setNeedClientAuth(isNeedClientAuth);
+ sslSocket.setWantClientAuth(isWantClientAuth);
+ }
+ if (suites != null)
+ sslSocket.setEnabledCipherSuites(suites);
+ if (protocols != null)
+ sslSocket.setEnabledProtocols(protocols);
+ sslSocket.startHandshake();
+ }
+ return socket;
+ }
+
+ /**
+ * Get the currently configured {@link TrustManager}.
+ *
+ * @return A TrustManager instance.
+ */
+ public TrustManager getTrustManager() {
+ return trustManager;
+ }
+
+ /**
+ * Override the default {@link TrustManager} to use.
+ *
+ * @param trustManager The TrustManager implementation to set.
+ */
+ public void setTrustManager(TrustManager trustManager) {
+ this.trustManager = trustManager;
+ }
+
+
+
+}
diff --git a/app/src/main/java/org/apache/commons/net/ftp/FTPSCommand.java b/app/src/main/java/org/apache/commons/net/ftp/FTPSCommand.java
new file mode 100644
index 0000000..6fd2efa
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/ftp/FTPSCommand.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.ftp;
+
+/**
+ * I acquire a command added in FTPS.
+ * @since 2.0
+ */
+public final class FTPSCommand {
+ public static final int AUTH = 0;
+ public static final int ADAT = 1;
+ public static final int PBSZ = 2;
+ public static final int PROT = 3;
+ public static final int CCC = 4;
+
+ public static final int AUTHENTICATION_SECURITY_MECHANISM = AUTH;
+ public static final int AUTHENTICATION_SECURITY_DATA = ADAT;
+ public static final int PROTECTION_BUFFER_SIZE = PBSZ;
+ public static final int DATA_CHANNEL_PROTECTION_LEVEL = PROT;
+ public static final int CLEAR_COMMAND_CHANNEL = CCC;
+
+ static final String[] _commands = {"AUTH","ADAT","PBSZ","PROT","CCC"};
+
+ /**
+ * Retrieve the FTPS command string corresponding to a specified
+ * command code.
+ * <p>
+ * @param command The command code.
+ * @return The FTPS command string corresponding to a specified
+ * command code.
+ */
+ public static final String getCommand(int command) {
+ return _commands[command];
+ }
+}
diff --git a/app/src/main/java/org/apache/commons/net/ftp/FTPSSocketFactory.java b/app/src/main/java/org/apache/commons/net/ftp/FTPSSocketFactory.java
new file mode 100644
index 0000000..b7e1186
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/ftp/FTPSSocketFactory.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.commons.net.ftp;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.UnknownHostException;
+
+import javax.net.SocketFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLServerSocket;
+
+
+/**
+ *
+ * Implementation of org.apache.commons.net.SocketFactory
+ *
+ * @since 2.0
+ */
+public class FTPSSocketFactory extends SocketFactory {
+
+ private SSLContext context;
+
+ public FTPSSocketFactory(SSLContext context) {
+ this.context = context;
+ }
+
+ @Override
+ public Socket createSocket(String address, int port) throws UnknownHostException, IOException {
+ return this.context.getSocketFactory().createSocket(address, port);
+ }
+
+ @Override
+ public Socket createSocket(InetAddress address, int port) throws IOException {
+ return this.context.getSocketFactory().createSocket(address, port);
+ }
+
+ @Override
+ public Socket createSocket(String address, int port, InetAddress localAddress, int localPort) throws UnknownHostException, IOException {
+ return this.context.getSocketFactory().createSocket(address, port, localAddress, localPort);
+ }
+
+ @Override
+ public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
+ return this.context.getSocketFactory().createSocket(address, port, localAddress, localPort);
+ }
+
+ public ServerSocket createServerSocket(int port) throws IOException {
+ return this.init(this.context.getServerSocketFactory().createServerSocket(port));
+ }
+
+ public ServerSocket createServerSocket(int port, int backlog) throws IOException {
+ return this.init(this.context.getServerSocketFactory().createServerSocket(port, backlog));
+ }
+
+ public ServerSocket createServerSocket(int port, int backlog, InetAddress ifAddress) throws IOException {
+ return this.init(this.context.getServerSocketFactory().createServerSocket(port, backlog, ifAddress));
+ }
+
+ public ServerSocket init(ServerSocket socket) throws IOException {
+ ((SSLServerSocket) socket).setUseClientMode(true);
+ return socket;
+ }
+}
diff --git a/app/src/main/java/org/apache/commons/net/ftp/FTPSTrustManager.java b/app/src/main/java/org/apache/commons/net/ftp/FTPSTrustManager.java
new file mode 100644
index 0000000..d1a2dd7
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/ftp/FTPSTrustManager.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.ftp;
+
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
+/**
+ * Custom {@link TrustManager} implementation.
+ *
+ * @version $Id: FTPSTrustManager.java 658520 2008-05-21 01:14:11Z sebb $
+ * @since 2.0
+ */
+public class FTPSTrustManager implements X509TrustManager
+{
+ /**
+ * No-op
+ */
+ public void checkClientTrusted(X509Certificate[] certificates, String authType)
+ {
+ return;
+ }
+
+ public void checkServerTrusted(X509Certificate[] certificates, String authType) throws CertificateException
+ {
+ for (int i = 0; i < certificates.length; ++i)
+ {
+ certificates[i].checkValidity();
+ }
+ }
+
+ public X509Certificate[] getAcceptedIssuers()
+ {
+ return null;
+ }
+}
diff --git a/app/src/main/java/org/apache/commons/net/ftp/parser/CompositeFileEntryParser.java b/app/src/main/java/org/apache/commons/net/ftp/parser/CompositeFileEntryParser.java
new file mode 100644
index 0000000..4977b3f
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/ftp/parser/CompositeFileEntryParser.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.ftp.parser;
+
+import org.apache.commons.net.ftp.FTPFile;
+import org.apache.commons.net.ftp.FTPFileEntryParser;
+import org.apache.commons.net.ftp.FTPFileEntryParserImpl;
+
+/**
+ * This implementation allows to pack some FileEntryParsers together
+ * and handle the case where to returned dirstyle isnt clearly defined.
+ * The matching parser will be cached.
+ * If the cached parser wont match due to the server changed the dirstyle,
+ * a new matching parser will be searched.
+ *
+ * @author Mario Ivankovits <mario@ops.co.at>
+ */
+public class CompositeFileEntryParser extends FTPFileEntryParserImpl
+{
+ private final FTPFileEntryParser[] ftpFileEntryParsers;
+ private FTPFileEntryParser cachedFtpFileEntryParser;
+
+ public CompositeFileEntryParser(FTPFileEntryParser[] ftpFileEntryParsers)
+ {
+ super();
+
+ this.cachedFtpFileEntryParser = null;
+ this.ftpFileEntryParsers = ftpFileEntryParsers;
+ }
+
+ public FTPFile parseFTPEntry(String listEntry)
+ {
+ if (cachedFtpFileEntryParser != null)
+ {
+ FTPFile matched = cachedFtpFileEntryParser.parseFTPEntry(listEntry);
+ if (matched != null)
+ {
+ return matched;
+ }
+ }
+ else
+ {
+ for (int iterParser=0; iterParser < ftpFileEntryParsers.length; iterParser++)
+ {
+ FTPFileEntryParser ftpFileEntryParser = ftpFileEntryParsers[iterParser];
+
+ FTPFile matched = ftpFileEntryParser.parseFTPEntry(listEntry);
+ if (matched != null)
+ {
+ cachedFtpFileEntryParser = ftpFileEntryParser;
+ return matched;
+ }
+ }
+ }
+ return null;
+ }
+}
diff --git a/app/src/main/java/org/apache/commons/net/ftp/parser/ConfigurableFTPFileEntryParserImpl.java b/app/src/main/java/org/apache/commons/net/ftp/parser/ConfigurableFTPFileEntryParserImpl.java
new file mode 100644
index 0000000..f83c878
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/ftp/parser/ConfigurableFTPFileEntryParserImpl.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.ftp.parser;
+
+import org.apache.commons.net.ftp.Configurable;
+import org.apache.commons.net.ftp.FTPClientConfig;
+
+import java.text.ParseException;
+import java.util.Calendar;
+
+
+/**
+ * <p>
+ * This abstract class implements the common timestamp parsing
+ * algorithm for all the concrete parsers. Classes derived from
+ * this one will parse file listings via a supplied regular expression
+ * that pulls out the date portion as a separate string which is
+ * passed to the underlying {@link FTPTimestampParser delegate} to
+ * handle parsing of the file timestamp.
+ * </p><p>
+ * This class also implements the {@link Configurable Configurable}
+ * interface to allow the parser to be configured from the outside.
+ * </p>
+ * @since 1.4
+ */
+/**
+ * To change the template for this generated type comment go to
+ * Window - Preferences - Java - Code Style - Code Templates - Comments
+ */
+public abstract class ConfigurableFTPFileEntryParserImpl
+extends RegexFTPFileEntryParserImpl
+implements Configurable
+{
+
+ private FTPTimestampParser timestampParser;
+
+ /**
+ * Only constructor for this absract class.
+ * @param regex Regular expression used main parsing of the
+ * file listing.
+ */
+ public ConfigurableFTPFileEntryParserImpl(String regex)
+ {
+ super(regex);
+ this.timestampParser = new FTPTimestampParserImpl();
+ }
+
+ /**
+ * This method is called by the concrete parsers to delegate
+ * timestamp parsing to the timestamp parser.
+ * <p>
+ * @param timestampStr the timestamp string pulled from the
+ * file listing by the regular expression parser, to be submitted
+ * to the <code>timestampParser</code> for extracting the timestamp.
+ * @return a <code>java.util.Calendar</code> containing results of the
+ * timestamp parse.
+ */
+ public Calendar parseTimestamp(String timestampStr) throws ParseException {
+ return this.timestampParser.parseTimestamp(timestampStr);
+ }
+
+
+ /**
+ * Implementation of the {@link Configurable Configurable}
+ * interface. Configures this parser by delegating to the
+ * underlying Configurable FTPTimestampParser implementation, '
+ * passing it the supplied {@link FTPClientConfig FTPClientConfig}
+ * if that is non-null or a default configuration defined by
+ * each concrete subclass.
+ * </p>
+ * @param config the configuration to be used to configure this parser.
+ * If it is null, a default configuration defined by
+ * each concrete subclass is used instead.
+ */
+ public void configure(FTPClientConfig config)
+ {
+ if (this.timestampParser instanceof Configurable) {
+ FTPClientConfig defaultCfg = getDefaultConfiguration();
+ if (config != null) {
+ if (null == config.getDefaultDateFormatStr()) {
+ config.setDefaultDateFormatStr(defaultCfg.getDefaultDateFormatStr());
+ }
+ if (null == config.getRecentDateFormatStr()) {
+ config.setRecentDateFormatStr(defaultCfg.getRecentDateFormatStr());
+ }
+ ((Configurable)this.timestampParser).configure(config);
+ } else {
+ ((Configurable)this.timestampParser).configure(defaultCfg);
+ }
+ }
+ }
+
+ /**
+ * Each concrete subclass must define this member to create
+ * a default configuration to be used when that subclass is
+ * instantiated without a {@link FTPClientConfig FTPClientConfig}
+ * parameter being specified.
+ * @return the default configuration for the subclass.
+ */
+ protected abstract FTPClientConfig getDefaultConfiguration();
+}
diff --git a/app/src/main/java/org/apache/commons/net/ftp/parser/DefaultFTPFileEntryParserFactory.java b/app/src/main/java/org/apache/commons/net/ftp/parser/DefaultFTPFileEntryParserFactory.java
new file mode 100644
index 0000000..c071a8e
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/ftp/parser/DefaultFTPFileEntryParserFactory.java
@@ -0,0 +1,247 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.ftp.parser;
+
+import org.apache.commons.net.ftp.Configurable;
+import org.apache.commons.net.ftp.FTPClientConfig;
+import org.apache.commons.net.ftp.FTPFileEntryParser;
+
+
+/**
+ * This is the default implementation of the
+ * FTPFileEntryParserFactory interface. This is the
+ * implementation that will be used by
+ * org.apache.commons.net.ftp.FTPClient.listFiles()
+ * if no other implementation has been specified.
+ *
+ * @see org.apache.commons.net.ftp.FTPClient#listFiles
+ * @see org.apache.commons.net.ftp.FTPClient#setParserFactory
+ */
+public class DefaultFTPFileEntryParserFactory
+ implements FTPFileEntryParserFactory
+{
+ private FTPClientConfig config = null;
+
+ /**
+ * This default implementation of the FTPFileEntryParserFactory
+ * interface works according to the following logic:
+ * First it attempts to interpret the supplied key as a fully
+ * qualified classname of a class implementing the
+ * FTPFileEntryParser interface. If that succeeds, a parser
+ * object of this class is instantiated and is returned;
+ * otherwise it attempts to interpret the key as an identirier
+ * commonly used by the FTP SYST command to identify systems.
+ * <p/>
+ * If <code>key</code> is not recognized as a fully qualified
+ * classname known to the system, this method will then attempt
+ * to see whether it <b>contains</b> a string identifying one of
+ * the known parsers. This comparison is <b>case-insensitive</b>.
+ * The intent here is where possible, to select as keys strings
+ * which are returned by the SYST command on the systems which
+ * the corresponding parser successfully parses. This enables
+ * this factory to be used in the auto-detection system.
+ * <p/>
+ *
+ * @param key should be a fully qualified classname corresponding to
+ * a class implementing the FTPFileEntryParser interface<br/>
+ * OR<br/>
+ * a string containing (case-insensitively) one of the
+ * following keywords:
+ * <ul>
+ * <li>{@link FTPClientConfig#SYST_UNIX UNIX}</li>
+ * <li>{@link FTPClientConfig#SYST_NT WINDOWS}</li>
+ * <li>{@link FTPClientConfig#SYST_OS2 OS/2}</li>
+ * <li>{@link FTPClientConfig#SYST_OS400 OS/400}</li>
+ * <li>{@link FTPClientConfig#SYST_VMS VMS}</li>
+ * <li>{@link FTPClientConfig#SYST_MVS MVS}</li>
+ * <li>{@link FTPClientConfig#SYST_NETWARE}</li>
+ * </ul>
+ * @return the FTPFileEntryParser corresponding to the supplied key.
+ * @throws ParserInitializationException thrown if for any reason the factory cannot resolve
+ * the supplied key into an FTPFileEntryParser.
+ * @see FTPFileEntryParser
+ */
+ public FTPFileEntryParser createFileEntryParser(String key)
+ {
+ if (key == null)
+ throw new ParserInitializationException("Parser key cannot be null");
+
+ Class<?> parserClass = null;
+ FTPFileEntryParser parser = null;
+ try
+ {
+ parserClass = Class.forName(key);
+ parser = (FTPFileEntryParser) parserClass.newInstance();
+ }
+ catch (ClassNotFoundException e)
+ {
+ try
+ {
+ String ukey = null;
+ if (null != key)
+ {
+ ukey = key.toUpperCase(java.util.Locale.ENGLISH);
+ }
+ if ((ukey.indexOf(FTPClientConfig.SYST_UNIX) >= 0)
+ || (ukey.indexOf(FTPClientConfig.SYST_L8) >= 0))
+ {
+ parser = createUnixFTPEntryParser();
+ }
+ else if (ukey.indexOf(FTPClientConfig.SYST_VMS) >= 0)
+ {
+ parser = createVMSVersioningFTPEntryParser();
+ }
+ else if (ukey.indexOf(FTPClientConfig.SYST_NT) >= 0)
+ {
+ parser = createNTFTPEntryParser();
+ }
+ else if (ukey.indexOf(FTPClientConfig.SYST_OS2) >= 0)
+ {
+ parser = createOS2FTPEntryParser();
+ }
+ else if (ukey.indexOf(FTPClientConfig.SYST_OS400) >= 0 ||
+ ukey.indexOf(FTPClientConfig.SYST_AS400) >= 0)
+ {
+ parser = createOS400FTPEntryParser();
+ }
+ else if (ukey.indexOf(FTPClientConfig.SYST_MVS) >= 0)
+ {
+ parser = createMVSEntryParser();
+ }
+ else if (ukey.indexOf(FTPClientConfig.SYST_NETWARE) >= 0)
+ {
+ parser = createNetwareFTPEntryParser();
+ }
+ else
+ {
+ throw new ParserInitializationException("Unknown parser type: " + key);
+ }
+ }
+ catch (NoClassDefFoundError nf) {
+ throw new ParserInitializationException("Error initializing parser", nf);
+ }
+
+ }
+ catch (NoClassDefFoundError e)
+ {
+ throw new ParserInitializationException("Error initializing parser", e);
+ }
+ catch (ClassCastException e)
+ {
+ throw new ParserInitializationException(parserClass.getName()
+ + " does not implement the interface "
+ + "org.apache.commons.net.ftp.FTPFileEntryParser.", e);
+ }
+ catch (Throwable e)
+ {
+ throw new ParserInitializationException("Error initializing parser", e);
+ }
+
+ if (parser instanceof Configurable) {
+ ((Configurable)parser).configure(this.config);
+ }
+ return parser;
+ }
+
+ /**
+ * <p>Implementation extracts a key from the supplied
+ * {@link FTPClientConfig FTPClientConfig}
+ * parameter and creates an object implementing the
+ * interface FTPFileEntryParser and uses the supplied configuration
+ * to configure it.
+ * </p><p>
+ * Note that this method will generally not be called in scenarios
+ * that call for autodetection of parser type but rather, for situations
+ * where the user knows that the server uses a non-default configuration
+ * and knows what that configuration is.
+ * </p>
+ * @param config A {@link FTPClientConfig FTPClientConfig}
+ * used to configure the parser created
+ *
+ * @return the @link FTPFileEntryParser FTPFileEntryParser} so created.
+ * @exception ParserInitializationException
+ * Thrown on any exception in instantiation
+ * @since 1.4
+ */
+ public FTPFileEntryParser createFileEntryParser(FTPClientConfig config)
+ throws ParserInitializationException
+ {
+ this.config = config;
+ String key = config.getServerSystemKey();
+ return createFileEntryParser(key);
+ }
+
+
+ public FTPFileEntryParser createUnixFTPEntryParser()
+ {
+ return new UnixFTPEntryParser();
+ }
+
+ public FTPFileEntryParser createVMSVersioningFTPEntryParser()
+ {
+ return new VMSVersioningFTPEntryParser();
+ }
+
+ public FTPFileEntryParser createNetwareFTPEntryParser() {
+ return new NetwareFTPEntryParser();
+ }
+
+ public FTPFileEntryParser createNTFTPEntryParser()
+ {
+ if (config != null && FTPClientConfig.SYST_NT.equals(
+ config.getServerSystemKey()))
+ {
+ return new NTFTPEntryParser();
+ } else {
+ return new CompositeFileEntryParser(new FTPFileEntryParser[]
+ {
+ new NTFTPEntryParser(),
+ new UnixFTPEntryParser()
+ });
+ }
+ }
+
+ public FTPFileEntryParser createOS2FTPEntryParser()
+ {
+ return new OS2FTPEntryParser();
+ }
+
+ public FTPFileEntryParser createOS400FTPEntryParser()
+ {
+ if (config != null &&
+ FTPClientConfig.SYST_OS400.equals(config.getServerSystemKey()))
+ {
+ return new OS400FTPEntryParser();
+ } else {
+ return new CompositeFileEntryParser(new FTPFileEntryParser[]
+ {
+ new OS400FTPEntryParser(),
+ new UnixFTPEntryParser()
+ });
+ }
+ }
+
+ public FTPFileEntryParser createMVSEntryParser()
+ {
+ return new MVSFTPEntryParser();
+ }
+
+
+
+}
+
diff --git a/app/src/main/java/org/apache/commons/net/ftp/parser/EnterpriseUnixFTPEntryParser.java b/app/src/main/java/org/apache/commons/net/ftp/parser/EnterpriseUnixFTPEntryParser.java
new file mode 100644
index 0000000..89c3356
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/ftp/parser/EnterpriseUnixFTPEntryParser.java
@@ -0,0 +1,167 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.ftp.parser;
+
+import org.apache.commons.net.ftp.FTPFile;
+
+import java.util.Calendar;
+
+/**
+ * Parser for the Connect Enterprise Unix FTP Server From Sterling Commerce.
+ * Here is a sample of the sort of output line this parser processes:
+ * "-C--E-----FTP B QUA1I1 18128 41 Aug 12 13:56 QUADTEST"
+ * <P><B>
+ * Note: EnterpriseUnixFTPEntryParser can only be instantiated through the
+ * DefaultFTPParserFactory by classname. It will not be chosen
+ * by the autodetection scheme.
+ * </B>
+ * @version $Id: EnterpriseUnixFTPEntryParser.java 658518 2008-05-21 01:04:30Z sebb $
+ * @author <a href="Winston.Ojeda@qg.com">Winston Ojeda</a>
+ * @see org.apache.commons.net.ftp.FTPFileEntryParser FTPFileEntryParser (for usage instructions)
+ * @see org.apache.commons.net.ftp.parser.DefaultFTPFileEntryParserFactory
+ */
+public class EnterpriseUnixFTPEntryParser extends RegexFTPFileEntryParserImpl
+{
+
+ /**
+ * months abbreviations looked for by this parser. Also used
+ * to determine <b>which</b> month has been matched by the parser.
+ */
+ private static final String MONTHS =
+ "(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)";
+
+ /**
+ * this is the regular expression used by this parser.
+ */
+ private static final String REGEX =
+ "(([\\-]|[A-Z])([\\-]|[A-Z])([\\-]|[A-Z])([\\-]|[A-Z])([\\-]|[A-Z])"
+ + "([\\-]|[A-Z])([\\-]|[A-Z])([\\-]|[A-Z])([\\-]|[A-Z])([\\-]|[A-Z]))"
+ + "(\\S*)\\s*"
+ + "(\\S+)\\s*"
+ + "(\\S*)\\s*"
+ + "(\\d*)\\s*"
+ + "(\\d*)\\s*"
+ + MONTHS
+ + "\\s*"
+ + "((?:[012]\\d*)|(?:3[01]))\\s*"
+ + "((\\d\\d\\d\\d)|((?:[01]\\d)|(?:2[0123])):([012345]\\d))\\s"
+ + "(\\S*)(\\s*.*)";
+
+ /**
+ * The sole constructor for a EnterpriseUnixFTPEntryParser object.
+ *
+ */
+ public EnterpriseUnixFTPEntryParser()
+ {
+ super(REGEX);
+ }
+
+ /**
+ * Parses a line of a unix FTP server file listing and converts it into a
+ * usable format in the form of an <code> FTPFile </code> instance. If
+ * the file listing line doesn't describe a file, <code> null </code> is
+ * returned, otherwise a <code> FTPFile </code> instance representing the
+ * files in the directory is returned.
+ *
+ * @param entry A line of text from the file listing
+ * @return An FTPFile instance corresponding to the supplied entry
+ */
+ public FTPFile parseFTPEntry(String entry)
+ {
+
+ FTPFile file = new FTPFile();
+ file.setRawListing(entry);
+
+ if (matches(entry))
+ {
+ String usr = group(14);
+ String grp = group(15);
+ String filesize = group(16);
+ String mo = group(17);
+ String da = group(18);
+ String yr = group(20);
+ String hr = group(21);
+ String min = group(22);
+ String name = group(23);
+
+ file.setType(FTPFile.FILE_TYPE);
+ file.setUser(usr);
+ file.setGroup(grp);
+ try
+ {
+ file.setSize(Long.parseLong(filesize));
+ }
+ catch (NumberFormatException e)
+ {
+ // intentionally do nothing
+ }
+
+ Calendar cal = Calendar.getInstance();
+ cal.set(Calendar.MILLISECOND, 0);
+ cal.set(Calendar.SECOND,
+ 0);
+ cal.set(Calendar.MINUTE,
+ 0);
+ cal.set(Calendar.HOUR_OF_DAY,
+ 0);
+ try
+ {
+
+ int pos = MONTHS.indexOf(mo);
+ int month = pos / 4;
+ if (yr != null)
+ {
+ // it's a year
+ cal.set(Calendar.YEAR,
+ Integer.parseInt(yr));
+ }
+ else
+ {
+ // it must be hour/minute or we wouldn't have matched
+ int year = cal.get(Calendar.YEAR);
+
+ // if the month we're reading is greater than now, it must
+ // be last year
+ if (cal.get(Calendar.MONTH) < month)
+ {
+ year--;
+ }
+ cal.set(Calendar.YEAR,
+ year);
+ cal.set(Calendar.HOUR_OF_DAY,
+ Integer.parseInt(hr));
+ cal.set(Calendar.MINUTE,
+ Integer.parseInt(min));
+ }
+ cal.set(Calendar.MONTH,
+ month);
+ cal.set(Calendar.DATE,
+ Integer.parseInt(da));
+ file.setTimestamp(cal);
+ }
+ catch (NumberFormatException e)
+ {
+ // do nothing, date will be uninitialized
+ }
+ file.setName(name);
+
+ return file;
+ }
+ return null;
+ }
+}
diff --git a/app/src/main/java/org/apache/commons/net/ftp/parser/FTPFileEntryParserFactory.java b/app/src/main/java/org/apache/commons/net/ftp/parser/FTPFileEntryParserFactory.java
new file mode 100644
index 0000000..e80ec0d
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/ftp/parser/FTPFileEntryParserFactory.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.ftp.parser;
+import org.apache.commons.net.ftp.FTPClientConfig;
+import org.apache.commons.net.ftp.FTPFileEntryParser;
+
+/**
+ * The interface describes a factory for creating FTPFileEntryParsers.
+ * @since 1.2
+ */
+public interface FTPFileEntryParserFactory
+{
+ /**
+ * Implementation should be a method that decodes the
+ * supplied key and creates an object implementing the
+ * interface FTPFileEntryParser.
+ *
+ * @param key A string that somehow identifies an
+ * FTPFileEntryParser to be created.
+ *
+ * @return the FTPFileEntryParser created.
+ * @exception ParserInitializationException
+ * Thrown on any exception in instantiation
+ */
+ public FTPFileEntryParser createFileEntryParser(String key)
+ throws ParserInitializationException;
+
+ /**
+ *<p>
+ * Implementation should be a method that extracts
+ * a key from the supplied {@link FTPClientConfig FTPClientConfig}
+ * parameter and creates an object implementing the
+ * interface FTPFileEntryParser and uses the supplied configuration
+ * to configure it.
+ * </p><p>
+ * Note that this method will generally not be called in scenarios
+ * that call for autodetection of parser type but rather, for situations
+ * where the user knows that the server uses a non-default configuration
+ * and knows what that configuration is.
+ * </p>
+ *
+ * @param config A {@link FTPClientConfig FTPClientConfig}
+ * used to configure the parser created
+ *
+ * @return the @link FTPFileEntryParser FTPFileEntryParser} so created.
+ * @exception ParserInitializationException
+ * Thrown on any exception in instantiation
+ * @since 1.4
+ */
+ public FTPFileEntryParser createFileEntryParser(FTPClientConfig config)
+ throws ParserInitializationException;
+
+}
diff --git a/app/src/main/java/org/apache/commons/net/ftp/parser/FTPTimestampParser.java b/app/src/main/java/org/apache/commons/net/ftp/parser/FTPTimestampParser.java
new file mode 100644
index 0000000..40b46cb
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/ftp/parser/FTPTimestampParser.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.ftp.parser;
+
+import java.text.ParseException;
+import java.util.Calendar;
+
+/**
+ * This interface specifies the concept of parsing an FTP server's
+ * timestamp.
+ * @since 1.4
+ */
+public interface FTPTimestampParser {
+
+ /**
+ * the default default date format.
+ */
+ public static final String DEFAULT_SDF = UnixFTPEntryParser.DEFAULT_DATE_FORMAT;
+ /**
+ * the default recent date format.
+ */
+ public static final String DEFAULT_RECENT_SDF = UnixFTPEntryParser.DEFAULT_RECENT_DATE_FORMAT;
+
+ /**
+ * Parses the supplied datestamp parameter. This parameter typically would
+ * have been pulled from a longer FTP listing via the regular expression
+ * mechanism
+ * @param timestampStr - the timestamp portion of the FTP directory listing
+ * to be parsed
+ * @return a <code>java.util.Calendar</code> object initialized to the date
+ * parsed by the parser
+ * @throws ParseException if none of the parser mechanisms belonging to
+ * the implementor can parse the input.
+ */
+ public Calendar parseTimestamp(String timestampStr) throws ParseException;
+
+}
diff --git a/app/src/main/java/org/apache/commons/net/ftp/parser/FTPTimestampParserImpl.java b/app/src/main/java/org/apache/commons/net/ftp/parser/FTPTimestampParserImpl.java
new file mode 100644
index 0000000..b4214be
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/ftp/parser/FTPTimestampParserImpl.java
@@ -0,0 +1,301 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.ftp.parser;
+
+import org.apache.commons.net.ftp.Configurable;
+import org.apache.commons.net.ftp.FTPClientConfig;
+
+import java.text.DateFormatSymbols;
+import java.text.ParseException;
+import java.text.ParsePosition;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.TimeZone;
+
+/**
+ * Default implementation of the {@link FTPTimestampParser FTPTimestampParser}
+ * interface also implements the {@link org.apache.commons.net.ftp.Configurable Configurable}
+ * interface to allow the parsing to be configured from the outside.
+ *
+ * @see ConfigurableFTPFileEntryParserImpl
+ * @since 1.4
+ */
+public class FTPTimestampParserImpl implements
+ FTPTimestampParser, Configurable
+{
+
+
+ private SimpleDateFormat defaultDateFormat;
+ private SimpleDateFormat recentDateFormat;
+ private boolean lenientFutureDates = false;
+
+
+ /**
+ * The only constructor for this class.
+ */
+ public FTPTimestampParserImpl() {
+ setDefaultDateFormat(DEFAULT_SDF);
+ setRecentDateFormat(DEFAULT_RECENT_SDF);
+ }
+
+ /**
+ * Implements the one {@link FTPTimestampParser#parseTimestamp(String) method}
+ * in the {@link FTPTimestampParser FTPTimestampParser} interface
+ * according to this algorithm:
+ *
+ * If the recentDateFormat member has been defined, try to parse the
+ * supplied string with that. If that parse fails, or if the recentDateFormat
+ * member has not been defined, attempt to parse with the defaultDateFormat
+ * member. If that fails, throw a ParseException.
+ *
+ * This method allows a {@link Calendar} instance to be passed in which represents the
+ * current (system) time.
+ *
+ * @see org.apache.commons.net.ftp.parser.FTPTimestampParser#parseTimestamp(java.lang.String)
+ *
+ * @param timestampStr The timestamp to be parsed
+ */
+ public Calendar parseTimestamp(String timestampStr) throws ParseException {
+ Calendar now = Calendar.getInstance();
+ return parseTimestamp(timestampStr, now);
+ }
+
+ /**
+ * Implements the one {@link FTPTimestampParser#parseTimestamp(String) method}
+ * in the {@link FTPTimestampParser FTPTimestampParser} interface
+ * according to this algorithm:
+ *
+ * If the recentDateFormat member has been defined, try to parse the
+ * supplied string with that. If that parse fails, or if the recentDateFormat
+ * member has not been defined, attempt to parse with the defaultDateFormat
+ * member. If that fails, throw a ParseException.
+ *
+ * @see org.apache.commons.net.ftp.parser.FTPTimestampParser#parseTimestamp(java.lang.String)
+ * @param timestampStr The timestamp to be parsed
+ * @param serverTime The current time for the server
+ * @since 1.5
+ */
+ public Calendar parseTimestamp(String timestampStr, Calendar serverTime) throws ParseException {
+ Calendar now = (Calendar) serverTime.clone();// Copy this, because we may change it
+ now.setTimeZone(this.getServerTimeZone());
+ Calendar working = (Calendar) now.clone();
+ working.setTimeZone(getServerTimeZone());
+ ParsePosition pp = new ParsePosition(0);
+
+ Date parsed = null;
+ if (recentDateFormat != null) {
+ if (lenientFutureDates) {
+ // add a day to "now" so that "slop" doesn't cause a date
+ // slightly in the future to roll back a full year. (Bug 35181)
+ now.add(Calendar.DATE, 1);
+ }
+ parsed = recentDateFormat.parse(timestampStr, pp);
+ }
+ if (parsed != null && pp.getIndex() == timestampStr.length())
+ {
+ working.setTime(parsed);
+ working.set(Calendar.YEAR, now.get(Calendar.YEAR));
+
+ if (working.after(now)) {
+ working.add(Calendar.YEAR, -1);
+ }
+ } else {
+ // Temporarily add the current year to the short date time
+ // to cope with short-date leap year strings.
+ // e.g. Java's DateFormatter will assume that "Feb 29 12:00" refers to
+ // Feb 29 1970 (an invalid date) rather than a potentially valid leap year date.
+ // This is pretty bad hack to work around the deficiencies of the JDK date/time classes.
+ if (recentDateFormat != null) {
+ pp = new ParsePosition(0);
+ int year = now.get(Calendar.YEAR);
+ String timeStampStrPlusYear = timestampStr + " " + year;
+ SimpleDateFormat hackFormatter = new SimpleDateFormat(recentDateFormat.toPattern() + " yyyy",
+ recentDateFormat.getDateFormatSymbols());
+ hackFormatter.setLenient(false);
+ hackFormatter.setTimeZone(recentDateFormat.getTimeZone());
+ parsed = hackFormatter.parse(timeStampStrPlusYear, pp);
+ }
+ if (parsed != null && pp.getIndex() == timestampStr.length() + 5) {
+ working.setTime(parsed);
+ }
+ else {
+ pp = new ParsePosition(0);
+ parsed = defaultDateFormat.parse(timestampStr, pp);
+ // note, length checks are mandatory for us since
+ // SimpleDateFormat methods will succeed if less than
+ // full string is matched. They will also accept,
+ // despite "leniency" setting, a two-digit number as
+ // a valid year (e.g. 22:04 will parse as 22 A.D.)
+ // so could mistakenly confuse an hour with a year,
+ // if we don't insist on full length parsing.
+ if (parsed != null && pp.getIndex() == timestampStr.length()) {
+ working.setTime(parsed);
+ } else {
+ throw new ParseException(
+ "Timestamp could not be parsed with older or recent DateFormat",
+ pp.getIndex());
+ }
+ }
+ }
+ return working;
+ }
+
+ /**
+ * @return Returns the defaultDateFormat.
+ */
+ public SimpleDateFormat getDefaultDateFormat() {
+ return defaultDateFormat;
+ }
+ /**
+ * @return Returns the defaultDateFormat pattern string.
+ */
+ public String getDefaultDateFormatString() {
+ return defaultDateFormat.toPattern();
+ }
+ /**
+ * @param defaultDateFormat The defaultDateFormat to be set.
+ */
+ private void setDefaultDateFormat(String format) {
+ if (format != null) {
+ this.defaultDateFormat = new SimpleDateFormat(format);
+ this.defaultDateFormat.setLenient(false);
+ }
+ }
+ /**
+ * @return Returns the recentDateFormat.
+ */
+ public SimpleDateFormat getRecentDateFormat() {
+ return recentDateFormat;
+ }
+ /**
+ * @return Returns the recentDateFormat.
+ */
+ public String getRecentDateFormatString() {
+ return recentDateFormat.toPattern();
+ }
+ /**
+ * @param recentDateFormat The recentDateFormat to set.
+ */
+ private void setRecentDateFormat(String format) {
+ if (format != null) {
+ this.recentDateFormat = new SimpleDateFormat(format);
+ this.recentDateFormat.setLenient(false);
+ }
+ }
+
+ /**
+ * @return returns an array of 12 strings representing the short
+ * month names used by this parse.
+ */
+ public String[] getShortMonths() {
+ return defaultDateFormat.getDateFormatSymbols().getShortMonths();
+ }
+
+
+ /**
+ * @return Returns the serverTimeZone used by this parser.
+ */
+ public TimeZone getServerTimeZone() {
+ return this.defaultDateFormat.getTimeZone();
+ }
+ /**
+ * sets a TimeZone represented by the supplied ID string into all
+ * of the parsers used by this server.
+ * @param serverTimeZone Time Id java.util.TimeZone id used by
+ * the ftp server. If null the client's local time zone is assumed.
+ */
+ private void setServerTimeZone(String serverTimeZoneId) {
+ TimeZone serverTimeZone = TimeZone.getDefault();
+ if (serverTimeZoneId != null) {
+ serverTimeZone = TimeZone.getTimeZone(serverTimeZoneId);
+ }
+ this.defaultDateFormat.setTimeZone(serverTimeZone);
+ if (this.recentDateFormat != null) {
+ this.recentDateFormat.setTimeZone(serverTimeZone);
+ }
+ }
+
+ /**
+ * Implementation of the {@link Configurable Configurable}
+ * interface. Configures this <code>FTPTimestampParser</code> according
+ * to the following logic:
+ * <p>
+ * Set up the {@link FTPClientConfig#setDefaultDateFormatStr(java.lang.String) defaultDateFormat}
+ * and optionally the {@link FTPClientConfig#setRecentDateFormatStr(String) recentDateFormat}
+ * to values supplied in the config based on month names configured as follows:
+ * </p><p><ul>
+ * <li>If a {@link FTPClientConfig#setShortMonthNames(String) shortMonthString}
+ * has been supplied in the <code>config</code>, use that to parse parse timestamps.</li>
+ * <li>Otherwise, if a {@link FTPClientConfig#setServerLanguageCode(String) serverLanguageCode}
+ * has been supplied in the <code>config</code>, use the month names represented
+ * by that {@link FTPClientConfig#lookupDateFormatSymbols(String) language}
+ * to parse timestamps.</li>
+ * <li>otherwise use default English month names</li>
+ * </ul></p><p>
+ * Finally if a {@link org.apache.commons.net.ftp.FTPClientConfig#setServerTimeZoneId(String) serverTimeZoneId}
+ * has been supplied via the config, set that into all date formats that have
+ * been configured.
+ * </p>
+ */
+ public void configure(FTPClientConfig config) {
+ DateFormatSymbols dfs = null;
+
+ String languageCode = config.getServerLanguageCode();
+ String shortmonths = config.getShortMonthNames();
+ if (shortmonths != null) {
+ dfs = FTPClientConfig.getDateFormatSymbols(shortmonths);
+ } else if (languageCode != null) {
+ dfs = FTPClientConfig.lookupDateFormatSymbols(languageCode);
+ } else {
+ dfs = FTPClientConfig.lookupDateFormatSymbols("en");
+ }
+
+
+ String recentFormatString = config.getRecentDateFormatStr();
+ if (recentFormatString == null) {
+ this.recentDateFormat = null;
+ } else {
+ this.recentDateFormat = new SimpleDateFormat(recentFormatString, dfs);
+ this.recentDateFormat.setLenient(false);
+ }
+
+ String defaultFormatString = config.getDefaultDateFormatStr();
+ if (defaultFormatString == null) {
+ throw new IllegalArgumentException("defaultFormatString cannot be null");
+ }
+ this.defaultDateFormat = new SimpleDateFormat(defaultFormatString, dfs);
+ this.defaultDateFormat.setLenient(false);
+
+ setServerTimeZone(config.getServerTimeZoneId());
+
+ this.lenientFutureDates = config.isLenientFutureDates();
+ }
+ /**
+ * @return Returns the lenientFutureDates.
+ */
+ boolean isLenientFutureDates() {
+ return lenientFutureDates;
+ }
+ /**
+ * @param lenientFutureDates The lenientFutureDates to set.
+ */
+ void setLenientFutureDates(boolean lenientFutureDates) {
+ this.lenientFutureDates = lenientFutureDates;
+ }
+}
diff --git a/app/src/main/java/org/apache/commons/net/ftp/parser/MVSFTPEntryParser.java b/app/src/main/java/org/apache/commons/net/ftp/parser/MVSFTPEntryParser.java
new file mode 100644
index 0000000..962f560
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/ftp/parser/MVSFTPEntryParser.java
@@ -0,0 +1,495 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.ftp.parser;
+
+import org.apache.commons.net.ftp.FTPClientConfig;
+import org.apache.commons.net.ftp.FTPFile;
+
+import java.text.ParseException;
+import java.util.List;
+
+/**
+ * Implementation of FTPFileEntryParser and FTPFileListParser for IBM zOS/MVS
+ * Systems.
+ *
+ * @author <a href="henrik.sorensen@balcab.ch">Henrik Sorensen</a>
+ * @author <a href="jnadler@srcginc.com">Jeff Nadler</a>
+ * @author <a href="wnoto@openfinance.com">William Noto</a>
+ *
+ * @version $Id: MVSFTPEntryParser.java 658520 2008-05-21 01:14:11Z sebb $
+ * @see org.apache.commons.net.ftp.FTPFileEntryParser FTPFileEntryParser (for
+ * usage instructions)
+ */
+public class MVSFTPEntryParser extends ConfigurableFTPFileEntryParserImpl {
+
+ static final int UNKNOWN_LIST_TYPE = -1;
+ static final int FILE_LIST_TYPE = 0;
+ static final int MEMBER_LIST_TYPE = 1;
+ static final int UNIX_LIST_TYPE = 2;
+ static final int JES_LEVEL_1_LIST_TYPE = 3;
+ static final int JES_LEVEL_2_LIST_TYPE = 4;
+
+ private int isType = UNKNOWN_LIST_TYPE;
+
+ /**
+ * Fallback parser for Unix-style listings
+ */
+ private UnixFTPEntryParser unixFTPEntryParser;
+
+ /**
+ * Dates are ignored for file lists, but are used for member lists where
+ * possible
+ */
+ static final String DEFAULT_DATE_FORMAT = "yyyy/MM/dd HH:mm"; // 2001/09/18
+ // 13:52
+
+ /**
+ * Matches these entries: Volume Unit Referred Ext Used Recfm Lrecl BlkSz
+ * Dsorg Dsname B10142 3390 2006/03/20 2 31 F 80 80 PS MDI.OKL.WORK
+ *
+ */
+ static final String FILE_LIST_REGEX = "\\S+\\s+" + // volume
+ // ignored
+ "\\S+\\s+" + // unit - ignored
+ "\\S+\\s+" + // access date - ignored
+ "\\S+\\s+" + // extents -ignored
+ "\\S+\\s+" + // used - ignored
+ "[FV]\\S*\\s+" + // recfm - must start with F or V
+ "\\S+\\s+" + // logical record length -ignored
+ "\\S+\\s+" + // block size - ignored
+ "(PS|PO|PO-E)\\s+" + // Dataset organisation. Many exist
+ // but only support: PS, PO, PO-E
+ "(\\S+)\\s*"; // Dataset Name (file name)
+
+ /**
+ * Matches these entries: Name VV.MM Created Changed Size Init Mod Id
+ * TBSHELF 01.03 2002/09/12 2002/10/11 09:37 11 11 0 KIL001
+ */
+ static final String MEMBER_LIST_REGEX = "(\\S+)\\s+" + // name
+ "\\S+\\s+" + // version, modification (ignored)
+ "\\S+\\s+" + // create date (ignored)
+ "(\\S+)\\s+" + // modification date
+ "(\\S+)\\s+" + // modification time
+ "\\S+\\s+" + // size in lines (ignored)
+ "\\S+\\s+" + // size in lines at creation(ignored)
+ "\\S+\\s+" + // lines modified (ignored)
+ "\\S+\\s*"; // id of user who modified (ignored)
+
+ /**
+ * Matches these entries, note: no header: IBMUSER1 JOB01906 OUTPUT 3 Spool
+ * Files 012345678901234567890123456789012345678901234 1 2 3 4
+ */
+ static final String JES_LEVEL_1_LIST_REGEX = "(\\S+)\\s+" + // job
+ // name
+ // ignored
+ "(\\S+)\\s+" + // job number
+ "(\\S+)\\s+" + // job status (OUTPUT,INPUT,ACTIVE)
+ "(\\S+)\\s+" + // number of spool files
+ "(\\S+)\\s+" + // Text "Spool" ignored
+ "(\\S+)\\s*" // Text "Files" ignored
+ ;
+
+ /**
+ * JES INTERFACE LEVEL 2 parser Matches these entries: JOBNAME JOBID OWNER
+ * STATUS CLASS IBMUSER1 JOB01906 IBMUSER OUTPUT A RC=0000 3 spool files
+ * IBMUSER TSU01830 IBMUSER OUTPUT TSU ABEND=522 3 spool files
+ * 012345678901234567890123456789012345678901234 1 2 3 4
+ * 012345678901234567890123456789012345678901234567890
+ */
+
+ static final String JES_LEVEL_2_LIST_REGEX = "(\\S+)\\s+" + // job
+ // name
+ // ignored
+ "(\\S+)\\s+" + // job number
+ "(\\S+)\\s+" + // owner ignored
+ "(\\S+)\\s+" + // job status (OUTPUT,INPUT,ACTIVE) ignored
+ "(\\S+)\\s+" + // job class ignored
+ "(\\S+).*" // rest ignored
+ ;
+
+ /*
+ * ---------------------------------------------------------------------
+ * Very brief and incomplete description of the zOS/MVS-filesystem. (Note:
+ * "zOS" is the operating system on the mainframe, and is the new name for
+ * MVS)
+ *
+ * The filesystem on the mainframe does not have hierarchal structure as for
+ * example the unix filesystem. For a more comprehensive description, please
+ * refer to the IBM manuals
+ *
+ * @LINK:
+ * http://publibfp.boulder.ibm.com/cgi-bin/bookmgr/BOOKS/dgt2d440/CONTENTS
+ *
+ *
+ * Dataset names =============
+ *
+ * A dataset name consist of a number of qualifiers separated by '.', each
+ * qualifier can be at most 8 characters, and the total length of a dataset
+ * can be max 44 characters including the dots.
+ *
+ *
+ * Dataset organisation ====================
+ *
+ * A dataset represents a piece of storage allocated on one or more disks.
+ * The structure of the storage is described with the field dataset
+ * organinsation (DSORG). There are a number of dataset organisations, but
+ * only two are usable for FTP transfer.
+ *
+ * DSORG: PS: sequential, or flat file PO: partitioned dataset PO-E:
+ * extended partitioned dataset
+ *
+ * The PS file is just a flat file, as you would find it on the unix file
+ * system.
+ *
+ * The PO and PO-E files, can be compared to a single level directory
+ * structure. A PO file consist of a number of dataset members, or files if
+ * you will. It is possible to CD into the file, and to retrieve the
+ * individual members.
+ *
+ *
+ * Dataset record format =====================
+ *
+ * The physical layout of the dataset is described on the dataset itself.
+ * There are a number of record formats (RECFM), but just a few is relavant
+ * for the FTP transfer.
+ *
+ * Any one beginning with either F or V can safely used by FTP transfer. All
+ * others should only be used with great care, so this version will just
+ * ignore the other record formats. F means a fixed number of records per
+ * allocated storage, and V means a variable number of records.
+ *
+ *
+ * Other notes ===========
+ *
+ * The file system supports automatically backup and retrieval of datasets.
+ * If a file is backed up, the ftp LIST command will return: ARCIVE Not
+ * Direct Access Device KJ.IOP998.ERROR.PL.UNITTEST
+ *
+ *
+ * Implementation notes ====================
+ *
+ * Only datasets that have dsorg PS, PO or PO-E and have recfm beginning
+ * with F or V, is fully parsed.
+ *
+ * The following fields in FTPFile is used: FTPFile.Rawlisting: Always set.
+ * FTPFile.Type: DIRECTORY_TYPE or FILE_TYPE or UNKNOWN FTPFile.Name: name
+ * FTPFile.Timestamp: change time or null
+ *
+ *
+ *
+ * Additional information ======================
+ *
+ * The MVS ftp server supports a number of features via the FTP interface.
+ * The features are controlled with the FTP command quote site filetype=<SEQ|JES|DB2>
+ * SEQ is the default and used for normal file transfer JES is used to
+ * interact with the Job Entry Subsystem (JES) similar to a job scheduler
+ * DB2 is used to interact with a DB2 subsystem
+ *
+ * This parser supports SEQ and JES.
+ *
+ *
+ *
+ *
+ *
+ *
+ */
+
+ /**
+ * The sole constructor for a MVSFTPEntryParser object.
+ *
+ */
+ public MVSFTPEntryParser() {
+ super(""); // note the regex is set in preParse.
+ super.configure(null); // configure parser with default configurations
+ }
+
+ /**
+ * Parses a line of an z/OS - MVS FTP server file listing and converts it
+ * into a usable format in the form of an <code> FTPFile </code> instance.
+ * If the file listing line doesn't describe a file, then
+ * <code> null </code> is returned. Otherwise a <code> FTPFile </code>
+ * instance representing the file is returned.
+ *
+ * @param entry
+ * A line of text from the file listing
+ * @return An FTPFile instance corresponding to the supplied entry
+ */
+ public FTPFile parseFTPEntry(String entry) {
+ boolean isParsed = false;
+ FTPFile f = new FTPFile();
+
+ if (isType == FILE_LIST_TYPE)
+ isParsed = parseFileList(f, entry);
+ else if (isType == MEMBER_LIST_TYPE) {
+ isParsed = parseMemberList(f, entry);
+ if (!isParsed)
+ isParsed = parseSimpleEntry(f, entry);
+ } else if (isType == UNIX_LIST_TYPE) {
+ isParsed = parseUnixList(f, entry);
+ } else if (isType == JES_LEVEL_1_LIST_TYPE) {
+ isParsed = parseJeslevel1List(f, entry);
+ } else if (isType == JES_LEVEL_2_LIST_TYPE) {
+ isParsed = parseJeslevel2List(f, entry);
+ }
+
+ if (!isParsed)
+ f = null;
+
+ return f;
+ }
+
+ /**
+ * Parse entries representing a dataset list. Only datasets with DSORG PS or
+ * PO or PO-E and with RECFM F* or V* will be parsed.
+ *
+ * Format of ZOS/MVS file list: 1 2 3 4 5 6 7 8 9 10 Volume Unit Referred
+ * Ext Used Recfm Lrecl BlkSz Dsorg Dsname B10142 3390 2006/03/20 2 31 F 80
+ * 80 PS MDI.OKL.WORK ARCIVE Not Direct Access Device
+ * KJ.IOP998.ERROR.PL.UNITTEST B1N231 3390 2006/03/20 1 15 VB 256 27998 PO
+ * PLU B1N231 3390 2006/03/20 1 15 VB 256 27998 PO-E PLB
+ *
+ * ----------------------------------- Group within Regex [1] Volume [2]
+ * Unit [3] Referred [4] Ext: number of extents [5] Used [6] Recfm: Record
+ * format [7] Lrecl: Logical record length [8] BlkSz: Block size [9] Dsorg:
+ * Dataset organisation. Many exists but only support: PS, PO, PO-E [10]
+ * Dsname: Dataset name
+ *
+ * Note: When volume is ARCIVE, it means the dataset is stored somewhere in
+ * a tape archive. These entries is currently not supported by this parser.
+ * A null value is returned.
+ *
+ * @param file
+ * will be updated with Name, Type, Timestamp if parsed.
+ * @param entry zosDirectoryEntry
+ * @return true: entry was parsed, false: entry was not parsed.
+ */
+ private boolean parseFileList(FTPFile file, String entry) {
+ if (matches(entry)) {
+ file.setRawListing(entry);
+ String name = group(2);
+ String dsorg = group(1);
+ file.setName(name);
+
+ // DSORG
+ if ("PS".equals(dsorg)) {
+ file.setType(FTPFile.FILE_TYPE);
+ }
+ else if ("PO".equals(dsorg) || "PO-E".equals(dsorg)) {
+ // regex already ruled out anything other than PO or PO-E
+ file.setType(FTPFile.DIRECTORY_TYPE);
+ }
+ else {
+ return false;
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Parse entries within a partitioned dataset.
+ *
+ * Format of a memberlist within a PDS: 1 2 3 4 5 6 7 8 9 Name VV.MM Created
+ * Changed Size Init Mod Id TBSHELF 01.03 2002/09/12 2002/10/11 09:37 11 11
+ * 0 KIL001 TBTOOL 01.12 2002/09/12 2004/11/26 19:54 51 28 0 KIL001
+ *
+ * ------------------------------------------- [1] Name [2] VV.MM: Version .
+ * modification [3] Created: yyyy / MM / dd [4,5] Changed: yyyy / MM / dd
+ * HH:mm [6] Size: number of lines [7] Init: number of lines when first
+ * created [8] Mod: number of modified lines a last save [9] Id: User id for
+ * last update
+ *
+ *
+ * @param file
+ * will be updated with Name, Type and Timestamp if parsed.
+ * @param entry zosDirectoryEntry
+ * @return true: entry was parsed, false: entry was not parsed.
+ */
+ private boolean parseMemberList(FTPFile file, String entry) {
+ if (matches(entry)) {
+ file.setRawListing(entry);
+ String name = group(1);
+ String datestr = group(2) + " " + group(3);
+ file.setName(name);
+ file.setType(FTPFile.FILE_TYPE);
+ try {
+ file.setTimestamp(super.parseTimestamp(datestr));
+ } catch (ParseException e) {
+ e.printStackTrace();
+ // just ignore parsing errors.
+ // TODO check this is ok
+ return false; // this is a parsing failure too.
+ }
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Assigns the name to the first word of the entry. Only to be used from a
+ * safe context, for example from a memberlist, where the regex for some
+ * reason fails. Then just assign the name field of FTPFile.
+ *
+ * @param file
+ * @param entry
+ * @return
+ */
+ private boolean parseSimpleEntry(FTPFile file, String entry) {
+ if (entry != null && entry.length() > 0) {
+ file.setRawListing(entry);
+ String name = entry.split(" ")[0];
+ file.setName(name);
+ file.setType(FTPFile.FILE_TYPE);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Parse the entry as a standard unix file. Using the UnixFTPEntryParser.
+ *
+ * @param file
+ * @param entry
+ * @return true: entry is parsed, false: entry could not be parsed.
+ */
+ private boolean parseUnixList(FTPFile file, String entry) {
+ file = unixFTPEntryParser.parseFTPEntry(entry);
+ if (file == null)
+ return false;
+ return true;
+ }
+
+ /**
+ * Matches these entries, note: no header: [1] [2] [3] [4] [5] IBMUSER1
+ * JOB01906 OUTPUT 3 Spool Files
+ * 012345678901234567890123456789012345678901234 1 2 3 4
+ * ------------------------------------------- Group in regex [1] Job name
+ * [2] Job number [3] Job status (INPUT,ACTIVE,OUTPUT) [4] Number of sysout
+ * files [5] The string "Spool Files"
+ *
+ *
+ * @param file
+ * will be updated with Name, Type and Timestamp if parsed.
+ * @param entry zosDirectoryEntry
+ * @return true: entry was parsed, false: entry was not parsed.
+ */
+ private boolean parseJeslevel1List(FTPFile file, String entry) {
+ if (matches(entry)) {
+ if (group(3).equalsIgnoreCase("OUTPUT")) {
+ file.setRawListing(entry);
+ String name = group(2); /* Job Number, used by GET */
+ file.setName(name);
+ file.setType(FTPFile.FILE_TYPE);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Matches these entries, note: no header: [1] [2] [3] [4] [5] JOBNAME JOBID
+ * OWNER STATUS CLASS IBMUSER1 JOB01906 IBMUSER OUTPUT A RC=0000 3 spool
+ * files IBMUSER TSU01830 IBMUSER OUTPUT TSU ABEND=522 3 spool files
+ * 012345678901234567890123456789012345678901234 1 2 3 4
+ * ------------------------------------------- Group in regex [1] Job name
+ * [2] Job number [3] Owner [4] Job status (INPUT,ACTIVE,OUTPUT) [5] Job
+ * Class [6] The rest
+ *
+ *
+ * @param file
+ * will be updated with Name, Type and Timestamp if parsed.
+ * @param entry zosDirectoryEntry
+ * @return true: entry was parsed, false: entry was not parsed.
+ */
+ private boolean parseJeslevel2List(FTPFile file, String entry) {
+ if (matches(entry)) {
+ if (group(4).equalsIgnoreCase("OUTPUT")) {
+ file.setRawListing(entry);
+ String name = group(2); /* Job Number, used by GET */
+ file.setName(name);
+ file.setType(FTPFile.FILE_TYPE);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * preParse is called as part of the interface. Per definition is is called
+ * before the parsing takes place. Three kind of lists is recognize:
+ * z/OS-MVS File lists z/OS-MVS Member lists unix file lists
+ * @since 2.0
+ */
+ @Override
+ public List<String> preParse(List<String> orig) {
+ // simply remove the header line. Composite logic will take care of the
+ // two different types of
+ // list in short order.
+ if (orig != null && orig.size() > 0) {
+ String header = orig.get(0);
+ if (header.indexOf("Volume") >= 0 && header.indexOf("Dsname") >= 0) {
+ setType(FILE_LIST_TYPE);
+ super.setRegex(FILE_LIST_REGEX);
+ } else if (header.indexOf("Name") >= 0 && header.indexOf("Id") >= 0) {
+ setType(MEMBER_LIST_TYPE);
+ super.setRegex(MEMBER_LIST_REGEX);
+ } else if (header.indexOf("total") == 0) {
+ setType(UNIX_LIST_TYPE);
+ unixFTPEntryParser = new UnixFTPEntryParser();
+ } else if (header.indexOf("Spool Files") >= 30) {
+ setType(JES_LEVEL_1_LIST_TYPE);
+ super.setRegex(JES_LEVEL_1_LIST_REGEX);
+ } else if (header.indexOf("JOBNAME") == 0
+ && header.indexOf("JOBID") > 8) {// header contains JOBNAME JOBID OWNER // STATUS CLASS
+ setType(JES_LEVEL_2_LIST_TYPE);
+ super.setRegex(JES_LEVEL_2_LIST_REGEX);
+ } else {
+ setType(UNKNOWN_LIST_TYPE);
+ }
+
+ if (isType != JES_LEVEL_1_LIST_TYPE) { // remove header is necessary
+ orig.remove(0);
+ }
+ }
+
+ return orig;
+ }
+
+ /**
+ * Explicitly set the type of listing being processed.
+ * @param type The listing type.
+ */
+ void setType(int type) {
+ isType = type;
+ }
+
+ /*
+ * @return
+ */
+ @Override
+ protected FTPClientConfig getDefaultConfiguration() {
+ return new FTPClientConfig(FTPClientConfig.SYST_MVS,
+ DEFAULT_DATE_FORMAT, null, null, null, null);
+ }
+
+}
diff --git a/app/src/main/java/org/apache/commons/net/ftp/parser/NTFTPEntryParser.java b/app/src/main/java/org/apache/commons/net/ftp/parser/NTFTPEntryParser.java
new file mode 100644
index 0000000..552d1e5
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/ftp/parser/NTFTPEntryParser.java
@@ -0,0 +1,148 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.ftp.parser;
+
+import org.apache.commons.net.ftp.FTPClientConfig;
+import org.apache.commons.net.ftp.FTPFile;
+
+import java.text.ParseException;
+
+/**
+ * Implementation of FTPFileEntryParser and FTPFileListParser for NT Systems.
+ *
+ * @author <a href="Winston.Ojeda@qg.com">Winston Ojeda</a>
+ * @author <a href="mailto:scohen@apache.org">Steve Cohen</a>
+ * @version $Id: NTFTPEntryParser.java 658518 2008-05-21 01:04:30Z sebb $
+ * @see org.apache.commons.net.ftp.FTPFileEntryParser FTPFileEntryParser (for usage instructions)
+ */
+public class NTFTPEntryParser extends ConfigurableFTPFileEntryParserImpl
+{
+
+ private static final String DEFAULT_DATE_FORMAT
+ = "MM-dd-yy hh:mma"; //11-09-01 12:30PM
+
+
+ /**
+ * this is the regular expression used by this parser.
+ */
+ private static final String REGEX =
+ "(\\S+)\\s+(\\S+)\\s+"
+ + "(?:(<DIR>)|([0-9]+))\\s+"
+ + "(\\S.*)";
+
+ /**
+ * The sole constructor for an NTFTPEntryParser object.
+ *
+ * @exception IllegalArgumentException
+ * Thrown if the regular expression is unparseable. Should not be seen
+ * under normal conditions. It it is seen, this is a sign that
+ * <code>REGEX</code> is not a valid regular expression.
+ */
+ public NTFTPEntryParser()
+ {
+ this(null);
+ }
+
+ /**
+ * This constructor allows the creation of an NTFTPEntryParser object
+ * with something other than the default configuration.
+ *
+ * @param config The {@link FTPClientConfig configuration} object used to
+ * configure this parser.
+ * @exception IllegalArgumentException
+ * Thrown if the regular expression is unparseable. Should not be seen
+ * under normal conditions. It it is seen, this is a sign that
+ * <code>REGEX</code> is not a valid regular expression.
+ * @since 1.4
+ */
+ public NTFTPEntryParser(FTPClientConfig config)
+ {
+ super(REGEX);
+ configure(config);
+ }
+
+ /**
+ * Parses a line of an NT FTP server file listing and converts it into a
+ * usable format in the form of an <code> FTPFile </code> instance. If the
+ * file listing line doesn't describe a file, <code> null </code> is
+ * returned, otherwise a <code> FTPFile </code> instance representing the
+ * files in the directory is returned.
+ * <p>
+ * @param entry A line of text from the file listing
+ * @return An FTPFile instance corresponding to the supplied entry
+ */
+ public FTPFile parseFTPEntry(String entry)
+ {
+ FTPFile f = new FTPFile();
+ f.setRawListing(entry);
+
+ if (matches(entry))
+ {
+ String datestr = group(1)+" "+group(2);
+ String dirString = group(3);
+ String size = group(4);
+ String name = group(5);
+ try
+ {
+ f.setTimestamp(super.parseTimestamp(datestr));
+ }
+ catch (ParseException e)
+ {
+ // intentionally do nothing
+ }
+
+ if (null == name || name.equals(".") || name.equals(".."))
+ {
+ return (null);
+ }
+ f.setName(name);
+
+
+ if ("<DIR>".equals(dirString))
+ {
+ f.setType(FTPFile.DIRECTORY_TYPE);
+ f.setSize(0);
+ }
+ else
+ {
+ f.setType(FTPFile.FILE_TYPE);
+ if (null != size)
+ {
+ f.setSize(Long.parseLong(size));
+ }
+ }
+ return (f);
+ }
+ return null;
+ }
+
+ /**
+ * Defines a default configuration to be used when this class is
+ * instantiated without a {@link FTPClientConfig FTPClientConfig}
+ * parameter being specified.
+ * @return the default configuration for this parser.
+ */
+ @Override
+ public FTPClientConfig getDefaultConfiguration() {
+ return new FTPClientConfig(
+ FTPClientConfig.SYST_NT,
+ DEFAULT_DATE_FORMAT,
+ null, null, null, null);
+ }
+
+}
diff --git a/app/src/main/java/org/apache/commons/net/ftp/parser/NetwareFTPEntryParser.java b/app/src/main/java/org/apache/commons/net/ftp/parser/NetwareFTPEntryParser.java
new file mode 100644
index 0000000..66f711f
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/ftp/parser/NetwareFTPEntryParser.java
@@ -0,0 +1,176 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.ftp.parser;
+
+import org.apache.commons.net.ftp.FTPClientConfig;
+import org.apache.commons.net.ftp.FTPFile;
+
+import java.text.ParseException;
+
+/**
+ * Implementation of FTPFileEntryParser and FTPFileListParser for Netware Systems. Note that some of the proprietary
+ * extensions for Novell-specific operations are not supported. See
+ * <a href="http://www.novell.com/documentation/nw65/index.html?page=/documentation/nw65/ftp_enu/data/fbhbgcfa.html">http://www.novell.com/documentation/nw65/index.html?page=/documentation/nw65/ftp_enu/data/fbhbgcfa.html</a>
+ * for more details.
+ *
+ * @author <a href="rwinston@apache.org">Rory Winston</a>
+ * @see org.apache.commons.net.ftp.FTPFileEntryParser FTPFileEntryParser (for usage instructions)
+ * @version $Id: NetwareFTPEntryParser.java 658520 2008-05-21 01:14:11Z sebb $
+ * @since 1.5
+ */
+public class NetwareFTPEntryParser extends ConfigurableFTPFileEntryParserImpl {
+
+ /**
+ * Default date format is e.g. Feb 22 2006
+ */
+ private static final String DEFAULT_DATE_FORMAT = "MMM dd yyyy";
+
+ /**
+ * Default recent date format is e.g. Feb 22 17:32
+ */
+ private static final String DEFAULT_RECENT_DATE_FORMAT = "MMM dd HH:mm";
+
+ /**
+ * this is the regular expression used by this parser.
+ * Example: d [-W---F--] SCION_VOL2 512 Apr 13 23:12 VOL2
+ */
+ private static final String REGEX = "(d|-){1}\\s+" // Directory/file flag
+ + "\\[(.*)\\]\\s+" // Attributes
+ + "(\\S+)\\s+" + "(\\d+)\\s+" // Owner and size
+ + "(\\S+\\s+\\S+\\s+((\\d+:\\d+)|(\\d{4})))" // Long/short date format
+ + "\\s+(.*)"; // Filename (incl. spaces)
+
+ /**
+ * The default constructor for a NetwareFTPEntryParser object.
+ *
+ * @exception IllegalArgumentException
+ * Thrown if the regular expression is unparseable. Should not be seen
+ * under normal conditions. It it is seen, this is a sign that
+ * <code>REGEX</code> is not a valid regular expression.
+ */
+ public NetwareFTPEntryParser() {
+ this(null);
+ }
+
+ /**
+ * This constructor allows the creation of an NetwareFTPEntryParser object
+ * with something other than the default configuration.
+ *
+ * @param config The {@link FTPClientConfig configuration} object used to
+ * configure this parser.
+ * @exception IllegalArgumentException
+ * Thrown if the regular expression is unparseable. Should not be seen
+ * under normal conditions. It it is seen, this is a sign that
+ * <code>REGEX</code> is not a valid regular expression.
+ * @since 1.4
+ */
+ public NetwareFTPEntryParser(FTPClientConfig config) {
+ super(REGEX);
+ configure(config);
+ }
+
+ /**
+ * Parses a line of an NetwareFTP server file listing and converts it into a
+ * usable format in the form of an <code> FTPFile </code> instance. If the
+ * file listing line doesn't describe a file, <code> null </code> is
+ * returned, otherwise a <code> FTPFile </code> instance representing the
+ * files in the directory is returned.
+ * <p>
+ * <p>
+ * Netware file permissions are in the following format: RWCEAFMS, and are explained as follows:
+ * <ul>
+ * <li><b>S</b> - Supervisor; All rights.
+ * <li><b>R</b> - Read; Right to open and read or execute.
+ * <li><b>W</b> - Write; Right to open and modify.
+ * <li><b>C</b> - Create; Right to create; when assigned to a file, allows a deleted file to be recovered.
+ * <li><b>E</b> - Erase; Right to delete.
+ * <li><b>M</b> - Modify; Right to rename a file and to change attributes.
+ * <li><b>F</b> - File Scan; Right to see directory or file listings.
+ * <li><b>A</b> - Access Control; Right to modify trustee assignments and the Inherited Rights Mask.
+ * </ul>
+ *
+ * See <a href="http://www.novell.com/documentation/nfap10/index.html?page=/documentation/nfap10/nfaubook/data/abxraws.html">here</a>
+ * for more details
+ *
+ * @param entry A line of text from the file listing
+ * @return An FTPFile instance corresponding to the supplied entry
+ */
+ public FTPFile parseFTPEntry(String entry) {
+
+ FTPFile f = new FTPFile();
+ if (matches(entry)) {
+ String dirString = group(1);
+ String attrib = group(2);
+ String user = group(3);
+ String size = group(4);
+ String datestr = group(5);
+ String name = group(9);
+
+ try {
+ f.setTimestamp(super.parseTimestamp(datestr));
+ } catch (ParseException e) {
+ // intentionally do nothing
+ }
+
+ //is it a DIR or a file
+ if (dirString.trim().equals("d")) {
+ f.setType(FTPFile.DIRECTORY_TYPE);
+ } else // Should be "-"
+ {
+ f.setType(FTPFile.FILE_TYPE);
+ }
+
+ f.setUser(user);
+
+ //set the name
+ f.setName(name.trim());
+
+ //set the size
+ f.setSize(Long.parseLong(size.trim()));
+
+ // Now set the permissions (or at least a subset thereof - full permissions would probably require
+ // subclassing FTPFile and adding extra metainformation there)
+ if (attrib.indexOf("R") != -1) {
+ f.setPermission(FTPFile.USER_ACCESS, FTPFile.READ_PERMISSION,
+ true);
+ }
+ if (attrib.indexOf("W") != -1) {
+ f.setPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION,
+ true);
+ }
+
+ return (f);
+ }
+ return null;
+
+ }
+
+ /**
+ * Defines a default configuration to be used when this class is
+ * instantiated without a {@link FTPClientConfig FTPClientConfig}
+ * parameter being specified.
+ * @return the default configuration for this parser.
+ */
+ @Override
+ protected FTPClientConfig getDefaultConfiguration() {
+ return new FTPClientConfig(FTPClientConfig.SYST_NETWARE,
+ DEFAULT_DATE_FORMAT, DEFAULT_RECENT_DATE_FORMAT, null, null,
+ null);
+ }
+
+}
diff --git a/app/src/main/java/org/apache/commons/net/ftp/parser/OS2FTPEntryParser.java b/app/src/main/java/org/apache/commons/net/ftp/parser/OS2FTPEntryParser.java
new file mode 100644
index 0000000..1ebec80
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/ftp/parser/OS2FTPEntryParser.java
@@ -0,0 +1,148 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.ftp.parser;
+
+import org.apache.commons.net.ftp.FTPClientConfig;
+import org.apache.commons.net.ftp.FTPFile;
+
+import java.text.ParseException;
+
+/**
+ * Implementation of FTPFileEntryParser and FTPFileListParser for OS2 Systems.
+ *
+ * @author <a href="Winston.Ojeda@qg.com">Winston Ojeda</a>
+ * @author <a href="mailto:scohen@apache.org">Steve Cohen</a>
+ * @version $Id: OS2FTPEntryParser.java 658518 2008-05-21 01:04:30Z sebb $
+ * @see org.apache.commons.net.ftp.FTPFileEntryParser FTPFileEntryParser (for usage instructions)
+ */
+public class OS2FTPEntryParser extends ConfigurableFTPFileEntryParserImpl
+
+{
+
+ private static final String DEFAULT_DATE_FORMAT
+ = "MM-dd-yy HH:mm"; //11-09-01 12:30
+ /**
+ * this is the regular expression used by this parser.
+ */
+ private static final String REGEX =
+ "\\s*([0-9]+)\\s*"
+ + "(\\s+|[A-Z]+)\\s*"
+ + "(DIR|\\s+)\\s*"
+ + "(\\S+)\\s+(\\S+)\\s+" /* date stuff */
+ + "(\\S.*)";
+
+ /**
+ * The default constructor for a OS2FTPEntryParser object.
+ *
+ * @exception IllegalArgumentException
+ * Thrown if the regular expression is unparseable. Should not be seen
+ * under normal conditions. It it is seen, this is a sign that
+ * <code>REGEX</code> is not a valid regular expression.
+ */
+ public OS2FTPEntryParser()
+ {
+ this(null);
+ }
+
+ /**
+ * This constructor allows the creation of an OS2FTPEntryParser object
+ * with something other than the default configuration.
+ *
+ * @param config The {@link FTPClientConfig configuration} object used to
+ * configure this parser.
+ * @exception IllegalArgumentException
+ * Thrown if the regular expression is unparseable. Should not be seen
+ * under normal conditions. It it is seen, this is a sign that
+ * <code>REGEX</code> is not a valid regular expression.
+ * @since 1.4
+ */
+ public OS2FTPEntryParser(FTPClientConfig config)
+ {
+ super(REGEX);
+ configure(config);
+ }
+
+ /**
+ * Parses a line of an OS2 FTP server file listing and converts it into a
+ * usable format in the form of an <code> FTPFile </code> instance. If the
+ * file listing line doesn't describe a file, <code> null </code> is
+ * returned, otherwise a <code> FTPFile </code> instance representing the
+ * files in the directory is returned.
+ * <p>
+ * @param entry A line of text from the file listing
+ * @return An FTPFile instance corresponding to the supplied entry
+ */
+ public FTPFile parseFTPEntry(String entry)
+ {
+
+ FTPFile f = new FTPFile();
+ if (matches(entry))
+ {
+ String size = group(1);
+ String attrib = group(2);
+ String dirString = group(3);
+ String datestr = group(4)+" "+group(5);
+ String name = group(6);
+ try
+ {
+ f.setTimestamp(super.parseTimestamp(datestr));
+ }
+ catch (ParseException e)
+ {
+ // intentionally do nothing
+ }
+
+
+ //is it a DIR or a file
+ if (dirString.trim().equals("DIR") || attrib.trim().equals("DIR"))
+ {
+ f.setType(FTPFile.DIRECTORY_TYPE);
+ }
+ else
+ {
+ f.setType(FTPFile.FILE_TYPE);
+ }
+
+
+ //set the name
+ f.setName(name.trim());
+
+ //set the size
+ f.setSize(Long.parseLong(size.trim()));
+
+ return (f);
+ }
+ return null;
+
+ }
+
+ /**
+ * Defines a default configuration to be used when this class is
+ * instantiated without a {@link FTPClientConfig FTPClientConfig}
+ * parameter being specified.
+ * @return the default configuration for this parser.
+ */
+ @Override
+ protected FTPClientConfig getDefaultConfiguration() {
+ return new FTPClientConfig(
+ FTPClientConfig.SYST_OS2,
+ DEFAULT_DATE_FORMAT,
+ null, null, null, null);
+ }
+
+}
diff --git a/app/src/main/java/org/apache/commons/net/ftp/parser/OS400FTPEntryParser.java b/app/src/main/java/org/apache/commons/net/ftp/parser/OS400FTPEntryParser.java
new file mode 100644
index 0000000..72152e0
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/ftp/parser/OS400FTPEntryParser.java
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.ftp.parser;
+
+import org.apache.commons.net.ftp.FTPClientConfig;
+import org.apache.commons.net.ftp.FTPFile;
+
+import java.text.ParseException;
+
+/**
+ * @version $Id: OS400FTPEntryParser.java 658518 2008-05-21 01:04:30Z sebb $
+ */
+
+public class OS400FTPEntryParser extends ConfigurableFTPFileEntryParserImpl
+{
+ private static final String DEFAULT_DATE_FORMAT
+ = "yy/MM/dd HH:mm:ss"; //01/11/09 12:30:24
+
+
+
+ private static final String REGEX =
+ "(\\S+)\\s+" // user
+ + "(\\d+)\\s+" // size
+ + "(\\S+)\\s+(\\S+)\\s+" // date stuff
+ + "(\\*\\S+)\\s+" // *STMF/*DIR
+ + "(\\S+/?)\\s*"; // filename
+
+
+ /**
+ * The default constructor for a OS400FTPEntryParser object.
+ *
+ * @exception IllegalArgumentException
+ * Thrown if the regular expression is unparseable. Should not be seen
+ * under normal conditions. It it is seen, this is a sign that
+ * <code>REGEX</code> is not a valid regular expression.
+ */
+ public OS400FTPEntryParser()
+ {
+ this(null);
+ }
+
+ /**
+ * This constructor allows the creation of an OS400FTPEntryParser object
+ * with something other than the default configuration.
+ *
+ * @param config The {@link FTPClientConfig configuration} object used to
+ * configure this parser.
+ * @exception IllegalArgumentException
+ * Thrown if the regular expression is unparseable. Should not be seen
+ * under normal conditions. It it is seen, this is a sign that
+ * <code>REGEX</code> is not a valid regular expression.
+ * @since 1.4
+ */
+ public OS400FTPEntryParser(FTPClientConfig config)
+ {
+ super(REGEX);
+ configure(config);
+ }
+
+
+ public FTPFile parseFTPEntry(String entry)
+ {
+
+ FTPFile file = new FTPFile();
+ file.setRawListing(entry);
+ int type;
+
+ if (matches(entry))
+ {
+ String usr = group(1);
+ String filesize = group(2);
+ String datestr = group(3)+" "+group(4);
+ String typeStr = group(5);
+ String name = group(6);
+
+ try
+ {
+ file.setTimestamp(super.parseTimestamp(datestr));
+ }
+ catch (ParseException e)
+ {
+ // intentionally do nothing
+ }
+
+
+ if (typeStr.equalsIgnoreCase("*STMF"))
+ {
+ type = FTPFile.FILE_TYPE;
+ }
+ else if (typeStr.equalsIgnoreCase("*DIR"))
+ {
+ type = FTPFile.DIRECTORY_TYPE;
+ }
+ else
+ {
+ type = FTPFile.UNKNOWN_TYPE;
+ }
+
+ file.setType(type);
+
+ file.setUser(usr);
+
+ try
+ {
+ file.setSize(Long.parseLong(filesize));
+ }
+ catch (NumberFormatException e)
+ {
+ // intentionally do nothing
+ }
+
+ if (name.endsWith("/"))
+ {
+ name = name.substring(0, name.length() - 1);
+ }
+ int pos = name.lastIndexOf('/');
+ if (pos > -1)
+ {
+ name = name.substring(pos + 1);
+ }
+
+ file.setName(name);
+
+ return file;
+ }
+ return null;
+ }
+
+ /**
+ * Defines a default configuration to be used when this class is
+ * instantiated without a {@link FTPClientConfig FTPClientConfig}
+ * parameter being specified.
+ * @return the default configuration for this parser.
+ */
+ @Override
+ protected FTPClientConfig getDefaultConfiguration() {
+ return new FTPClientConfig(
+ FTPClientConfig.SYST_OS400,
+ DEFAULT_DATE_FORMAT,
+ null, null, null, null);
+ }
+
+}
diff --git a/app/src/main/java/org/apache/commons/net/ftp/parser/ParserInitializationException.java b/app/src/main/java/org/apache/commons/net/ftp/parser/ParserInitializationException.java
new file mode 100644
index 0000000..8af9261
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/ftp/parser/ParserInitializationException.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.ftp.parser;
+
+/**
+ * This class encapsulates all errors that may be thrown by
+ * the process of an FTPFileEntryParserFactory creating and
+ * instantiating an FTPFileEntryParser.
+ */
+public class ParserInitializationException extends RuntimeException {
+
+ /**
+ * Root exception that caused this to be thrown
+ */
+ private final Throwable rootCause;
+
+ /**
+ * Constucts a ParserInitializationException with just a message
+ *
+ * @param message Exception message
+ */
+ public ParserInitializationException(String message) {
+ super(message);
+ this.rootCause = null;
+ }
+
+ /**
+ * Constucts a ParserInitializationException with a message
+ * and a root cause.
+ *
+ * @param message Exception message
+ * @param rootCause root cause throwable that caused
+ * this to be thrown
+ */
+ public ParserInitializationException(String message, Throwable rootCause) {
+ super(message);
+ this.rootCause = rootCause;
+ }
+
+ /**
+ * returns the root cause of this exception or null
+ * if no root cause was specified.
+ *
+ * @return the root cause of this exception being thrown
+ */
+ public Throwable getRootCause() {
+ return this.rootCause;
+ }
+
+}
diff --git a/app/src/main/java/org/apache/commons/net/ftp/parser/RegexFTPFileEntryParserImpl.java b/app/src/main/java/org/apache/commons/net/ftp/parser/RegexFTPFileEntryParserImpl.java
new file mode 100644
index 0000000..a196b73
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/ftp/parser/RegexFTPFileEntryParserImpl.java
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.commons.net.ftp.parser;
+
+import org.apache.commons.net.ftp.FTPFileEntryParserImpl;
+
+import java.util.regex.MatchResult;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+/**
+ * This abstract class implements both the older FTPFileListParser and
+ * newer FTPFileEntryParser interfaces with default functionality.
+ * All the classes in the parser subpackage inherit from this.
+ *
+ * This is the base for all regular based FTPFileEntryParser
+ *
+ * @author Steve Cohen <scohen@apache.org>
+ */
+public abstract class RegexFTPFileEntryParserImpl extends
+ FTPFileEntryParserImpl {
+ /**
+ * internal pattern the matcher tries to match, representing a file
+ * entry
+ */
+ private Pattern pattern = null;
+
+ /**
+ * internal match result used by the parser
+ */
+ private MatchResult result = null;
+
+ /**
+ * Internal PatternMatcher object used by the parser. It has protected
+ * scope in case subclasses want to make use of it for their own purposes.
+ */
+ protected Matcher _matcher_ = null;
+
+ /**
+ * The constructor for a RegexFTPFileEntryParserImpl object.
+ *
+ * @param regex The regular expression with which this object is
+ * initialized.
+ *
+ * @exception IllegalArgumentException
+ * Thrown if the regular expression is unparseable. Should not be seen in
+ * normal conditions. It it is seen, this is a sign that a subclass has
+ * been created with a bad regular expression. Since the parser must be
+ * created before use, this means that any bad parser subclasses created
+ * from this will bomb very quickly, leading to easy detection.
+ */
+
+ public RegexFTPFileEntryParserImpl(String regex) {
+ super();
+ setRegex(regex);
+ }
+
+ /**
+ * Convenience method delegates to the internal MatchResult's matches()
+ * method.
+ *
+ * @param s the String to be matched
+ * @return true if s matches this object's regular expression.
+ */
+
+ public boolean matches(String s) {
+ this.result = null;
+ _matcher_ = pattern.matcher(s);
+ if (_matcher_.matches()) {
+ this.result = _matcher_.toMatchResult();
+ }
+ return null != this.result;
+ }
+
+ /**
+ * Convenience method
+ *
+ * @return the number of groups() in the internal MatchResult.
+ */
+
+ public int getGroupCnt() {
+ if (this.result == null) {
+ return 0;
+ }
+ return this.result.groupCount();
+ }
+
+ /**
+ * Convenience method delegates to the internal MatchResult's group()
+ * method.
+ *
+ * @param matchnum match group number to be retrieved
+ *
+ * @return the content of the <code>matchnum'th<code> group of the internal
+ * match or null if this method is called without a match having
+ * been made.
+ */
+ public String group(int matchnum) {
+ if (this.result == null) {
+ return null;
+ }
+ return this.result.group(matchnum);
+ }
+
+ /**
+ * For debugging purposes - returns a string shows each match group by
+ * number.
+ *
+ * @return a string shows each match group by number.
+ */
+
+ public String getGroupsAsString() {
+ StringBuffer b = new StringBuffer();
+ for (int i = 1; i <= this.result.groupCount(); i++) {
+ b.append(i).append(") ").append(this.result.group(i)).append(
+ System.getProperty("line.separator"));
+ }
+ return b.toString();
+ }
+
+ /**
+ * Alter the current regular expression being utilised for entry parsing
+ * and create a new {@link Pattern} instance.
+ * @param regex The new regular expression
+ * @return
+ * @since 2.0
+ */
+ public boolean setRegex(String regex) {
+ try {
+ pattern = Pattern.compile(regex);
+ } catch (PatternSyntaxException pse) {
+ throw new IllegalArgumentException("Unparseable regex supplied: "
+ + regex);
+ }
+ return (pattern != null);
+ }
+
+}
diff --git a/app/src/main/java/org/apache/commons/net/ftp/parser/UnixFTPEntryParser.java b/app/src/main/java/org/apache/commons/net/ftp/parser/UnixFTPEntryParser.java
new file mode 100644
index 0000000..3025b1f
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/ftp/parser/UnixFTPEntryParser.java
@@ -0,0 +1,296 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.ftp.parser;
+
+import org.apache.commons.net.ftp.FTPClientConfig;
+import org.apache.commons.net.ftp.FTPFile;
+
+import java.text.ParseException;
+
+/**
+ * Implementation FTPFileEntryParser and FTPFileListParser for standard
+ * Unix Systems.
+ *
+ * This class is based on the logic of Daniel Savarese's
+ * DefaultFTPListParser, but adapted to use regular expressions and to fit the
+ * new FTPFileEntryParser interface.
+ * @version $Id: UnixFTPEntryParser.java 658518 2008-05-21 01:04:30Z sebb $
+ * @see org.apache.commons.net.ftp.FTPFileEntryParser FTPFileEntryParser (for usage instructions)
+ */
+public class UnixFTPEntryParser extends ConfigurableFTPFileEntryParserImpl
+{
+
+ static final String DEFAULT_DATE_FORMAT
+ = "MMM d yyyy"; //Nov 9 2001
+
+ static final String DEFAULT_RECENT_DATE_FORMAT
+ = "MMM d HH:mm"; //Nov 9 20:06
+
+ static final String NUMERIC_DATE_FORMAT
+ = "yyyy-MM-dd HH:mm"; //2001-11-09 20:06
+
+ /**
+ * Some Linux distributions are now shipping an FTP server which formats
+ * file listing dates in an all-numeric format:
+ * <code>"yyyy-MM-dd HH:mm</code>.
+ * This is a very welcome development, and hopefully it will soon become
+ * the standard. However, since it is so new, for now, and possibly
+ * forever, we merely accomodate it, but do not make it the default.
+ * <p>
+ * For now end users may specify this format only via
+ * <code>UnixFTPEntryParser(FTPClientConfig)</code>.
+ * Steve Cohen - 2005-04-17
+ */
+ public static final FTPClientConfig NUMERIC_DATE_CONFIG =
+ new FTPClientConfig(
+ FTPClientConfig.SYST_UNIX,
+ NUMERIC_DATE_FORMAT,
+ null, null, null, null);
+
+ /**
+ * this is the regular expression used by this parser.
+ *
+ * Permissions:
+ * r the file is readable
+ * w the file is writable
+ * x the file is executable
+ * - the indicated permission is not granted
+ * L mandatory locking occurs during access (the set-group-ID bit is
+ * on and the group execution bit is off)
+ * s the set-user-ID or set-group-ID bit is on, and the corresponding
+ * user or group execution bit is also on
+ * S undefined bit-state (the set-user-ID bit is on and the user
+ * execution bit is off)
+ * t the 1000 (octal) bit, or sticky bit, is on [see chmod(1)], and
+ * execution is on
+ * T the 1000 bit is turned on, and execution is off (undefined bit-
+ * state)
+ * e z/OS external link bit
+ */
+ private static final String REGEX =
+ "([bcdelfmpSs-])"
+ +"(((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-])))\\+?\\s+"
+ + "(\\d+)\\s+"
+ + "(?:(\\S+(?:\\s\\S+)*?)\\s+)?" // owner name (optional spaces)
+ + "(?:(\\S+(?:\\s\\S+)*)\\s+)?" // group name (optional spaces)
+ + "(\\d+(?:,\\s*\\d+)?)\\s+"
+
+ /*
+ numeric or standard format date
+ */
+ + "((?:\\d+[-/]\\d+[-/]\\d+)|(?:\\S+\\s+\\S+))\\s+"
+
+ /*
+ year (for non-recent standard format)
+ or time (for numeric or recent standard format
+ */
+ + "(\\d+(?::\\d+)?)\\s+"
+
+ + "(\\S*)(\\s*.*)";
+
+
+ /**
+ * The default constructor for a UnixFTPEntryParser object.
+ *
+ * @exception IllegalArgumentException
+ * Thrown if the regular expression is unparseable. Should not be seen
+ * under normal conditions. It it is seen, this is a sign that
+ * <code>REGEX</code> is not a valid regular expression.
+ */
+ public UnixFTPEntryParser()
+ {
+ this(null);
+ }
+
+ /**
+ * This constructor allows the creation of a UnixFTPEntryParser object with
+ * something other than the default configuration.
+ *
+ * @param config The {@link FTPClientConfig configuration} object used to
+ * configure this parser.
+ * @exception IllegalArgumentException
+ * Thrown if the regular expression is unparseable. Should not be seen
+ * under normal conditions. It it is seen, this is a sign that
+ * <code>REGEX</code> is not a valid regular expression.
+ * @since 1.4
+ */
+ public UnixFTPEntryParser(FTPClientConfig config)
+ {
+ super(REGEX);
+ configure(config);
+ }
+
+
+ /**
+ * Parses a line of a unix (standard) FTP server file listing and converts
+ * it into a usable format in the form of an <code> FTPFile </code>
+ * instance. If the file listing line doesn't describe a file,
+ * <code> null </code> is returned, otherwise a <code> FTPFile </code>
+ * instance representing the files in the directory is returned.
+ * <p>
+ * @param entry A line of text from the file listing
+ * @return An FTPFile instance corresponding to the supplied entry
+ */
+ public FTPFile parseFTPEntry(String entry) {
+ FTPFile file = new FTPFile();
+ file.setRawListing(entry);
+ int type;
+ boolean isDevice = false;
+
+ if (matches(entry))
+ {
+ String typeStr = group(1);
+ String hardLinkCount = group(15);
+ String usr = group(16);
+ String grp = group(17);
+ String filesize = group(18);
+ String datestr = group(19) + " " + group(20);
+ String name = group(21);
+ String endtoken = group(22);
+
+ try
+ {
+ file.setTimestamp(super.parseTimestamp(datestr));
+ }
+ catch (ParseException e)
+ {
+ // intentionally do nothing
+ }
+
+
+ // bcdlfmpSs-
+ switch (typeStr.charAt(0))
+ {
+ case 'd':
+ type = FTPFile.DIRECTORY_TYPE;
+ break;
+ case 'e':
+ type = FTPFile.SYMBOLIC_LINK_TYPE;
+ break;
+ case 'l':
+ type = FTPFile.SYMBOLIC_LINK_TYPE;
+ break;
+ case 'b':
+ case 'c':
+ isDevice = true;
+ // break; - fall through
+ case 'f':
+ case '-':
+ type = FTPFile.FILE_TYPE;
+ break;
+ default:
+ type = FTPFile.UNKNOWN_TYPE;
+ }
+
+ file.setType(type);
+
+ int g = 4;
+ for (int access = 0; access < 3; access++, g += 4)
+ {
+ // Use != '-' to avoid having to check for suid and sticky bits
+ file.setPermission(access, FTPFile.READ_PERMISSION,
+ (!group(g).equals("-")));
+ file.setPermission(access, FTPFile.WRITE_PERMISSION,
+ (!group(g + 1).equals("-")));
+
+ String execPerm = group(g + 2);
+ if (!execPerm.equals("-") && !Character.isUpperCase(execPerm.charAt(0)))
+ {
+ file.setPermission(access, FTPFile.EXECUTE_PERMISSION, true);
+ }
+ else
+ {
+ file.setPermission(access, FTPFile.EXECUTE_PERMISSION, false);
+ }
+ }
+
+ if (!isDevice)
+ {
+ try
+ {
+ file.setHardLinkCount(Integer.parseInt(hardLinkCount));
+ }
+ catch (NumberFormatException e)
+ {
+ // intentionally do nothing
+ }
+ }
+
+ file.setUser(usr);
+ file.setGroup(grp);
+
+ try
+ {
+ file.setSize(Long.parseLong(filesize));
+ }
+ catch (NumberFormatException e)
+ {
+ // intentionally do nothing
+ }
+
+ if (null == endtoken)
+ {
+ file.setName(name);
+ }
+ else
+ {
+ // oddball cases like symbolic links, file names
+ // with spaces in them.
+ name += endtoken;
+ if (type == FTPFile.SYMBOLIC_LINK_TYPE)
+ {
+
+ int end = name.indexOf(" -> ");
+ // Give up if no link indicator is present
+ if (end == -1)
+ {
+ file.setName(name);
+ }
+ else
+ {
+ file.setName(name.substring(0, end));
+ file.setLink(name.substring(end + 4));
+ }
+
+ }
+ else
+ {
+ file.setName(name);
+ }
+ }
+ return file;
+ }
+ return null;
+ }
+
+ /**
+ * Defines a default configuration to be used when this class is
+ * instantiated without a {@link FTPClientConfig FTPClientConfig}
+ * parameter being specified.
+ * @return the default configuration for this parser.
+ */
+ @Override
+ protected FTPClientConfig getDefaultConfiguration() {
+ return new FTPClientConfig(
+ FTPClientConfig.SYST_UNIX,
+ DEFAULT_DATE_FORMAT,
+ DEFAULT_RECENT_DATE_FORMAT,
+ null, null, null);
+ }
+
+}
diff --git a/app/src/main/java/org/apache/commons/net/ftp/parser/VMSFTPEntryParser.java b/app/src/main/java/org/apache/commons/net/ftp/parser/VMSFTPEntryParser.java
new file mode 100644
index 0000000..012dcbb
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/ftp/parser/VMSFTPEntryParser.java
@@ -0,0 +1,289 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.ftp.parser;
+
+import org.apache.commons.net.ftp.FTPClientConfig;
+import org.apache.commons.net.ftp.FTPFile;
+import org.apache.commons.net.ftp.FTPListParseEngine;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.ParseException;
+import java.util.StringTokenizer;
+
+/**
+ * Implementation FTPFileEntryParser and FTPFileListParser for VMS Systems.
+ * This is a sample of VMS LIST output
+ *
+ * "1-JUN.LIS;1 9/9 2-JUN-1998 07:32:04 [GROUP,OWNER] (RWED,RWED,RWED,RE)",
+ * "1-JUN.LIS;2 9/9 2-JUN-1998 07:32:04 [GROUP,OWNER] (RWED,RWED,RWED,RE)",
+ * "DATA.DIR;1 1/9 2-JUN-1998 07:32:04 [GROUP,OWNER] (RWED,RWED,RWED,RE)",
+ * <P><B>
+ * Note: VMSFTPEntryParser can only be instantiated through the
+ * DefaultFTPParserFactory by classname. It will not be chosen
+ * by the autodetection scheme.
+ * </B>
+ * <P>
+ *
+ * @author <a href="Winston.Ojeda@qg.com">Winston Ojeda</a>
+ * @author <a href="mailto:scohen@apache.org">Steve Cohen</a>
+ * @author <a href="sestegra@free.fr">Stephane ESTE-GRACIAS</a>
+ * @version $Id: VMSFTPEntryParser.java 658518 2008-05-21 01:04:30Z sebb $
+ *
+ * @see org.apache.commons.net.ftp.FTPFileEntryParser FTPFileEntryParser (for usage instructions)
+ * @see org.apache.commons.net.ftp.parser.DefaultFTPFileEntryParserFactory
+ */
+public class VMSFTPEntryParser extends ConfigurableFTPFileEntryParserImpl
+{
+
+ private static final String DEFAULT_DATE_FORMAT
+ = "d-MMM-yyyy HH:mm:ss"; //9-NOV-2001 12:30:24
+
+ /**
+ * this is the regular expression used by this parser.
+ */
+ private static final String REGEX =
+ "(.*;[0-9]+)\\s*" //1 file and version
+ + "(\\d+)/\\d+\\s*" //2 size/allocated
+ +"(\\S+)\\s+(\\S+)\\s+" //3+4 date and time
+ + "\\[(([0-9$A-Za-z_]+)|([0-9$A-Za-z_]+),([0-9$a-zA-Z_]+))\\]?\\s*" //5(6,7,8) owner
+ + "\\([a-zA-Z]*,([a-zA-Z]*),([a-zA-Z]*),([a-zA-Z]*)\\)"; //9,10,11 Permissions (O,G,W)
+ // TODO - perhaps restrict permissions to [RWED]* ?
+
+
+
+ /**
+ * Constructor for a VMSFTPEntryParser object.
+ *
+ * @exception IllegalArgumentException
+ * Thrown if the regular expression is unparseable. Should not be seen
+ * under normal conditions. It it is seen, this is a sign that
+ * <code>REGEX</code> is not a valid regular expression.
+ */
+ public VMSFTPEntryParser()
+ {
+ this(null);
+ }
+
+ /**
+ * This constructor allows the creation of a VMSFTPEntryParser object with
+ * something other than the default configuration.
+ *
+ * @param config The {@link FTPClientConfig configuration} object used to
+ * configure this parser.
+ * @exception IllegalArgumentException
+ * Thrown if the regular expression is unparseable. Should not be seen
+ * under normal conditions. It it is seen, this is a sign that
+ * <code>REGEX</code> is not a valid regular expression.
+ * @since 1.4
+ */
+ public VMSFTPEntryParser(FTPClientConfig config)
+ {
+ super(REGEX);
+ configure(config);
+ }
+
+
+
+ /***
+ * Parses an FTP server file listing and converts it into a usable format
+ * in the form of an array of <code> FTPFile </code> instances. If the
+ * file list contains no files, <code> null </code> should be
+ * returned, otherwise an array of <code> FTPFile </code> instances
+ * representing the files in the directory is returned.
+ * <p>
+ * @param listStream The InputStream from which the file list should be
+ * read.
+ * @return The list of file information contained in the given path. null
+ * if the list could not be obtained or if there are no files in
+ * the directory.
+ * @exception IOException If an I/O error occurs reading the listStream.
+ ***/
+ public FTPFile[] parseFileList(InputStream listStream) throws IOException {
+ FTPListParseEngine engine = new FTPListParseEngine(this);
+ engine.readServerList(listStream);
+ return engine.getFiles();
+ }
+
+
+
+ /**
+ * Parses a line of a VMS FTP server file listing and converts it into a
+ * usable format in the form of an <code> FTPFile </code> instance. If the
+ * file listing line doesn't describe a file, <code> null </code> is
+ * returned, otherwise a <code> FTPFile </code> instance representing the
+ * files in the directory is returned.
+ * <p>
+ * @param entry A line of text from the file listing
+ * @return An FTPFile instance corresponding to the supplied entry
+ */
+ public FTPFile parseFTPEntry(String entry)
+ {
+ //one block in VMS equals 512 bytes
+ long longBlock = 512;
+
+ if (matches(entry))
+ {
+ FTPFile f = new FTPFile();
+ f.setRawListing(entry);
+ String name = group(1);
+ String size = group(2);
+ String datestr = group(3)+" "+group(4);
+ String owner = group(5);
+ String permissions[] = new String[3];
+ permissions[0]= group(9);
+ permissions[1]= group(10);
+ permissions[2]= group(11);
+ try
+ {
+ f.setTimestamp(super.parseTimestamp(datestr));
+ }
+ catch (ParseException e)
+ {
+ // intentionally do nothing
+ }
+
+
+ String grp;
+ String user;
+ StringTokenizer t = new StringTokenizer(owner, ",");
+ switch (t.countTokens()) {
+ case 1:
+ grp = null;
+ user = t.nextToken();
+ break;
+ case 2:
+ grp = t.nextToken();
+ user = t.nextToken();
+ break;
+ default:
+ grp = null;
+ user = null;
+ }
+
+ if (name.lastIndexOf(".DIR") != -1)
+ {
+ f.setType(FTPFile.DIRECTORY_TYPE);
+ }
+ else
+ {
+ f.setType(FTPFile.FILE_TYPE);
+ }
+ //set FTPFile name
+ //Check also for versions to be returned or not
+ if (isVersioning())
+ {
+ f.setName(name);
+ }
+ else
+ {
+ name = name.substring(0, name.lastIndexOf(";"));
+ f.setName(name);
+ }
+ //size is retreived in blocks and needs to be put in bytes
+ //for us humans and added to the FTPFile array
+ long sizeInBytes = Long.parseLong(size) * longBlock;
+ f.setSize(sizeInBytes);
+
+ f.setGroup(grp);
+ f.setUser(user);
+ //set group and owner
+
+ //Set file permission.
+ //VMS has (SYSTEM,OWNER,GROUP,WORLD) users that can contain
+ //R (read) W (write) E (execute) D (delete)
+
+ //iterate for OWNER GROUP WORLD permissions
+ for (int access = 0; access < 3; access++)
+ {
+ String permission = permissions[access];
+
+ f.setPermission(access, FTPFile.READ_PERMISSION, permission.indexOf('R')>=0);
+ f.setPermission(access, FTPFile.WRITE_PERMISSION, permission.indexOf('W')>=0);
+ f.setPermission(access, FTPFile.EXECUTE_PERMISSION, permission.indexOf('E')>=0);
+ }
+
+ return f;
+ }
+ return null;
+ }
+
+
+ /**
+ * Reads the next entry using the supplied BufferedReader object up to
+ * whatever delemits one entry from the next. This parser cannot use
+ * the default implementation of simply calling BufferedReader.readLine(),
+ * because one entry may span multiple lines.
+ *
+ * @param reader The BufferedReader object from which entries are to be
+ * read.
+ *
+ * @return A string representing the next ftp entry or null if none found.
+ * @exception IOException thrown on any IO Error reading from the reader.
+ */
+ @Override
+ public String readNextEntry(BufferedReader reader) throws IOException
+ {
+ String line = reader.readLine();
+ StringBuffer entry = new StringBuffer();
+ while (line != null)
+ {
+ if (line.startsWith("Directory") || line.startsWith("Total")) {
+ line = reader.readLine();
+ continue;
+ }
+
+ entry.append(line);
+ if (line.trim().endsWith(")"))
+ {
+ break;
+ }
+ line = reader.readLine();
+ }
+ return (entry.length() == 0 ? null : entry.toString());
+ }
+
+ protected boolean isVersioning() {
+ return false;
+ }
+
+ /**
+ * Defines a default configuration to be used when this class is
+ * instantiated without a {@link FTPClientConfig FTPClientConfig}
+ * parameter being specified.
+ * @return the default configuration for this parser.
+ */
+ @Override
+ protected FTPClientConfig getDefaultConfiguration() {
+ return new FTPClientConfig(
+ FTPClientConfig.SYST_VMS,
+ DEFAULT_DATE_FORMAT,
+ null, null, null, null);
+ }
+
+
+}
+
+/* Emacs configuration
+ * Local variables: **
+ * mode: java **
+ * c-basic-offset: 4 **
+ * indent-tabs-mode: nil **
+ * End: **
+ */
diff --git a/app/src/main/java/org/apache/commons/net/ftp/parser/VMSVersioningFTPEntryParser.java b/app/src/main/java/org/apache/commons/net/ftp/parser/VMSVersioningFTPEntryParser.java
new file mode 100644
index 0000000..3c266e6
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/ftp/parser/VMSVersioningFTPEntryParser.java
@@ -0,0 +1,183 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.ftp.parser;
+
+import org.apache.commons.net.ftp.FTPClientConfig;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.regex.MatchResult;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+/**
+ * Special implementation VMSFTPEntryParser with versioning turned on.
+ * This parser removes all duplicates and only leaves the version with the highest
+ * version number for each filename.
+ *
+ * This is a sample of VMS LIST output
+ *
+ * "1-JUN.LIS;1 9/9 2-JUN-1998 07:32:04 [GROUP,OWNER] (RWED,RWED,RWED,RE)",
+ * "1-JUN.LIS;2 9/9 2-JUN-1998 07:32:04 [GROUP,OWNER] (RWED,RWED,RWED,RE)",
+ * "DATA.DIR;1 1/9 2-JUN-1998 07:32:04 [GROUP,OWNER] (RWED,RWED,RWED,RE)",
+ * <P>
+ *
+ * @author <a href="Winston.Ojeda@qg.com">Winston Ojeda</a>
+ * @author <a href="sestegra@free.fr">Stephane ESTE-GRACIAS</a>
+ * @version $Id: VMSVersioningFTPEntryParser.java 636854 2008-03-13 19:55:01Z sebb $
+ *
+ * @see org.apache.commons.net.ftp.FTPFileEntryParser FTPFileEntryParser (for usage instructions)
+ */
+public class VMSVersioningFTPEntryParser extends VMSFTPEntryParser
+{
+
+ private Matcher _preparse_matcher_;
+ private Pattern _preparse_pattern_;
+ private static final String PRE_PARSE_REGEX =
+ "(.*);([0-9]+)\\s*.*";
+
+ /**
+ * Constructor for a VMSFTPEntryParser object. Sets the versioning member
+ * to the supplied value.
+ *
+ * @exception IllegalArgumentException
+ * Thrown if the regular expression is unparseable. Should not be seen
+ * under normal conditions. It it is seen, this is a sign that
+ * <code>REGEX</code> is not a valid regular expression.
+ */
+ public VMSVersioningFTPEntryParser()
+ {
+ this(null);
+ }
+
+ /**
+ * This constructor allows the creation of a VMSVersioningFTPEntryParser
+ * object with something other than the default configuration.
+ *
+ * @param config The {@link FTPClientConfig configuration} object used to
+ * configure this parser.
+ * @exception IllegalArgumentException
+ * Thrown if the regular expression is unparseable. Should not be seen
+ * under normal conditions. It it is seen, this is a sign that
+ * <code>REGEX</code> is not a valid regular expression.
+ * @since 1.4
+ */
+ public VMSVersioningFTPEntryParser(FTPClientConfig config)
+ {
+ super();
+ configure(config);
+ try
+ {
+ //_preparse_matcher_ = new Perl5Matcher();
+ _preparse_pattern_ = Pattern.compile(PRE_PARSE_REGEX);
+ }
+ catch (PatternSyntaxException pse)
+ {
+ throw new IllegalArgumentException (
+ "Unparseable regex supplied: " + PRE_PARSE_REGEX);
+ }
+
+ }
+
+
+
+ private static class NameVersion {
+ String name;
+ int versionNumber;
+ NameVersion(String name, String vers) {
+ this.name = name;
+ this.versionNumber = Integer.parseInt(vers);
+ }
+ }
+
+ /**
+ * Implement hook provided for those implementers (such as
+ * VMSVersioningFTPEntryParser, and possibly others) which return
+ * multiple files with the same name to remove the duplicates ..
+ *
+ * @param original Original list
+ *
+ * @return Original list purged of duplicates
+ */
+ @Override
+ public List<String> preParse(List<String> original) {
+ original = super.preParse(original);
+ HashMap<String, NameVersion> existingEntries = new HashMap<String, NameVersion>();
+ ListIterator<String> iter = original.listIterator();
+ while (iter.hasNext()) {
+ String entry = iter.next().trim();
+ MatchResult result = null;
+ _preparse_matcher_ = _preparse_pattern_.matcher(entry);
+ if (_preparse_matcher_.matches()) {
+ result = _preparse_matcher_.toMatchResult();
+ String name = result.group(1);
+ String version = result.group(2);
+ NameVersion nv = new NameVersion(name, version);
+ NameVersion existing = existingEntries.get(name);
+ if (null != existing) {
+ if (nv.versionNumber < existing.versionNumber) {
+ iter.remove(); // removal removes from original list.
+ continue;
+ }
+ }
+ existingEntries.put(name, nv);
+ }
+
+ }
+ // we've now removed all entries less than with less than the largest
+ // version number for each name that were listed after the largest.
+ // we now must remove those with smaller than the largest version number
+ // for each name that were found before the largest
+ while (iter.hasPrevious()) {
+ String entry = iter.previous().trim();
+ MatchResult result = null;
+ _preparse_matcher_ = _preparse_pattern_.matcher(entry);
+ if (_preparse_matcher_.matches()) {
+ result = _preparse_matcher_.toMatchResult();
+ String name = result.group(1);
+ String version = result.group(2);
+ NameVersion nv = new NameVersion(name, version);
+ NameVersion existing = existingEntries.get(name);
+ if (null != existing) {
+ if (nv.versionNumber < existing.versionNumber) {
+ iter.remove(); // removal removes from original list.
+ }
+ }
+ }
+
+ }
+ return original;
+ }
+
+
+ @Override
+ protected boolean isVersioning() {
+ return true;
+ }
+
+}
+
+/* Emacs configuration
+ * Local variables: **
+ * mode: java **
+ * c-basic-offset: 4 **
+ * indent-tabs-mode: nil **
+ * End: **
+ */
diff --git a/app/src/main/java/org/apache/commons/net/io/CopyStreamAdapter.java b/app/src/main/java/org/apache/commons/net/io/CopyStreamAdapter.java
new file mode 100644
index 0000000..c702726
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/io/CopyStreamAdapter.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.io;
+
+import org.apache.commons.net.util.ListenerList;
+
+import java.util.EventListener;
+
+/**
+ * The CopyStreamAdapter will relay CopyStreamEvents to a list of listeners
+ * when either of its bytesTransferred() methods are called. Its purpose
+ * is to facilitate the notification of the progress of a copy operation
+ * performed by one of the static copyStream() methods in
+ * org.apache.commons.io.Util to multiple listeners. The static
+ * copyStream() methods invoke the
+ * bytesTransfered(long, int) of a CopyStreamListener for performance
+ * reasons and also because multiple listeners cannot be registered given
+ * that the methods are static.
+ * <p>
+ * <p>
+ * @see CopyStreamEvent
+ * @see CopyStreamListener
+ * @see Util
+ * @author <a href="mailto:savarese@apache.org">Daniel F. Savarese</a>
+ * @version $Id: CopyStreamAdapter.java 489397 2006-12-21 16:28:51Z rwinston $
+ */
+public class CopyStreamAdapter implements CopyStreamListener
+{
+ private ListenerList internalListeners;
+
+ /**
+ * Creates a new copyStreamAdapter.
+ */
+ public CopyStreamAdapter()
+ {
+ internalListeners = new ListenerList();
+ }
+
+ /**
+ * This method is invoked by a CopyStreamEvent source after copying
+ * a block of bytes from a stream. The CopyStreamEvent will contain
+ * the total number of bytes transferred so far and the number of bytes
+ * transferred in the last write. The CopyStreamAdapater will relay
+ * the event to all of its registered listeners, listing itself as the
+ * source of the event.
+ * @param event The CopyStreamEvent fired by the copying of a block of
+ * bytes.
+ */
+ public void bytesTransferred(CopyStreamEvent event)
+ {
+ bytesTransferred(event.getTotalBytesTransferred(),
+ event.getBytesTransferred(),
+ event.getStreamSize());
+ }
+
+ /**
+ * This method is not part of the JavaBeans model and is used by the
+ * static methods in the org.apache.commons.io.Util class for efficiency.
+ * It is invoked after a block of bytes to inform the listener of the
+ * transfer. The CopyStreamAdapater will create a CopyStreamEvent
+ * from the arguments and relay the event to all of its registered
+ * listeners, listing itself as the source of the event.
+ * @param totalBytesTransferred The total number of bytes transferred
+ * so far by the copy operation.
+ * @param bytesTransferred The number of bytes copied by the most recent
+ * write.
+ * @param streamSize The number of bytes in the stream being copied.
+ * This may be equal to CopyStreamEvent.UNKNOWN_STREAM_SIZE if
+ * the size is unknown.
+ */
+ public void bytesTransferred(long totalBytesTransferred,
+ int bytesTransferred, long streamSize)
+ {
+ CopyStreamEvent event;
+
+ event = new CopyStreamEvent(this,
+ totalBytesTransferred,
+ bytesTransferred,
+ streamSize);
+
+ for (EventListener listener : internalListeners)
+ {
+ ((CopyStreamListener) (listener)).bytesTransferred(event);
+ }
+ }
+
+ /**
+ * Registers a CopyStreamListener to receive CopyStreamEvents.
+ * Although this method is not declared to be synchronized, it is
+ * implemented in a thread safe manner.
+ * @param listener The CopyStreamlistener to register.
+ */
+ public void addCopyStreamListener(CopyStreamListener listener)
+ {
+ internalListeners.addListener(listener);
+ }
+
+ /**
+ * Unregisters a CopyStreamListener. Although this method is not
+ * synchronized, it is implemented in a thread safe manner.
+ * @param listener The CopyStreamlistener to unregister.
+ */
+ public void removeCopyStreamListener(CopyStreamListener listener)
+ {
+ internalListeners.removeListener(listener);
+ }
+}
diff --git a/app/src/main/java/org/apache/commons/net/io/CopyStreamEvent.java b/app/src/main/java/org/apache/commons/net/io/CopyStreamEvent.java
new file mode 100644
index 0000000..d7d0ec3
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/io/CopyStreamEvent.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.io;
+
+import java.util.EventObject;
+
+/**
+ * A CopyStreamEvent is triggered after every write performed by a
+ * stream copying operation. The event stores the number of bytes
+ * transferred by the write triggering the event as well as the total
+ * number of bytes transferred so far by the copy operation.
+ * <p>
+ * <p>
+ * @see CopyStreamListener
+ * @see CopyStreamAdapter
+ * @see Util
+ * @author <a href="mailto:savarese@apache.org">Daniel F. Savarese</a>
+ * @version $Id: CopyStreamEvent.java 489397 2006-12-21 16:28:51Z rwinston $
+ */
+public class CopyStreamEvent extends EventObject
+{
+ /**
+ * Constant used to indicate the stream size is unknown.
+ */
+ public static final long UNKNOWN_STREAM_SIZE = -1;
+
+ private int bytesTransferred;
+ private long totalBytesTransferred;
+ private long streamSize;
+
+ /**
+ * Creates a new CopyStreamEvent instance.
+ * @param source The source of the event.
+ * @param totalBytesTransferred The total number of bytes transferred so
+ * far during a copy operation.
+ * @param bytesTransferred The number of bytes transferred during the
+ * write that triggered the CopyStreamEvent.
+ * @param streamSize The number of bytes in the stream being copied.
+ * This may be set to <code>UNKNOWN_STREAM_SIZE</code> if the
+ * size is unknown.
+ */
+ public CopyStreamEvent(Object source, long totalBytesTransferred,
+ int bytesTransferred, long streamSize)
+ {
+ super(source);
+ this.bytesTransferred = bytesTransferred;
+ this.totalBytesTransferred = totalBytesTransferred;
+ this.streamSize = streamSize;
+ }
+
+ /**
+ * Returns the number of bytes transferred by the write that triggered
+ * the event.
+ * @return The number of bytes transferred by the write that triggered
+ * the vent.
+ */
+ public int getBytesTransferred()
+ {
+ return bytesTransferred;
+ }
+
+ /**
+ * Returns the total number of bytes transferred so far by the copy
+ * operation.
+ * @return The total number of bytes transferred so far by the copy
+ * operation.
+ */
+ public long getTotalBytesTransferred()
+ {
+ return totalBytesTransferred;
+ }
+
+ /**
+ * Returns the size of the stream being copied.
+ * This may be set to <code>UNKNOWN_STREAM_SIZE</code> if the
+ * size is unknown.
+ * @return The size of the stream being copied.
+ */
+ public long getStreamSize()
+ {
+ return streamSize;
+ }
+}
diff --git a/app/src/main/java/org/apache/commons/net/io/CopyStreamException.java b/app/src/main/java/org/apache/commons/net/io/CopyStreamException.java
new file mode 100644
index 0000000..8d34145
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/io/CopyStreamException.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.io;
+
+import java.io.IOException;
+
+/**
+ * The CopyStreamException class is thrown by the org.apache.commons.io.Util
+ * copyStream() methods. It stores the number of bytes confirmed to
+ * have been transferred before an I/O error as well as the IOException
+ * responsible for the failure of a copy operation.
+ * @see Util
+ * @author <a href="mailto:savarese@apache.org">Daniel F. Savarese</a>
+ * @version $Id: CopyStreamException.java 489397 2006-12-21 16:28:51Z rwinston $
+ */
+public class CopyStreamException extends IOException
+{
+ private long totalBytesTransferred;
+ private IOException ioException;
+
+ /**
+ * Creates a new CopyStreamException instance.
+ * @param message A message describing the error.
+ * @param bytesTransferred The total number of bytes transferred before
+ * an exception was thrown in a copy operation.
+ * @param exception The IOException thrown during a copy operation.
+ */
+ public CopyStreamException(String message,
+ long bytesTransferred,
+ IOException exception)
+ {
+ super(message);
+ totalBytesTransferred = bytesTransferred;
+ ioException = exception;
+ }
+
+ /**
+ * Returns the total number of bytes confirmed to have
+ * been transferred by a failed copy operation.
+ * @return The total number of bytes confirmed to have
+ * been transferred by a failed copy operation.
+ */
+ public long getTotalBytesTransferred()
+ {
+ return totalBytesTransferred;
+ }
+
+ /**
+ * Returns the IOException responsible for the failure of a copy operation.
+ * @return The IOException responsible for the failure of a copy operation.
+ */
+ public IOException getIOException()
+ {
+ return ioException;
+ }
+}
diff --git a/app/src/main/java/org/apache/commons/net/io/CopyStreamListener.java b/app/src/main/java/org/apache/commons/net/io/CopyStreamListener.java
new file mode 100644
index 0000000..9e97fb9
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/io/CopyStreamListener.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.io;
+
+import java.util.EventListener;
+
+/**
+ * The CopyStreamListener class can accept CopyStreamEvents to keep track
+ * of the progress of a stream copying operation. However, it is currently
+ * not used that way within NetComponents for performance reasons. Rather
+ * the bytesTransferred(long, int) method is called directly rather than
+ * passing an event to bytesTransferred(CopyStreamEvent), saving the creation
+ * of a CopyStreamEvent instance. Also, the only place where
+ * CopyStreamListener is currently used within NetComponents is in the
+ * static methods of the uninstantiable org.apache.commons.io.Util class, which
+ * would preclude the use of addCopyStreamListener and
+ * removeCopyStreamListener methods. However, future additions may use the
+ * JavaBean event model, which is why the hooks have been included from the
+ * beginning.
+ * <p>
+ * <p>
+ * @see CopyStreamEvent
+ * @see CopyStreamAdapter
+ * @see Util
+ * @author <a href="mailto:savarese@apache.org">Daniel F. Savarese</a>
+ * @version $Id: CopyStreamListener.java 489397 2006-12-21 16:28:51Z rwinston $
+ */
+public interface CopyStreamListener extends EventListener
+{
+ /**
+ * This method is invoked by a CopyStreamEvent source after copying
+ * a block of bytes from a stream. The CopyStreamEvent will contain
+ * the total number of bytes transferred so far and the number of bytes
+ * transferred in the last write.
+ * @param event The CopyStreamEvent fired by the copying of a block of
+ * bytes.
+ */
+ public void bytesTransferred(CopyStreamEvent event);
+
+
+ /**
+ * This method is not part of the JavaBeans model and is used by the
+ * static methods in the org.apache.commons.io.Util class for efficiency.
+ * It is invoked after a block of bytes to inform the listener of the
+ * transfer.
+ * @param totalBytesTransferred The total number of bytes transferred
+ * so far by the copy operation.
+ * @param bytesTransferred The number of bytes copied by the most recent
+ * write.
+ * @param streamSize The number of bytes in the stream being copied.
+ * This may be equal to CopyStreamEvent.UNKNOWN_STREAM_SIZE if
+ * the size is unknown.
+ */
+ public void bytesTransferred(long totalBytesTransferred,
+ int bytesTransferred,
+ long streamSize);
+}
diff --git a/app/src/main/java/org/apache/commons/net/io/DotTerminatedMessageReader.java b/app/src/main/java/org/apache/commons/net/io/DotTerminatedMessageReader.java
new file mode 100644
index 0000000..c0f344d
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/io/DotTerminatedMessageReader.java
@@ -0,0 +1,280 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.io;
+
+import java.io.IOException;
+import java.io.PushbackReader;
+import java.io.Reader;
+
+/**
+ * DotTerminatedMessageReader is a class used to read messages from a
+ * server that are terminated by a single dot followed by a
+ * &lt;CR&gt;&lt;LF&gt;
+ * sequence and with double dots appearing at the begining of lines which
+ * do not signal end of message yet start with a dot. Various Internet
+ * protocols such as NNTP and POP3 produce messages of this type.
+ * <p>
+ * This class handles stripping of the duplicate period at the beginning
+ * of lines starting with a period, converts NETASCII newlines to the
+ * local line separator format, truncates the end of message indicator,
+ * and ensures you cannot read past the end of the message.
+ * @author <a href="mailto:savarese@apache.org">Daniel F. Savarese</a>
+ * @version $Id: DotTerminatedMessageReader.java 636825 2008-03-13 18:34:52Z sebb $
+ */
+public final class DotTerminatedMessageReader extends Reader
+{
+ private static final String LS;
+ private static final char[] LS_CHARS;
+
+ static
+ {
+ LS = System.getProperty("line.separator");
+ LS_CHARS = LS.toCharArray();
+ }
+
+ private boolean atBeginning;
+ private boolean eof;
+ private int pos;
+ private char[] internalBuffer;
+ private PushbackReader internalReader;
+
+ /**
+ * Creates a DotTerminatedMessageReader that wraps an existing Reader
+ * input source.
+ * @param reader The Reader input source containing the message.
+ */
+ public DotTerminatedMessageReader(Reader reader)
+ {
+ super(reader);
+ internalBuffer = new char[LS_CHARS.length + 3];
+ pos = internalBuffer.length;
+ // Assumes input is at start of message
+ atBeginning = true;
+ eof = false;
+ internalReader = new PushbackReader(reader);
+ }
+
+ /**
+ * Reads and returns the next character in the message. If the end of the
+ * message has been reached, returns -1. Note that a call to this method
+ * may result in multiple reads from the underlying input stream to decode
+ * the message properly (removing doubled dots and so on). All of
+ * this is transparent to the programmer and is only mentioned for
+ * completeness.
+ * @return The next character in the message. Returns -1 if the end of the
+ * message has been reached.
+ * @exception IOException If an error occurs while reading the underlying
+ * stream.
+ */
+ @Override
+ public int read() throws IOException
+ {
+ int ch;
+
+ synchronized (lock)
+ {
+ if (pos < internalBuffer.length)
+ {
+ return internalBuffer[pos++];
+ }
+
+ if (eof)
+ {
+ return -1;
+ }
+
+ if ((ch = internalReader.read()) == -1)
+ {
+ eof = true;
+ return -1;
+ }
+
+ if (atBeginning)
+ {
+ atBeginning = false;
+ if (ch == '.')
+ {
+ ch = internalReader.read();
+
+ if (ch != '.')
+ {
+ // read newline
+ eof = true;
+ internalReader.read();
+ return -1;
+ }
+ else
+ {
+ return '.';
+ }
+ }
+ }
+
+ if (ch == '\r')
+ {
+ ch = internalReader.read();
+
+ if (ch == '\n')
+ {
+ ch = internalReader.read();
+
+ if (ch == '.')
+ {
+ ch = internalReader.read();
+
+ if (ch != '.')
+ {
+ // read newline and indicate end of file
+ internalReader.read();
+ eof = true;
+ }
+ else
+ {
+ internalBuffer[--pos] = (char) ch;
+ }
+ }
+ else
+ {
+ internalReader.unread(ch);
+ }
+
+ pos -= LS_CHARS.length;
+ System.arraycopy(LS_CHARS, 0, internalBuffer, pos,
+ LS_CHARS.length);
+ ch = internalBuffer[pos++];
+ }
+ else
+ {
+ internalBuffer[--pos] = (char) ch;
+ return '\r';
+ }
+ }
+
+ return ch;
+ }
+ }
+
+ /**
+ * Reads the next characters from the message into an array and
+ * returns the number of characters read. Returns -1 if the end of the
+ * message has been reached.
+ * @param buffer The character array in which to store the characters.
+ * @return The number of characters read. Returns -1 if the
+ * end of the message has been reached.
+ * @exception IOException If an error occurs in reading the underlying
+ * stream.
+ */
+ @Override
+ public int read(char[] buffer) throws IOException
+ {
+ return read(buffer, 0, buffer.length);
+ }
+
+ /**
+ * Reads the next characters from the message into an array and
+ * returns the number of characters read. Returns -1 if the end of the
+ * message has been reached. The characters are stored in the array
+ * starting from the given offset and up to the length specified.
+ * @param buffer The character array in which to store the characters.
+ * @param offset The offset into the array at which to start storing
+ * characters.
+ * @param length The number of characters to read.
+ * @return The number of characters read. Returns -1 if the
+ * end of the message has been reached.
+ * @exception IOException If an error occurs in reading the underlying
+ * stream.
+ */
+ @Override
+ public int read(char[] buffer, int offset, int length) throws IOException
+ {
+ int ch, off;
+ synchronized (lock)
+ {
+ if (length < 1)
+ {
+ return 0;
+ }
+ if ((ch = read()) == -1)
+ {
+ return -1;
+ }
+ off = offset;
+
+ do
+ {
+ buffer[offset++] = (char) ch;
+ }
+ while (--length > 0 && (ch = read()) != -1);
+
+ return (offset - off);
+ }
+ }
+
+ /**
+ * Determines if the message is ready to be read.
+ * @return True if the message is ready to be read, false if not.
+ * @exception IOException If an error occurs while checking the underlying
+ * stream.
+ */
+ @Override
+ public boolean ready() throws IOException
+ {
+ synchronized (lock)
+ {
+ return (pos < internalBuffer.length || internalReader.ready());
+ }
+ }
+
+ /**
+ * Closes the message for reading. This doesn't actually close the
+ * underlying stream. The underlying stream may still be used for
+ * communicating with the server and therefore is not closed.
+ * <p>
+ * If the end of the message has not yet been reached, this method
+ * will read the remainder of the message until it reaches the end,
+ * so that the underlying stream may continue to be used properly
+ * for communicating with the server. If you do not fully read
+ * a message, you MUST close it, otherwise your program will likely
+ * hang or behave improperly.
+ * @exception IOException If an error occurs while reading the
+ * underlying stream.
+ */
+ @Override
+ public void close() throws IOException
+ {
+ synchronized (lock)
+ {
+ if (internalReader == null)
+ {
+ return;
+ }
+
+ if (!eof)
+ {
+ while (read() != -1)
+ {
+ ;
+ }
+ }
+ eof = true;
+ atBeginning = false;
+ pos = internalBuffer.length;
+ internalReader = null;
+ }
+ }
+}
diff --git a/app/src/main/java/org/apache/commons/net/io/DotTerminatedMessageWriter.java b/app/src/main/java/org/apache/commons/net/io/DotTerminatedMessageWriter.java
new file mode 100644
index 0000000..853e42f
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/io/DotTerminatedMessageWriter.java
@@ -0,0 +1,215 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.io;
+
+import java.io.IOException;
+import java.io.Writer;
+
+/***
+ * DotTerminatedMessageWriter is a class used to write messages to a
+ * server that are terminated by a single dot followed by a
+ * &lt;CR&gt;&lt;LF&gt;
+ * sequence and with double dots appearing at the begining of lines which
+ * do not signal end of message yet start with a dot. Various Internet
+ * protocols such as NNTP and POP3 produce messages of this type.
+ * <p>
+ * This class handles the doubling of line-starting periods,
+ * converts single linefeeds to NETASCII newlines, and on closing
+ * will send the final message terminator dot and NETASCII newline
+ * sequence.
+ * <p>
+ * <p>
+ * @author Daniel F. Savarese
+ ***/
+
+public final class DotTerminatedMessageWriter extends Writer
+{
+ private static final int __NOTHING_SPECIAL_STATE = 0;
+ private static final int __LAST_WAS_CR_STATE = 1;
+ private static final int __LAST_WAS_NL_STATE = 2;
+
+ private int __state;
+ private Writer __output;
+
+
+ /***
+ * Creates a DotTerminatedMessageWriter that wraps an existing Writer
+ * output destination.
+ * <p>
+ * @param output The Writer output destination to write the message.
+ ***/
+ public DotTerminatedMessageWriter(Writer output)
+ {
+ super(output);
+ __output = output;
+ __state = __NOTHING_SPECIAL_STATE;
+ }
+
+
+ /***
+ * Writes a character to the output. Note that a call to this method
+ * may result in multiple writes to the underling Writer in order to
+ * convert naked linefeeds to NETASCII line separators and to double
+ * line-leading periods. This is transparent to the programmer and
+ * is only mentioned for completeness.
+ * <p>
+ * @param ch The character to write.
+ * @exception IOException If an error occurs while writing to the
+ * underlying output.
+ ***/
+ @Override
+ public void write(int ch) throws IOException
+ {
+ synchronized (lock)
+ {
+ switch (ch)
+ {
+ case '\r':
+ __state = __LAST_WAS_CR_STATE;
+ __output.write('\r');
+ return ;
+ case '\n':
+ if (__state != __LAST_WAS_CR_STATE)
+ __output.write('\r');
+ __output.write('\n');
+ __state = __LAST_WAS_NL_STATE;
+ return ;
+ case '.':
+ // Double the dot at the beginning of a line
+ if (__state == __LAST_WAS_NL_STATE)
+ __output.write('.');
+ // Fall through
+ default:
+ __state = __NOTHING_SPECIAL_STATE;
+ __output.write(ch);
+ return ;
+ }
+ }
+ }
+
+
+ /***
+ * Writes a number of characters from a character array to the output
+ * starting from a given offset.
+ * <p>
+ * @param buffer The character array to write.
+ * @param offset The offset into the array at which to start copying data.
+ * @param length The number of characters to write.
+ * @exception IOException If an error occurs while writing to the underlying
+ * output.
+ ***/
+ @Override
+ public void write(char[] buffer, int offset, int length) throws IOException
+ {
+ synchronized (lock)
+ {
+ while (length-- > 0)
+ write(buffer[offset++]);
+ }
+ }
+
+
+ /***
+ * Writes a character array to the output.
+ * <p>
+ * @param buffer The character array to write.
+ * @exception IOException If an error occurs while writing to the underlying
+ * output.
+ ***/
+ @Override
+ public void write(char[] buffer) throws IOException
+ {
+ write(buffer, 0, buffer.length);
+ }
+
+
+ /***
+ * Writes a String to the output.
+ * <p>
+ * @param string The String to write.
+ * @exception IOException If an error occurs while writing to the underlying
+ * output.
+ ***/
+ @Override
+ public void write(String string) throws IOException
+ {
+ write(string.toCharArray());
+ }
+
+
+ /***
+ * Writes part of a String to the output starting from a given offset.
+ * <p>
+ * @param string The String to write.
+ * @param offset The offset into the String at which to start copying data.
+ * @param length The number of characters to write.
+ * @exception IOException If an error occurs while writing to the underlying
+ * output.
+ ***/
+ @Override
+ public void write(String string, int offset, int length) throws IOException
+ {
+ write(string.toCharArray(), offset, length);
+ }
+
+
+ /***
+ * Flushes the underlying output, writing all buffered output.
+ * <p>
+ * @exception IOException If an error occurs while writing to the underlying
+ * output.
+ ***/
+ @Override
+ public void flush() throws IOException
+ {
+ synchronized (lock)
+ {
+ __output.flush();
+ }
+ }
+
+
+ /***
+ * Flushes the underlying output, writing all buffered output, but doesn't
+ * actually close the underlying stream. The underlying stream may still
+ * be used for communicating with the server and therefore is not closed.
+ * <p>
+ * @exception IOException If an error occurs while writing to the underlying
+ * output or closing the Writer.
+ ***/
+ @Override
+ public void close() throws IOException
+ {
+ synchronized (lock)
+ {
+ if (__output == null)
+ return ;
+
+ if (__state == __LAST_WAS_CR_STATE)
+ __output.write('\n');
+ else if (__state != __LAST_WAS_NL_STATE)
+ __output.write("\r\n");
+
+ __output.write(".\r\n");
+
+ __output.flush();
+ __output = null;
+ }
+ }
+
+}
diff --git a/app/src/main/java/org/apache/commons/net/io/FromNetASCIIInputStream.java b/app/src/main/java/org/apache/commons/net/io/FromNetASCIIInputStream.java
new file mode 100644
index 0000000..76588ab
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/io/FromNetASCIIInputStream.java
@@ -0,0 +1,205 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PushbackInputStream;
+
+/***
+ * This class wraps an input stream, replacing all occurrences
+ * of &lt;CR&gt;&lt;LF&gt; (carriage return followed by a linefeed),
+ * which is the NETASCII standard for representing a newline, with the
+ * local line separator representation. You would use this class to
+ * implement ASCII file transfers requiring conversion from NETASCII.
+ * <p>
+ * <p>
+ * @author Daniel F. Savarese
+ ***/
+
+public final class FromNetASCIIInputStream extends PushbackInputStream
+{
+ static final boolean _noConversionRequired;
+ static final String _lineSeparator;
+ static final byte[] _lineSeparatorBytes;
+
+ static {
+ _lineSeparator = System.getProperty("line.separator");
+ _noConversionRequired = _lineSeparator.equals("\r\n");
+ _lineSeparatorBytes = _lineSeparator.getBytes();
+ }
+
+ private int __length = 0;
+
+ /***
+ * Returns true if the NetASCII line separator differs from the system
+ * line separator, false if they are the same. This method is useful
+ * to determine whether or not you need to instantiate a
+ * FromNetASCIIInputStream object.
+ * <p>
+ * @return True if the NETASCII line separator differs from the local
+ * system line separator, false if they are the same.
+ ***/
+ public static final boolean isConversionRequired()
+ {
+ return !_noConversionRequired;
+ }
+
+ /***
+ * Creates a FromNetASCIIInputStream instance that wraps an existing
+ * InputStream.
+ ***/
+ public FromNetASCIIInputStream(InputStream input)
+ {
+ super(input, _lineSeparatorBytes.length + 1);
+ }
+
+
+ private int __read() throws IOException
+ {
+ int ch;
+
+ ch = super.read();
+
+ if (ch == '\r')
+ {
+ ch = super.read();
+ if (ch == '\n')
+ {
+ unread(_lineSeparatorBytes);
+ ch = super.read();
+ // This is a kluge for read(byte[], ...) to read the right amount
+ --__length;
+ }
+ else
+ {
+ if (ch != -1)
+ unread(ch);
+ return '\r';
+ }
+ }
+
+ return ch;
+ }
+
+
+ /***
+ * Reads and returns the next byte in the stream. If the end of the
+ * message has been reached, returns -1. Note that a call to this method
+ * may result in multiple reads from the underlying input stream in order
+ * to convert NETASCII line separators to the local line separator format.
+ * This is transparent to the programmer and is only mentioned for
+ * completeness.
+ * <p>
+ * @return The next character in the stream. Returns -1 if the end of the
+ * stream has been reached.
+ * @exception IOException If an error occurs while reading the underlying
+ * stream.
+ ***/
+ @Override
+ public int read() throws IOException
+ {
+ if (_noConversionRequired)
+ return super.read();
+
+ return __read();
+ }
+
+
+ /***
+ * Reads the next number of bytes from the stream into an array and
+ * returns the number of bytes read. Returns -1 if the end of the
+ * stream has been reached.
+ * <p>
+ * @param buffer The byte array in which to store the data.
+ * @return The number of bytes read. Returns -1 if the
+ * end of the message has been reached.
+ * @exception IOException If an error occurs in reading the underlying
+ * stream.
+ ***/
+ @Override
+ public int read(byte buffer[]) throws IOException
+ {
+ return read(buffer, 0, buffer.length);
+ }
+
+
+ /***
+ * Reads the next number of bytes from the stream into an array and returns
+ * the number of bytes read. Returns -1 if the end of the
+ * message has been reached. The characters are stored in the array
+ * starting from the given offset and up to the length specified.
+ * <p>
+ * @param buffer The byte array in which to store the data.
+ * @param offset The offset into the array at which to start storing data.
+ * @param length The number of bytes to read.
+ * @return The number of bytes read. Returns -1 if the
+ * end of the stream has been reached.
+ * @exception IOException If an error occurs while reading the underlying
+ * stream.
+ ***/
+ @Override
+ public int read(byte buffer[], int offset, int length) throws IOException
+ {
+ int ch, off;
+
+ if (length < 1)
+ return 0;
+
+ ch = available();
+
+ __length = (length > ch ? ch : length);
+
+ // If nothing is available, block to read only one character
+ if (__length < 1)
+ __length = 1;
+
+ if (_noConversionRequired)
+ return super.read(buffer, offset, __length);
+
+ if ((ch = __read()) == -1)
+ return -1;
+
+ off = offset;
+
+ do
+ {
+ buffer[offset++] = (byte)ch;
+ }
+ while (--__length > 0 && (ch = __read()) != -1);
+
+
+ return (offset - off);
+ }
+
+
+ // PushbackInputStream in JDK 1.1.3 returns the wrong thing
+ /***
+ * Returns the number of bytes that can be read without blocking EXCEPT
+ * when newline conversions have to be made somewhere within the
+ * available block of bytes. In other words, you really should not
+ * rely on the value returned by this method if you are trying to avoid
+ * blocking.
+ ***/
+ @Override
+ public int available() throws IOException
+ {
+ return (buf.length - pos) + in.available();
+ }
+
+}
diff --git a/app/src/main/java/org/apache/commons/net/io/FromNetASCIIOutputStream.java b/app/src/main/java/org/apache/commons/net/io/FromNetASCIIOutputStream.java
new file mode 100644
index 0000000..c025a1b
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/io/FromNetASCIIOutputStream.java
@@ -0,0 +1,174 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.io;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/***
+ * This class wraps an output stream, replacing all occurrences
+ * of &lt;CR&gt;&lt;LF&gt; (carriage return followed by a linefeed),
+ * which is the NETASCII standard for representing a newline, with the
+ * local line separator representation. You would use this class to
+ * implement ASCII file transfers requiring conversion from NETASCII.
+ * <p>
+ * Because of the translation process, a call to <code>flush()</code> will
+ * not flush the last byte written if that byte was a carriage
+ * return. A call to {@link #close close() }, however, will
+ * flush the carriage return.
+ * <p>
+ * <p>
+ * @author Daniel F. Savarese
+ ***/
+
+public final class FromNetASCIIOutputStream extends FilterOutputStream
+{
+ private boolean __lastWasCR;
+
+ /***
+ * Creates a FromNetASCIIOutputStream instance that wraps an existing
+ * OutputStream.
+ * <p>
+ * @param output The OutputStream to wrap.
+ ***/
+ public FromNetASCIIOutputStream(OutputStream output)
+ {
+ super(output);
+ __lastWasCR = false;
+ }
+
+
+ private void __write(int ch) throws IOException
+ {
+ switch (ch)
+ {
+ case '\r':
+ __lastWasCR = true;
+ // Don't write anything. We need to see if next one is linefeed
+ break;
+ case '\n':
+ if (__lastWasCR)
+ {
+ out.write(FromNetASCIIInputStream._lineSeparatorBytes);
+ __lastWasCR = false;
+ break;
+ }
+ __lastWasCR = false;
+ out.write('\n');
+ break;
+ default:
+ if (__lastWasCR)
+ {
+ out.write('\r');
+ __lastWasCR = false;
+ }
+ out.write(ch);
+ break;
+ }
+ }
+
+
+ /***
+ * Writes a byte to the stream. Note that a call to this method
+ * might not actually write a byte to the underlying stream until a
+ * subsequent character is written, from which it can be determined if
+ * a NETASCII line separator was encountered.
+ * This is transparent to the programmer and is only mentioned for
+ * completeness.
+ * <p>
+ * @param ch The byte to write.
+ * @exception IOException If an error occurs while writing to the underlying
+ * stream.
+ ***/
+ @Override
+ public synchronized void write(int ch)
+ throws IOException
+ {
+ if (FromNetASCIIInputStream._noConversionRequired)
+ {
+ out.write(ch);
+ return ;
+ }
+
+ __write(ch);
+ }
+
+
+ /***
+ * Writes a byte array to the stream.
+ * <p>
+ * @param buffer The byte array to write.
+ * @exception IOException If an error occurs while writing to the underlying
+ * stream.
+ ***/
+ @Override
+ public synchronized void write(byte buffer[])
+ throws IOException
+ {
+ write(buffer, 0, buffer.length);
+ }
+
+
+ /***
+ * Writes a number of bytes from a byte array to the stream starting from
+ * a given offset.
+ * <p>
+ * @param buffer The byte array to write.
+ * @param offset The offset into the array at which to start copying data.
+ * @param length The number of bytes to write.
+ * @exception IOException If an error occurs while writing to the underlying
+ * stream.
+ ***/
+ @Override
+ public synchronized void write(byte buffer[], int offset, int length)
+ throws IOException
+ {
+ if (FromNetASCIIInputStream._noConversionRequired)
+ {
+ // FilterOutputStream method is very slow.
+ //super.write(buffer, offset, length);
+ out.write(buffer, offset, length);
+ return ;
+ }
+
+ while (length-- > 0)
+ __write(buffer[offset++]);
+ }
+
+
+ /***
+ * Closes the stream, writing all pending data.
+ * <p>
+ * @exception IOException If an error occurs while closing the stream.
+ ***/
+ @Override
+ public synchronized void close()
+ throws IOException
+ {
+ if (FromNetASCIIInputStream._noConversionRequired)
+ {
+ super.close();
+ return ;
+ }
+
+ if (__lastWasCR)
+ out.write('\r');
+ super.close();
+ }
+}
diff --git a/app/src/main/java/org/apache/commons/net/io/SocketInputStream.java b/app/src/main/java/org/apache/commons/net/io/SocketInputStream.java
new file mode 100644
index 0000000..673f434
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/io/SocketInputStream.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.io;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.Socket;
+
+/***
+ * This class wraps an input stream, storing a reference to its originating
+ * socket. When the stream is closed, it will also close the socket
+ * immediately afterward. This class is useful for situations where you
+ * are dealing with a stream originating from a socket, but do not have
+ * a reference to the socket, and want to make sure it closes when the
+ * stream closes.
+ * <p>
+ * <p>
+ * @author Daniel F. Savarese
+ * @see SocketOutputStream
+ ***/
+
+public class SocketInputStream extends FilterInputStream
+{
+ private Socket __socket;
+
+ /***
+ * Creates a SocketInputStream instance wrapping an input stream and
+ * storing a reference to a socket that should be closed on closing
+ * the stream.
+ * <p>
+ * @param socket The socket to close on closing the stream.
+ * @param stream The input stream to wrap.
+ ***/
+ public SocketInputStream(Socket socket, InputStream stream)
+ {
+ super(stream);
+ __socket = socket;
+ }
+
+ /***
+ * Closes the stream and immediately afterward closes the referenced
+ * socket.
+ * <p>
+ * @exception IOException If there is an error in closing the stream
+ * or socket.
+ ***/
+ @Override
+ public void close() throws IOException
+ {
+ super.close();
+ __socket.close();
+ }
+}
diff --git a/app/src/main/java/org/apache/commons/net/io/SocketOutputStream.java b/app/src/main/java/org/apache/commons/net/io/SocketOutputStream.java
new file mode 100644
index 0000000..abd7f5d
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/io/SocketOutputStream.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.io;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.Socket;
+
+/***
+ * This class wraps an output stream, storing a reference to its originating
+ * socket. When the stream is closed, it will also close the socket
+ * immediately afterward. This class is useful for situations where you
+ * are dealing with a stream originating from a socket, but do not have
+ * a reference to the socket, and want to make sure it closes when the
+ * stream closes.
+ * <p>
+ * <p>
+ * @author Daniel F. Savarese
+ * @see SocketInputStream
+ ***/
+
+public class SocketOutputStream extends FilterOutputStream
+{
+ private Socket __socket;
+
+ /***
+ * Creates a SocketOutputStream instance wrapping an output stream and
+ * storing a reference to a socket that should be closed on closing
+ * the stream.
+ * <p>
+ * @param socket The socket to close on closing the stream.
+ * @param stream The input stream to wrap.
+ ***/
+ public SocketOutputStream(Socket socket, OutputStream stream)
+ {
+ super(stream);
+ __socket = socket;
+ }
+
+
+ /***
+ * Writes a number of bytes from a byte array to the stream starting from
+ * a given offset. This method bypasses the equivalent method in
+ * FilterOutputStream because the FilterOutputStream implementation is
+ * very inefficient.
+ * <p>
+ * @param buffer The byte array to write.
+ * @param offset The offset into the array at which to start copying data.
+ * @param length The number of bytes to write.
+ * @exception IOException If an error occurs while writing to the underlying
+ * stream.
+ ***/
+ @Override
+ public void write(byte buffer[], int offset, int length) throws IOException
+ {
+ out.write(buffer, offset, length);
+ }
+
+
+ /***
+ * Closes the stream and immediately afterward closes the referenced
+ * socket.
+ * <p>
+ * @exception IOException If there is an error in closing the stream
+ * or socket.
+ ***/
+ @Override
+ public void close() throws IOException
+ {
+ super.close();
+ __socket.close();
+ }
+}
diff --git a/app/src/main/java/org/apache/commons/net/io/ToNetASCIIInputStream.java b/app/src/main/java/org/apache/commons/net/io/ToNetASCIIInputStream.java
new file mode 100644
index 0000000..55e4735
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/io/ToNetASCIIInputStream.java
@@ -0,0 +1,181 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.io;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/***
+ * This class wraps an input stream, replacing all singly occurring
+ * &lt;LF&gt; (linefeed) characters with &lt;CR&gt;&lt;LF&gt; (carriage return
+ * followed by linefeed), which is the NETASCII standard for representing
+ * a newline.
+ * You would use this class to implement ASCII file transfers requiring
+ * conversion to NETASCII.
+ * <p>
+ * <p>
+ * @author Daniel F. Savarese
+ ***/
+
+public final class ToNetASCIIInputStream extends FilterInputStream
+{
+ private static final int __NOTHING_SPECIAL = 0;
+ private static final int __LAST_WAS_CR = 1;
+ private static final int __LAST_WAS_NL = 2;
+ private int __status;
+
+ /***
+ * Creates a ToNetASCIIInputStream instance that wraps an existing
+ * InputStream.
+ * <p>
+ * @param input The InputStream to .
+ ***/
+ public ToNetASCIIInputStream(InputStream input)
+ {
+ super(input);
+ __status = __NOTHING_SPECIAL;
+ }
+
+
+ /***
+ * Reads and returns the next byte in the stream. If the end of the
+ * message has been reached, returns -1.
+ * <p>
+ * @return The next character in the stream. Returns -1 if the end of the
+ * stream has been reached.
+ * @exception IOException If an error occurs while reading the underlying
+ * stream.
+ ***/
+ @Override
+ public int read() throws IOException
+ {
+ int ch;
+
+ if (__status == __LAST_WAS_NL)
+ {
+ __status = __NOTHING_SPECIAL;
+ return '\n';
+ }
+
+ ch = in.read();
+
+ switch (ch)
+ {
+ case '\r':
+ __status = __LAST_WAS_CR;
+ return '\r';
+ case '\n':
+ if (__status != __LAST_WAS_CR)
+ {
+ __status = __LAST_WAS_NL;
+ return '\r';
+ }
+ // else fall through
+ default:
+ __status = __NOTHING_SPECIAL;
+ return ch;
+ }
+ // statement not reached
+ //return ch;
+ }
+
+
+ /***
+ * Reads the next number of bytes from the stream into an array and
+ * returns the number of bytes read. Returns -1 if the end of the
+ * stream has been reached.
+ * <p>
+ * @param buffer The byte array in which to store the data.
+ * @return The number of bytes read. Returns -1 if the
+ * end of the message has been reached.
+ * @exception IOException If an error occurs in reading the underlying
+ * stream.
+ ***/
+ @Override
+ public int read(byte buffer[]) throws IOException
+ {
+ return read(buffer, 0, buffer.length);
+ }
+
+
+ /***
+ * Reads the next number of bytes from the stream into an array and returns
+ * the number of bytes read. Returns -1 if the end of the
+ * message has been reached. The characters are stored in the array
+ * starting from the given offset and up to the length specified.
+ * <p>
+ * @param buffer The byte array in which to store the data.
+ * @param offset The offset into the array at which to start storing data.
+ * @param length The number of bytes to read.
+ * @return The number of bytes read. Returns -1 if the
+ * end of the stream has been reached.
+ * @exception IOException If an error occurs while reading the underlying
+ * stream.
+ ***/
+ @Override
+ public int read(byte buffer[], int offset, int length) throws IOException
+ {
+ int ch, off;
+
+ if (length < 1)
+ return 0;
+
+ ch = available();
+
+ if (length > ch)
+ length = ch;
+
+ // If nothing is available, block to read only one character
+ if (length < 1)
+ length = 1;
+
+ if ((ch = read()) == -1)
+ return -1;
+
+ off = offset;
+
+ do
+ {
+ buffer[offset++] = (byte)ch;
+ }
+ while (--length > 0 && (ch = read()) != -1);
+
+ return (offset - off);
+ }
+
+ /*** Returns false. Mark is not supported. ***/
+ @Override
+ public boolean markSupported()
+ {
+ return false;
+ }
+
+ @Override
+ public int available() throws IOException
+ {
+ int result;
+
+ result = in.available();
+
+ if (__status == __LAST_WAS_NL)
+ return (result + 1);
+
+ return result;
+ }
+}
diff --git a/app/src/main/java/org/apache/commons/net/io/ToNetASCIIOutputStream.java b/app/src/main/java/org/apache/commons/net/io/ToNetASCIIOutputStream.java
new file mode 100644
index 0000000..aeacc98
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/io/ToNetASCIIOutputStream.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.io;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/***
+ * This class wraps an output stream, replacing all singly occurring
+ * &lt;LF&gt; (linefeed) characters with &lt;CR&gt;&lt;LF&gt; (carriage return
+ * followed by linefeed), which is the NETASCII standard for representing
+ * a newline.
+ * You would use this class to implement ASCII file transfers requiring
+ * conversion to NETASCII.
+ * <p>
+ * <p>
+ * @author Daniel F. Savarese
+ ***/
+
+public final class ToNetASCIIOutputStream extends FilterOutputStream
+{
+ private boolean __lastWasCR;
+
+ /***
+ * Creates a ToNetASCIIOutputStream instance that wraps an existing
+ * OutputStream.
+ * <p>
+ * @param output The OutputStream to wrap.
+ ***/
+ public ToNetASCIIOutputStream(OutputStream output)
+ {
+ super(output);
+ __lastWasCR = false;
+ }
+
+
+ /***
+ * Writes a byte to the stream. Note that a call to this method
+ * may result in multiple writes to the underlying input stream in order
+ * to convert naked newlines to NETASCII line separators.
+ * This is transparent to the programmer and is only mentioned for
+ * completeness.
+ * <p>
+ * @param ch The byte to write.
+ * @exception IOException If an error occurs while writing to the underlying
+ * stream.
+ ***/
+ @Override
+ public synchronized void write(int ch)
+ throws IOException
+ {
+ switch (ch)
+ {
+ case '\r':
+ __lastWasCR = true;
+ out.write('\r');
+ return ;
+ case '\n':
+ if (!__lastWasCR)
+ out.write('\r');
+ // Fall through
+ default:
+ __lastWasCR = false;
+ out.write(ch);
+ return ;
+ }
+ }
+
+
+ /***
+ * Writes a byte array to the stream.
+ * <p>
+ * @param buffer The byte array to write.
+ * @exception IOException If an error occurs while writing to the underlying
+ * stream.
+ ***/
+ @Override
+ public synchronized void write(byte buffer[])
+ throws IOException
+ {
+ write(buffer, 0, buffer.length);
+ }
+
+
+ /***
+ * Writes a number of bytes from a byte array to the stream starting from
+ * a given offset.
+ * <p>
+ * @param buffer The byte array to write.
+ * @param offset The offset into the array at which to start copying data.
+ * @param length The number of bytes to write.
+ * @exception IOException If an error occurs while writing to the underlying
+ * stream.
+ ***/
+ @Override
+ public synchronized void write(byte buffer[], int offset, int length)
+ throws IOException
+ {
+ while (length-- > 0)
+ write(buffer[offset++]);
+ }
+
+}
diff --git a/app/src/main/java/org/apache/commons/net/io/Util.java b/app/src/main/java/org/apache/commons/net/io/Util.java
new file mode 100644
index 0000000..4e85a93
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/io/Util.java
@@ -0,0 +1,334 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.Writer;
+
+/***
+ * The Util class cannot be instantiated and stores short static convenience
+ * methods that are often quite useful.
+ * <p>
+ * <p>
+ * @see CopyStreamException
+ * @see CopyStreamListener
+ * @see CopyStreamAdapter
+ * @author Daniel F. Savarese
+ ***/
+
+public final class Util
+{
+ /***
+ * The default buffer size used by {@link #copyStream copyStream }
+ * and {@link #copyReader copyReader }. It's value is 1024.
+ ***/
+ public static final int DEFAULT_COPY_BUFFER_SIZE = 1024;
+
+ // Cannot be instantiated
+ private Util()
+ { }
+
+
+ /***
+ * Copies the contents of an InputStream to an OutputStream using a
+ * copy buffer of a given size and notifies the provided
+ * CopyStreamListener of the progress of the copy operation by calling
+ * its bytesTransferred(long, int) method after each write to the
+ * destination. If you wish to notify more than one listener you should
+ * use a CopyStreamAdapter as the listener and register the additional
+ * listeners with the CopyStreamAdapter.
+ * <p>
+ * The contents of the InputStream are
+ * read until the end of the stream is reached, but neither the
+ * source nor the destination are closed. You must do this yourself
+ * outside of the method call. The number of bytes read/written is
+ * returned.
+ * <p>
+ * @param source The source InputStream.
+ * @param dest The destination OutputStream.
+ * @param bufferSize The number of bytes to buffer during the copy.
+ * @param streamSize The number of bytes in the stream being copied.
+ * Should be set to CopyStreamEvent.UNKNOWN_STREAM_SIZE if unknown.
+ * @param listener The CopyStreamListener to notify of progress. If
+ * this parameter is null, notification is not attempted.
+ * @param flush Whether to flush the output stream after every
+ * write. This is necessary for interactive sessions that rely on
+ * buffered streams. If you don't flush, the data will stay in
+ * the stream buffer.
+ * @exception CopyStreamException If an error occurs while reading from the
+ * source or writing to the destination. The CopyStreamException
+ * will contain the number of bytes confirmed to have been
+ * transferred before an
+ * IOException occurred, and it will also contain the IOException
+ * that caused the error. These values can be retrieved with
+ * the CopyStreamException getTotalBytesTransferred() and
+ * getIOException() methods.
+ ***/
+ public static final long copyStream(InputStream source, OutputStream dest,
+ int bufferSize, long streamSize,
+ CopyStreamListener listener,
+ boolean flush)
+ throws CopyStreamException
+ {
+ int bytes;
+ long total;
+ byte[] buffer;
+
+ buffer = new byte[bufferSize];
+ total = 0;
+
+ try
+ {
+ while ((bytes = source.read(buffer)) != -1)
+ {
+ // Technically, some read(byte[]) methods may return 0 and we cannot
+ // accept that as an indication of EOF.
+
+ if (bytes == 0)
+ {
+ bytes = source.read();
+ if (bytes < 0)
+ break;
+ dest.write(bytes);
+ if(flush)
+ dest.flush();
+ ++total;
+ if (listener != null)
+ listener.bytesTransferred(total, 1, streamSize);
+ continue;
+ }
+
+ dest.write(buffer, 0, bytes);
+ if(flush)
+ dest.flush();
+ total += bytes;
+ if (listener != null)
+ listener.bytesTransferred(total, bytes, streamSize);
+ }
+ }
+ catch (IOException e)
+ {
+ throw new CopyStreamException("IOException caught while copying.",
+ total, e);
+ }
+
+ return total;
+ }
+
+
+ /***
+ * Copies the contents of an InputStream to an OutputStream using a
+ * copy buffer of a given size and notifies the provided
+ * CopyStreamListener of the progress of the copy operation by calling
+ * its bytesTransferred(long, int) method after each write to the
+ * destination. If you wish to notify more than one listener you should
+ * use a CopyStreamAdapter as the listener and register the additional
+ * listeners with the CopyStreamAdapter.
+ * <p>
+ * The contents of the InputStream are
+ * read until the end of the stream is reached, but neither the
+ * source nor the destination are closed. You must do this yourself
+ * outside of the method call. The number of bytes read/written is
+ * returned.
+ * <p>
+ * @param source The source InputStream.
+ * @param dest The destination OutputStream.
+ * @param bufferSize The number of bytes to buffer during the copy.
+ * @param streamSize The number of bytes in the stream being copied.
+ * Should be set to CopyStreamEvent.UNKNOWN_STREAM_SIZE if unknown.
+ * @param listener The CopyStreamListener to notify of progress. If
+ * this parameter is null, notification is not attempted.
+ * @exception CopyStreamException If an error occurs while reading from the
+ * source or writing to the destination. The CopyStreamException
+ * will contain the number of bytes confirmed to have been
+ * transferred before an
+ * IOException occurred, and it will also contain the IOException
+ * that caused the error. These values can be retrieved with
+ * the CopyStreamException getTotalBytesTransferred() and
+ * getIOException() methods.
+ ***/
+ public static final long copyStream(InputStream source, OutputStream dest,
+ int bufferSize, long streamSize,
+ CopyStreamListener listener)
+ throws CopyStreamException
+ {
+ return copyStream(source, dest, bufferSize, streamSize, listener,
+ true);
+ }
+
+
+ /***
+ * Copies the contents of an InputStream to an OutputStream using a
+ * copy buffer of a given size. The contents of the InputStream are
+ * read until the end of the stream is reached, but neither the
+ * source nor the destination are closed. You must do this yourself
+ * outside of the method call. The number of bytes read/written is
+ * returned.
+ * <p>
+ * @param source The source InputStream.
+ * @param dest The destination OutputStream.
+ * @return The number of bytes read/written in the copy operation.
+ * @exception CopyStreamException If an error occurs while reading from the
+ * source or writing to the destination. The CopyStreamException
+ * will contain the number of bytes confirmed to have been
+ * transferred before an
+ * IOException occurred, and it will also contain the IOException
+ * that caused the error. These values can be retrieved with
+ * the CopyStreamException getTotalBytesTransferred() and
+ * getIOException() methods.
+ ***/
+ public static final long copyStream(InputStream source, OutputStream dest,
+ int bufferSize)
+ throws CopyStreamException
+ {
+ return copyStream(source, dest, bufferSize,
+ CopyStreamEvent.UNKNOWN_STREAM_SIZE, null);
+ }
+
+
+ /***
+ * Same as <code> copyStream(source, dest, DEFAULT_COPY_BUFFER_SIZE); </code>
+ ***/
+ public static final long copyStream(InputStream source, OutputStream dest)
+ throws CopyStreamException
+ {
+ return copyStream(source, dest, DEFAULT_COPY_BUFFER_SIZE);
+ }
+
+
+ /***
+ * Copies the contents of a Reader to a Writer using a
+ * copy buffer of a given size and notifies the provided
+ * CopyStreamListener of the progress of the copy operation by calling
+ * its bytesTransferred(long, int) method after each write to the
+ * destination. If you wish to notify more than one listener you should
+ * use a CopyStreamAdapter as the listener and register the additional
+ * listeners with the CopyStreamAdapter.
+ * <p>
+ * The contents of the Reader are
+ * read until its end is reached, but neither the source nor the
+ * destination are closed. You must do this yourself outside of the
+ * method call. The number of characters read/written is returned.
+ * <p>
+ * @param source The source Reader.
+ * @param dest The destination writer.
+ * @param bufferSize The number of characters to buffer during the copy.
+ * @param streamSize The number of characters in the stream being copied.
+ * Should be set to CopyStreamEvent.UNKNOWN_STREAM_SIZE if unknown.
+ * @param listener The CopyStreamListener to notify of progress. If
+ * this parameter is null, notification is not attempted.
+ * @return The number of characters read/written in the copy operation.
+ * @exception CopyStreamException If an error occurs while reading from the
+ * source or writing to the destination. The CopyStreamException
+ * will contain the number of bytes confirmed to have been
+ * transferred before an
+ * IOException occurred, and it will also contain the IOException
+ * that caused the error. These values can be retrieved with
+ * the CopyStreamException getTotalBytesTransferred() and
+ * getIOException() methods.
+ ***/
+ public static final long copyReader(Reader source, Writer dest,
+ int bufferSize, long streamSize,
+ CopyStreamListener listener)
+ throws CopyStreamException
+ {
+ int chars;
+ long total;
+ char[] buffer;
+
+ buffer = new char[bufferSize];
+ total = 0;
+
+ try
+ {
+ while ((chars = source.read(buffer)) != -1)
+ {
+ // Technically, some read(char[]) methods may return 0 and we cannot
+ // accept that as an indication of EOF.
+ if (chars == 0)
+ {
+ chars = source.read();
+ if (chars < 0)
+ break;
+ dest.write(chars);
+ dest.flush();
+ ++total;
+ if (listener != null)
+ listener.bytesTransferred(total, chars, streamSize);
+ continue;
+ }
+
+ dest.write(buffer, 0, chars);
+ dest.flush();
+ total += chars;
+ if (listener != null)
+ listener.bytesTransferred(total, chars, streamSize);
+ }
+ }
+ catch (IOException e)
+ {
+ throw new CopyStreamException("IOException caught while copying.",
+ total, e);
+ }
+
+ return total;
+ }
+
+
+ /***
+ * Copies the contents of a Reader to a Writer using a
+ * copy buffer of a given size. The contents of the Reader are
+ * read until its end is reached, but neither the source nor the
+ * destination are closed. You must do this yourself outside of the
+ * method call. The number of characters read/written is returned.
+ * <p>
+ * @param source The source Reader.
+ * @param dest The destination writer.
+ * @param bufferSize The number of characters to buffer during the copy.
+ * @return The number of characters read/written in the copy operation.
+ * @exception CopyStreamException If an error occurs while reading from the
+ * source or writing to the destination. The CopyStreamException
+ * will contain the number of bytes confirmed to have been
+ * transferred before an
+ * IOException occurred, and it will also contain the IOException
+ * that caused the error. These values can be retrieved with
+ * the CopyStreamException getTotalBytesTransferred() and
+ * getIOException() methods.
+ ***/
+ public static final long copyReader(Reader source, Writer dest,
+ int bufferSize)
+ throws CopyStreamException
+ {
+ return copyReader(source, dest, bufferSize,
+ CopyStreamEvent.UNKNOWN_STREAM_SIZE, null);
+ }
+
+
+ /***
+ * Same as <code> copyReader(source, dest, DEFAULT_COPY_BUFFER_SIZE); </code>
+ ***/
+ public static final long copyReader(Reader source, Writer dest)
+ throws CopyStreamException
+ {
+ return copyReader(source, dest, DEFAULT_COPY_BUFFER_SIZE);
+ }
+
+}
diff --git a/app/src/main/java/org/apache/commons/net/util/ListenerList.java b/app/src/main/java/org/apache/commons/net/util/ListenerList.java
new file mode 100644
index 0000000..796fb78
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/util/ListenerList.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.net.util;
+
+import java.io.Serializable;
+import java.util.EventListener;
+import java.util.Iterator;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * @author Daniel F. Savarese
+ */
+
+public class ListenerList implements Serializable, Iterable<EventListener>
+{
+ private final CopyOnWriteArrayList<EventListener> __listeners;
+
+ public ListenerList()
+ {
+ __listeners = new CopyOnWriteArrayList<EventListener>();
+ }
+
+ public void addListener(EventListener listener)
+ {
+ __listeners.add(listener);
+ }
+
+ public void removeListener(EventListener listener)
+ {
+ __listeners.remove(listener);
+ }
+
+ public int getListenerCount()
+ {
+ return __listeners.size();
+ }
+
+ /**
+ * Return an {@link Iterator} for the {@link EventListener} instances
+ *
+ * @since 2.0
+ * TODO Check that this is a good defensive strategy
+ */
+ public Iterator<EventListener> iterator() {
+ return __listeners.iterator();
+ }
+
+}
diff --git a/app/src/main/java/org/apache/commons/net/util/SubnetUtils.java b/app/src/main/java/org/apache/commons/net/util/SubnetUtils.java
new file mode 100644
index 0000000..90cc6e8
--- /dev/null
+++ b/app/src/main/java/org/apache/commons/net/util/SubnetUtils.java
@@ -0,0 +1,211 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.net.util;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A class that performs some subnet calculations given a network address and a subnet mask.
+ * @see http://www.faqs.org/rfcs/rfc1519.html
+ * @author <rwinston@apache.org>
+ * @since 2.0
+ */
+public class SubnetUtils {
+
+ private static final String IP_ADDRESS = "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})";
+ private static final String SLASH_FORMAT = IP_ADDRESS + "/(\\d{1,3})";
+ private static final Pattern addressPattern = Pattern.compile(IP_ADDRESS);
+ private static final Pattern cidrPattern = Pattern.compile(SLASH_FORMAT);
+ private static final int NBITS = 32;
+
+ private int netmask = 0;
+ private int address = 0;
+ private int network = 0;
+ private int broadcast = 0;
+
+ /**
+ * Constructor that takes a CIDR-notation string, e.g. "192.168.0.1/16"
+ * @param cidrNotation A CIDR-notation string, e.g. "192.168.0.1/16"
+ */
+ public SubnetUtils(String cidrNotation) {
+ calculate(cidrNotation);
+ }
+
+ /**
+ * Constructor that takes two dotted decimal addresses.
+ * @param address An IP address, e.g. "192.168.0.1"
+ * @param mask A dotted decimal netmask e.g. "255.255.0.0"
+ */
+ public SubnetUtils(String address, String mask) {
+ calculate(toCidrNotation(address, mask));
+ }
+
+ /**
+ * Convenience container for subnet summary information.
+ *
+ */
+ public final class SubnetInfo {
+ private SubnetInfo() {}
+
+ private int netmask() { return netmask; }
+ private int network() { return network; }
+ private int address() { return address; }
+ private int broadcast() { return broadcast; }
+ private int low() { return network() + 1; }
+ private int high() { return broadcast() - 1; }
+
+ public boolean isInRange(String address) { return isInRange(toInteger(address)); }
+ private boolean isInRange(int address) { return ((address-low()) <= (high()-low())); }
+
+ public String getBroadcastAddress() { return format(toArray(broadcast())); }
+ public String getNetworkAddress() { return format(toArray(network())); }
+ public String getNetmask() { return format(toArray(netmask())); }
+ public String getAddress() { return format(toArray(address())); }
+ public String getLowAddress() { return format(toArray(low())); }
+ public String getHighAddress() { return format(toArray(high())); }
+ public int getAddressCount() { return (broadcast() - low()); }
+
+ public int asInteger(String address) { return toInteger(address); }
+
+ public String getCidrSignature() {
+ return toCidrNotation(
+ format(toArray(address())),
+ format(toArray(netmask()))
+ );
+ }
+
+ public String[] getAllAddresses() {
+ String[] addresses = new String[getAddressCount()];
+ for (int add = low(), j=0; add <= high(); ++add, ++j) {
+ addresses[j] = format(toArray(add));
+ }
+ return addresses;
+ }
+ }
+
+ /**
+ * Return a {@link SubnetInfo} instance that contains subnet-specific statistics
+ * @return
+ */
+ public final SubnetInfo getInfo() { return new SubnetInfo(); }
+
+ /*
+ * Initialize the internal fields from the supplied CIDR mask
+ */
+ private void calculate(String mask) {
+ Matcher matcher = cidrPattern.matcher(mask);
+
+ if (matcher.matches()) {
+ address = matchAddress(matcher);
+
+ /* Create a binary netmask from the number of bits specification /x */
+ int cidrPart = rangeCheck(Integer.parseInt(matcher.group(5)), 0, NBITS-1);
+ for (int j = 0; j < cidrPart; ++j) {
+ netmask |= (1 << 31-j);
+ }
+
+ /* Calculate base network address */
+ network = (address & netmask);
+
+ /* Calculate broadcast address */
+ broadcast = network | ~(netmask);
+ }
+ else
+ throw new IllegalArgumentException("Could not parse [" + mask + "]");
+ }
+
+ /*
+ * Convert a dotted decimal format address to a packed integer format
+ */
+ private int toInteger(String address) {
+ Matcher matcher = addressPattern.matcher(address);
+ if (matcher.matches()) {
+ return matchAddress(matcher);
+ }
+ else
+ throw new IllegalArgumentException("Could not parse [" + address + "]");
+ }
+
+ /*
+ * Convenience method to extract the components of a dotted decimal address and
+ * pack into an integer using a regex match
+ */
+ private int matchAddress(Matcher matcher) {
+ int addr = 0;
+ for (int i = 1; i <= 4; ++i) {
+ int n = (rangeCheck(Integer.parseInt(matcher.group(i)), 0, 255));
+ addr |= ((n & 0xff) << 8*(4-i));
+ }
+ return addr;
+ }
+
+ /*
+ * Convert a packed integer address into a 4-element array
+ */
+ private int[] toArray(int val) {
+ int ret[] = new int[4];
+ for (int j = 3; j >= 0; --j)
+ ret[j] |= ((val >>> 8*(3-j)) & (0xff));
+ return ret;
+ }
+
+ /*
+ * Convert a 4-element array into dotted decimal format
+ */
+ private String format(int[] octets) {
+ StringBuilder str = new StringBuilder();
+ for (int i =0; i < octets.length; ++i){
+ str.append(octets[i]);
+ if (i != octets.length - 1) {
+ str.append(".");
+ }
+ }
+ return str.toString();
+ }
+
+ /*
+ * Convenience function to check integer boundaries
+ */
+ private int rangeCheck(int value, int begin, int end) {
+ if (value >= begin && value <= end)
+ return value;
+
+ throw new IllegalArgumentException("Value out of range: [" + value + "]");
+ }
+
+ /*
+ * Count the number of 1-bits in a 32-bit integer using a divide-and-conquer strategy
+ * see Hacker's Delight section 5.1
+ */
+ int pop(int x) {
+ x = x - ((x >>> 1) & 0x55555555);
+ x = (x & 0x33333333) + ((x >>> 2) & 0x33333333);
+ x = (x + (x >>> 4)) & 0x0F0F0F0F;
+ x = x + (x >>> 8);
+ x = x + (x >>> 16);
+ return x & 0x0000003F;
+ }
+
+ /* Convert two dotted decimal addresses to a single xxx.xxx.xxx.xxx/yy format
+ * by counting the 1-bit population in the mask address. (It may be better to count
+ * NBITS-#trailing zeroes for this case)
+ */
+ private String toCidrNotation(String addr, String mask) {
+ return addr + "/" + pop(toInteger(mask));
+ }
+}
diff --git a/app/src/main/res/drawable-hdpi/icon.png b/app/src/main/res/drawable-hdpi/icon.png
new file mode 100644
index 0000000..adaeefb
--- /dev/null
+++ b/app/src/main/res/drawable-hdpi/icon.png
Binary files differ
diff --git a/app/src/main/res/drawable-hdpi/marcus72.png b/app/src/main/res/drawable-hdpi/marcus72.png
new file mode 100644
index 0000000..b93712d
--- /dev/null
+++ b/app/src/main/res/drawable-hdpi/marcus72.png
Binary files differ
diff --git a/app/src/main/res/drawable-ldpi/binary.png b/app/src/main/res/drawable-ldpi/binary.png
new file mode 100644
index 0000000..c86d3e9
--- /dev/null
+++ b/app/src/main/res/drawable-ldpi/binary.png
Binary files differ
diff --git a/app/src/main/res/drawable-ldpi/compressed.png b/app/src/main/res/drawable-ldpi/compressed.png
new file mode 100644
index 0000000..1959b85
--- /dev/null
+++ b/app/src/main/res/drawable-ldpi/compressed.png
Binary files differ
diff --git a/app/src/main/res/drawable-ldpi/folder.png b/app/src/main/res/drawable-ldpi/folder.png
new file mode 100644
index 0000000..df03153
--- /dev/null
+++ b/app/src/main/res/drawable-ldpi/folder.png
Binary files differ
diff --git a/app/src/main/res/drawable-ldpi/generic.png b/app/src/main/res/drawable-ldpi/generic.png
new file mode 100644
index 0000000..478e06e
--- /dev/null
+++ b/app/src/main/res/drawable-ldpi/generic.png
Binary files differ
diff --git a/app/src/main/res/drawable-ldpi/icon.png b/app/src/main/res/drawable-ldpi/icon.png
new file mode 100644
index 0000000..4744bb9
--- /dev/null
+++ b/app/src/main/res/drawable-ldpi/icon.png
Binary files differ
diff --git a/app/src/main/res/drawable-ldpi/image.png b/app/src/main/res/drawable-ldpi/image.png
new file mode 100644
index 0000000..1fe3209
--- /dev/null
+++ b/app/src/main/res/drawable-ldpi/image.png
Binary files differ
diff --git a/app/src/main/res/drawable-ldpi/link.png b/app/src/main/res/drawable-ldpi/link.png
new file mode 100644
index 0000000..f53339b
--- /dev/null
+++ b/app/src/main/res/drawable-ldpi/link.png
Binary files differ
diff --git a/app/src/main/res/drawable-ldpi/movie.png b/app/src/main/res/drawable-ldpi/movie.png
new file mode 100644
index 0000000..131e9dc
--- /dev/null
+++ b/app/src/main/res/drawable-ldpi/movie.png
Binary files differ
diff --git a/app/src/main/res/drawable-ldpi/music.png b/app/src/main/res/drawable-ldpi/music.png
new file mode 100644
index 0000000..8cbbe02
--- /dev/null
+++ b/app/src/main/res/drawable-ldpi/music.png
Binary files differ
diff --git a/app/src/main/res/drawable-ldpi/text.png b/app/src/main/res/drawable-ldpi/text.png
new file mode 100644
index 0000000..c43fb62
--- /dev/null
+++ b/app/src/main/res/drawable-ldpi/text.png
Binary files differ
diff --git a/app/src/main/res/drawable-ldpi/unknown.png b/app/src/main/res/drawable-ldpi/unknown.png
new file mode 100644
index 0000000..e4e2982
--- /dev/null
+++ b/app/src/main/res/drawable-ldpi/unknown.png
Binary files differ
diff --git a/app/src/main/res/drawable-mdpi/icon.png b/app/src/main/res/drawable-mdpi/icon.png
new file mode 100644
index 0000000..a8a7390
--- /dev/null
+++ b/app/src/main/res/drawable-mdpi/icon.png
Binary files differ
diff --git a/app/src/main/res/drawable-mdpi/up_arrow.png b/app/src/main/res/drawable-mdpi/up_arrow.png
new file mode 100644
index 0000000..7303851
--- /dev/null
+++ b/app/src/main/res/drawable-mdpi/up_arrow.png
Binary files differ
diff --git a/app/src/main/res/layout/browser.xml b/app/src/main/res/layout/browser.xml
new file mode 100644
index 0000000..4ba2fe4
--- /dev/null
+++ b/app/src/main/res/layout/browser.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical" >
+
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+ <ImageButton
+ android:id="@+id/upDirButton"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:src="@drawable/up_arrow" />
+
+ <EditText
+ android:id="@+id/directoryname"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="0.5"
+ android:inputType="textUri" />
+
+ <ImageButton
+ android:id="@+id/goDirButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@android:drawable/ic_media_play" />
+
+ </LinearLayout>
+ <ListView
+ android:id="@+id/filelist"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" >
+ </ListView>
+</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/filerow.xml b/app/src/main/res/layout/filerow.xml
new file mode 100644
index 0000000..abede54
--- /dev/null
+++ b/app/src/main/res/layout/filerow.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent" >
+ <ImageView android:id="@+id/fileicon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="5dp"/>
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+
+ <TextView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/filetext"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:textStyle="bold">
+ </TextView>
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+ <TextView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/filesize"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="0.5">
+ </TextView>
+
+ <TextView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/filemodified"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="0.5"
+ android:gravity="right"
+ android:layout_marginRight="5dp" >
+
+ </TextView>
+ </LinearLayout>
+ </LinearLayout>
+</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/home.xml b/app/src/main/res/layout/home.xml
new file mode 100644
index 0000000..a68954f
--- /dev/null
+++ b/app/src/main/res/layout/home.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+ <Button
+ android:id="@+id/browse"
+ android:layout_height="fill_parent"
+ android:layout_width="fill_parent"
+ android:layout_weight="1"
+ android:text="Upload File"
+ />
+ <Button
+ android:id="@+id/audio"
+ android:layout_height="fill_parent"
+ android:layout_width="fill_parent"
+ android:layout_weight="1"
+ android:text="Record Audio"
+ />
+ <Button
+ android:id="@+id/locate"
+ android:layout_height="fill_parent"
+ android:layout_width="fill_parent"
+ android:layout_weight="1"
+ android:text="Find Location"
+ />
+
+</LinearLayout>
diff --git a/app/src/main/res/layout/share.xml b/app/src/main/res/layout/share.xml
new file mode 100644
index 0000000..2573155
--- /dev/null
+++ b/app/src/main/res/layout/share.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:android1="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical" >
+<ScrollView
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:gravity="top"
+ android:orientation="vertical" >
+
+ <TextView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="Enter Filename" />
+
+ <EditText
+ android:id="@+id/filename"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:focusable="true" />
+ <Button
+ android:id="@+id/uploadfile"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="Upload" />
+
+ <RelativeLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="top"
+ android:orientation="horizontal"
+ android:background="#333333"
+ >
+
+ <ImageView
+ android:id="@+id/thumbnail"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:layout_margin="5dp"
+ android1:layout_marginBottom="5dp" />
+
+ </RelativeLayout>
+ <LinearLayout
+ android:id="@+id/content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+ <Button
+ android:id="@+id/rotateleft"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="Rotate Left"
+ />
+ <Button
+ android:id="@+id/rotateright"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="Rotate Right"
+ />
+
+ <CheckBox
+ android1:id="@+id/resize"
+ android1:layout_width="wrap_content"
+ android1:layout_height="wrap_content"
+ android1:checked="true"
+ android1:text="Reduce Size" />
+
+ </LinearLayout>
+
+
+ </LinearLayout>
+</ScrollView>
+</RelativeLayout>
+
+
+
diff --git a/app/src/main/res/layout/soundrecorder.xml b/app/src/main/res/layout/soundrecorder.xml
new file mode 100644
index 0000000..58b62a2
--- /dev/null
+++ b/app/src/main/res/layout/soundrecorder.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+ <Button
+ android:id="@+id/Record"
+ android:layout_height="wrap_content"
+ android:layout_width="fill_parent"
+ android:text="Record"
+/>
+ <Button
+ android:id="@+id/Stop"
+ android:layout_height="wrap_content"
+ android:layout_width="fill_parent"
+ android:text="Stop"
+/>
+</LinearLayout>
diff --git a/app/src/main/res/layout/upload.xml b/app/src/main/res/layout/upload.xml
new file mode 100644
index 0000000..d56345d
--- /dev/null
+++ b/app/src/main/res/layout/upload.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ >
+<ListView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/hello"
+ />
+ <ProgressBar
+android:id="@+id/ProgressBar01"
+android:indeterminateOnly="false"
+android:progressDrawable="@android:drawable/progress_horizontal"
+android:indeterminateDrawable="@android:drawable/progress_indeterminate_horizontal"
+android:minHeight="20dip"
+android:maxHeight="20dip" android:layout_width="fill_parent"/>
+</LinearLayout>
diff --git a/app/src/main/res/layout/uploadlocation.xml b/app/src/main/res/layout/uploadlocation.xml
new file mode 100644
index 0000000..69251e4
--- /dev/null
+++ b/app/src/main/res/layout/uploadlocation.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+ <TextView
+ android:text="Current Location"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ />
+ <TextView
+ android:id="@+id/location"
+ android:text=""
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ />
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ >
+ <Button
+ android:id="@+id/getlocation"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="Get Location"
+/>
+ <Button
+ android:id="@+id/uploadlocation"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="Upload"
+/>
+</LinearLayout>
+</LinearLayout>
diff --git a/app/src/main/res/menu/menu.xml b/app/src/main/res/menu/menu.xml
new file mode 100644
index 0000000..1d60246
--- /dev/null
+++ b/app/src/main/res/menu/menu.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu
+ xmlns:android="http://schemas.android.com/apk/res/android">
+<item android:id="@+id/menuBrowse"
+ android:icon="@android:drawable/ic_menu_upload"
+ android:title="Upload Files"></item>
+<item android:id="@+id/menuRecord"
+ android:icon="@android:drawable/ic_btn_speak_now"
+ android:title="Record Audio"></item>
+<item android:id="@+id/menuLocate"
+ android:icon="@android:drawable/ic_menu_compass"
+ android:title="Send Location"></item>
+<item android:id="@+id/menuExit"
+ android:icon="@android:drawable/ic_menu_close_clear_cancel"
+ android:title="Exit"></item>
+</menu>
diff --git a/app/src/main/res/values-v11/strings.xml b/app/src/main/res/values-v11/strings.xml
new file mode 100644
index 0000000..9e611cf
--- /dev/null
+++ b/app/src/main/res/values-v11/strings.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="hello">Hello World, fileBrowser!</string>
+ <string name="app_name">blauploader</string>
+ <string name="cancel">Cancel</string>
+</resources>
diff --git a/app/src/main/res/values-v11/themes.xml b/app/src/main/res/values-v11/themes.xml
new file mode 100644
index 0000000..933554f
--- /dev/null
+++ b/app/src/main/res/values-v11/themes.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <style name="LightTheme" parent="@android:style/Theme.Holo.Light">
+ <!-- Any customizations for your app running on pre-3.0 devices here -->
+ </style>
+</resources> \ No newline at end of file
diff --git a/app/src/main/res/values-v14/strings.xml b/app/src/main/res/values-v14/strings.xml
new file mode 100644
index 0000000..9e611cf
--- /dev/null
+++ b/app/src/main/res/values-v14/strings.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="hello">Hello World, fileBrowser!</string>
+ <string name="app_name">blauploader</string>
+ <string name="cancel">Cancel</string>
+</resources>
diff --git a/app/src/main/res/values-v14/themes.xml b/app/src/main/res/values-v14/themes.xml
new file mode 100644
index 0000000..393e3e2
--- /dev/null
+++ b/app/src/main/res/values-v14/themes.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <style name="LightTheme" parent="@android:style/Theme.Holo.Light.DarkActionBar">
+ <!-- Any customizations for your app running on pre-3.0 devices here -->
+ </style>
+</resources> \ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..0d6c9c3
--- /dev/null
+++ b/app/src/main/res/values/strings.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <string name="hello">Hello World, fileBrowser!</string>
+ <string name="app_name">up.org.je</string>
+ <string name="cancel">Cancel</string>
+ <string name="hello_world">Hello world!</string>
+ <string name="menu_settings">Settings</string>
+ <string name="title_activity_main">MainActivity</string>
+
+</resources> \ No newline at end of file
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
new file mode 100644
index 0000000..884ba69
--- /dev/null
+++ b/app/src/main/res/values/themes.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <style name="LightTheme" parent="@android:style/Theme.Light">
+ <!-- Any customizations for your app running on pre-3.0 devices here -->
+ </style>
+</resources> \ No newline at end of file