mirror of https://github.com/gnosygnu/xowa
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
637 lines
28 KiB
637 lines
28 KiB
/*
|
|
XOWA: the XOWA Offline Wiki Application
|
|
Copyright (C) 2012 gnosygnu@gmail.com
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
package gplx.ios; import gplx.*;
|
|
import java.io.*;
|
|
import java.net.HttpURLConnection;
|
|
import java.net.URL;
|
|
import java.net.URLConnection;
|
|
import java.net.URLEncoder;
|
|
import java.nio.*;
|
|
import java.nio.channels.*;
|
|
import java.util.Date;
|
|
|
|
import javax.print.FlavorException;
|
|
import javax.tools.JavaCompiler;
|
|
import gplx.core.criterias.*;
|
|
public class IoEngine_system extends IoEngine_base {
|
|
@Override public String Key() {return IoEngine_.SysKey;}
|
|
@Override public void DeleteDirDeep(IoEngine_xrg_deleteDir args) {utl.DeleteDirDeep(this, args.Url(), args);}
|
|
@Override public void XferDir(IoEngine_xrg_xferDir args) {Io_url trg = args.Trg(); utl.XferDir(this, args.Src(), IoEnginePool.Instance.Get_by(trg.Info().EngineKey()), trg, args);}
|
|
@Override public void XferFil(IoEngine_xrg_xferFil args) {utl.XferFil(this, args);}
|
|
@Override public IoItmDir QueryDirDeep(IoEngine_xrg_queryDir args) {return utl.QueryDirDeep(this, args);}
|
|
@Override public void CopyDir(Io_url src, Io_url trg) {IoEngine_xrg_xferDir.copy_(src, trg).Recur_().Exec();}
|
|
@Override public void MoveDirDeep(IoEngine_xrg_xferDir args) {Io_url trg = args.Trg(); utl.XferDir(this, args.Src(), IoEnginePool.Instance.Get_by(trg.Info().EngineKey()), trg, args);}
|
|
@Override public void DeleteFil_api(IoEngine_xrg_deleteFil args) {
|
|
Io_url url = args.Url();
|
|
File fil = Fil_(url);
|
|
if (!Fil_Exists(fil)) return;
|
|
MarkFileWritable(fil, url, args.ReadOnlyFails(), "DeleteFile");
|
|
DeleteFil_lang(fil, url);
|
|
}
|
|
@Override public boolean ExistsFil_api(Io_url url) {
|
|
File f = new File(url.Xto_api());
|
|
return f.exists();
|
|
}
|
|
@Override public void SaveFilText_api(IoEngine_xrg_saveFilStr mpo) {
|
|
Io_url url = mpo.Url();
|
|
|
|
// encode string
|
|
byte[] textBytes = null;
|
|
textBytes = Bry_.new_u8(mpo.Text());
|
|
|
|
FileChannel fc = null; FileOutputStream fos = null;
|
|
if (!ExistsDir(url.OwnerDir())) CreateDir(url.OwnerDir());
|
|
try {
|
|
// open file
|
|
try {fos = new FileOutputStream(url.Xto_api(), mpo.Append());}
|
|
catch (FileNotFoundException e) {throw Err_Fil_NotFound(e, url);}
|
|
fc = fos.getChannel();
|
|
|
|
// write text
|
|
try {fc.write(ByteBuffer.wrap(textBytes));}
|
|
catch (IOException e) {
|
|
Closeable_close(fc, url, false);
|
|
Closeable_close(fos, url, false);
|
|
throw Err_.new_exc(e, "io", "write data to file failed", "url", url.Xto_api());
|
|
}
|
|
if (!Op_sys.Cur().Tid_is_drd()) {
|
|
File fil = new File(url.Xto_api());
|
|
IoEngine_system_xtn.SetExecutable(fil, true);
|
|
}
|
|
}
|
|
finally {
|
|
// cleanup
|
|
Closeable_close(fc, url, false);
|
|
Closeable_close(fos, url, false);
|
|
}
|
|
}
|
|
@Override public String LoadFilStr(IoEngine_xrg_loadFilStr args) {
|
|
Io_url url = args.Url(); String url_str = url.Xto_api();
|
|
boolean file_exists = ExistsFil_api(url); // check if file exists first to avoid throwing exception; note that most callers pass Missing_ignored; DATE:2015-02-24
|
|
if (!file_exists) {
|
|
if (args.MissingIgnored()) return "";
|
|
else throw Err_Fil_NotFound(url);
|
|
}
|
|
// get reader for file
|
|
InputStream stream = null;
|
|
try {stream = new FileInputStream(url_str);}
|
|
catch (FileNotFoundException e) {
|
|
if (args.MissingIgnored()) return "";
|
|
throw Err_Fil_NotFound(e, url);
|
|
}
|
|
return Load_from_stream_as_str(stream, url_str);
|
|
}
|
|
@SuppressWarnings("resource") public static String Load_from_stream_as_str(InputStream stream, String url_str) {
|
|
InputStreamReader reader = null;
|
|
try {reader = new InputStreamReader(stream, IoEngineArgs.Instance.LoadFilStr_Encoding);}
|
|
catch (UnsupportedEncodingException e) {
|
|
Closeable_close(stream, url_str, false);
|
|
throw Err_text_unsupported_encoding(IoEngineArgs.Instance.LoadFilStr_Encoding, "", url_str, e);
|
|
}
|
|
|
|
// make other objects
|
|
char[] readerBuffer = new char[IoEngineArgs.Instance.LoadFilStr_BufferSize];
|
|
int pos = 0;
|
|
StringWriter sw = new StringWriter();
|
|
|
|
// transfer data
|
|
while (true) {
|
|
try {pos = reader.read(readerBuffer);}
|
|
catch (IOException e) {
|
|
try {
|
|
stream.close();
|
|
reader.close();
|
|
}
|
|
catch (IOException e2) {}
|
|
throw Err_.new_exc(e, "io", "read data from file failed", "url", url_str, "pos", pos);
|
|
}
|
|
if (pos == -1) break;
|
|
sw.write(readerBuffer, 0, pos);
|
|
}
|
|
|
|
// cleanup
|
|
Closeable_close(stream, url_str, false);
|
|
Closeable_close(reader, url_str, false);
|
|
return sw.toString();
|
|
}
|
|
@Override public boolean ExistsDir(Io_url url) {return new File(url.Xto_api()).exists();}
|
|
@Override public void CreateDir(Io_url url) {new File(url.Xto_api()).mkdirs();}
|
|
@Override public void DeleteDir(Io_url url) {
|
|
File dir = new File(url.Xto_api());
|
|
if (!dir.exists()) return;
|
|
boolean rv = dir.delete();
|
|
if (!rv) throw Err_.new_(IoEngineArgs.Instance.Err_IoException, "delete dir failed", "url", url.Xto_api());
|
|
}
|
|
@Override public IoItmDir QueryDir(Io_url url) {
|
|
IoItmDir rv = IoItmDir_.scan_(url);
|
|
File dirInfo = new File(url.Xto_api());
|
|
if (!dirInfo.exists()) {
|
|
rv.Exists_set(false);
|
|
return rv;
|
|
}
|
|
IoUrlInfo urlInfo = url.Info();
|
|
File[] subItmAry = dirInfo.listFiles();
|
|
if (subItmAry == null) return rv; // directory has no files
|
|
for (int i = 0; i < subItmAry.length; i++) {
|
|
File subItm = subItmAry[i];
|
|
if (subItm.isFile()) {
|
|
IoItmFil subFil = QueryMkr_fil(urlInfo, subItm);
|
|
rv.SubFils().Add(subFil);
|
|
}
|
|
else {
|
|
IoItmDir subDir = QueryMkr_dir(urlInfo, subItm);
|
|
rv.SubDirs().Add(subDir);
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
IoItmFil QueryMkr_fil(IoUrlInfo urlInfo, File apiFil) {
|
|
Io_url filUrl = Io_url_.new_inf_(apiFil.getPath(), urlInfo); // NOTE: may throw PathTooLongException when url is > 248 (exception messages states 260)
|
|
long fil_len = apiFil.exists() ? apiFil.length() : IoItmFil.Size_invalid; // NOTE: if file doesn't exist, set len to -1; needed for "boolean Exists() {return size != Size_Invalid;}"; DATE:2014-06-21
|
|
IoItmFil rv = IoItmFil_.new_(filUrl, fil_len, DateAdp_.MinValue, DateAdp_.unixtime_lcl_ms_(apiFil.lastModified()));
|
|
rv.ReadOnly_(!apiFil.canWrite());
|
|
return rv;
|
|
}
|
|
IoItmDir QueryMkr_dir(IoUrlInfo urlInfo, File apiDir) {
|
|
Io_url dirUrl = Io_url_.new_inf_(apiDir.getPath() + urlInfo.DirSpr(), urlInfo); // NOTE: may throw PathTooLongException when url is > 248 (exception messages states 260)
|
|
return IoItmDir_.scan_(dirUrl);
|
|
}
|
|
@Override public IoItmFil QueryFil(Io_url url) {
|
|
File fil = new File(url.Xto_api());
|
|
return QueryMkr_fil(url.Info(), fil);
|
|
}
|
|
@Override public void UpdateFilAttrib(Io_url url, IoItmAttrib atr) {
|
|
File f = new File(url.Xto_api());
|
|
boolean rv = true;
|
|
if (atr.ReadOnly() != Fil_ReadOnly(f)) {
|
|
if (atr.ReadOnly())
|
|
rv = f.setReadOnly();
|
|
else {
|
|
if (!Op_sys.Cur().Tid_is_drd())
|
|
IoEngine_system_xtn.SetWritable(f, true);
|
|
}
|
|
if (!rv) throw Err_.new_(IoEngineArgs.Instance.Err_IoException, "set file attribute failed", "attribute", "readOnly", "cur", Fil_ReadOnly(f), "new", atr.ReadOnly(), "url", url.Xto_api());
|
|
}
|
|
if (atr.Hidden() != f.isHidden()) {
|
|
//Runtime.getRuntime().exec("attrib +H myHiddenFile.java");
|
|
}
|
|
}
|
|
@Override public void UpdateFilModifiedTime(Io_url url, DateAdp modified) {
|
|
File f = new File(url.Xto_api());
|
|
long timeInt = modified.UnderDateTime().getTimeInMillis();
|
|
// if (timeInt < 0) {
|
|
// UsrDlg_._.Notify("{0} {1}", url.Xto_api(), timeInt);
|
|
// return;
|
|
// }
|
|
if (!f.setLastModified(timeInt)) {
|
|
if (Fil_ReadOnly(f)) {
|
|
boolean success = false;
|
|
try {
|
|
UpdateFilAttrib(url, IoItmAttrib.normal_());
|
|
success = f.setLastModified(timeInt);
|
|
}
|
|
finally {
|
|
UpdateFilAttrib(url, IoItmAttrib.readOnly_());
|
|
}
|
|
if (!success) throw Err_.new_wo_type("could not update file modified time", "url", url.Xto_api(), "modifiedTime", modified.XtoStr_gplx_long());
|
|
}
|
|
}
|
|
}
|
|
@Override public IoStream OpenStreamRead(Io_url url) {return IoStream_base.new_(url, IoStream_.Mode_rdr);}
|
|
@Override public IoStream OpenStreamWrite(IoEngine_xrg_openWrite args) {
|
|
Io_url url = args.Url();
|
|
if (!ExistsFil_api(url)) SaveFilText_api(IoEngine_xrg_saveFilStr.new_(url, ""));
|
|
return IoStream_base.new_(url, args.Mode());
|
|
}
|
|
@SuppressWarnings("resource")
|
|
@Override public void CopyFil(IoEngine_xrg_xferFil args) {
|
|
// TODO:JAVA6 hidden property ignored; 1.6 does not allow OS-independent way of setting isHidden (wnt only possible through jni)
|
|
boolean overwrite = args.Overwrite();
|
|
Io_url srcUrl = args.Src(), trgUrl = args.Trg();
|
|
File srcFil = new File(srcUrl.Xto_api()), trgFil = new File(trgUrl.Xto_api());
|
|
if (trgFil.isFile()) { // trgFil exists; check if overwrite set and trgFil is writable
|
|
Chk_TrgFil_Overwrite(overwrite, trgUrl);
|
|
MarkFileWritable(trgFil, trgUrl, args.ReadOnlyFails(), "copy");
|
|
}
|
|
else { // trgFil doesn't exist; must create file first else fileNotFound exception thrown
|
|
// if (overwrite) throw Err_
|
|
boolean rv = true; //Exception exc = null;
|
|
if (!ExistsDir(trgUrl.OwnerDir())) CreateDir(trgUrl.OwnerDir());
|
|
try {
|
|
trgFil.createNewFile();
|
|
if (!Op_sys.Cur().Tid_is_drd())
|
|
IoEngine_system_xtn.SetExecutable(trgFil, true);
|
|
}
|
|
catch (IOException e) {
|
|
// exc = e;
|
|
rv = false;
|
|
}
|
|
if (!rv)
|
|
throw Err_.new_wo_type("create file failed", "trg", trgUrl.Xto_api());
|
|
}
|
|
FileInputStream srcStream = null; FileOutputStream trgStream = null;
|
|
FileChannel srcChannel = null, trgChannel = null;
|
|
try {
|
|
// make objects
|
|
try {srcStream = new FileInputStream(srcFil);}
|
|
catch (FileNotFoundException e) {throw IoErr.FileNotFound("copy", srcUrl);}
|
|
try {trgStream = new FileOutputStream(trgFil);}
|
|
catch (FileNotFoundException e) {
|
|
trgStream = TryToUnHideFile(trgFil, trgUrl);
|
|
if (trgStream == null)
|
|
throw IoErr.FileNotFound("copy", trgUrl);
|
|
// else
|
|
// wasHidden = true;
|
|
}
|
|
srcChannel = srcStream.getChannel();
|
|
trgChannel = trgStream.getChannel();
|
|
|
|
// transfer data
|
|
long pos = 0, count = 0, read = 0;
|
|
try {count = srcChannel.size();}
|
|
catch (IOException e) {throw Err_.new_exc(e, "io", "size failed", "src", srcUrl.Xto_api());}
|
|
int totalBufferSize = IoEngineArgs.Instance.LoadFilStr_BufferSize;
|
|
long transferSize = (count > totalBufferSize) ? totalBufferSize : count; // transfer as much as fileSize, but limit to LoadFilStr_BufferSize
|
|
while (pos < count) {
|
|
try {read = trgChannel.transferFrom(srcChannel, pos, transferSize);}
|
|
catch (IOException e) {
|
|
Closeable_close(srcChannel, srcUrl, false);
|
|
Closeable_close(trgChannel, trgUrl, false);
|
|
Closeable_close(srcStream, srcUrl, false);
|
|
Closeable_close(trgStream, srcUrl, false);
|
|
throw Err_.new_exc(e, "io", "transfer data failed", "src", srcUrl.Xto_api(), "trg", trgUrl.Xto_api());
|
|
}
|
|
if (read == -1) break;
|
|
pos += read;
|
|
}
|
|
// if (wasHidden)
|
|
//
|
|
}
|
|
finally {
|
|
// cleanup
|
|
Closeable_close(srcChannel, srcUrl, false);
|
|
Closeable_close(trgChannel, trgUrl, false);
|
|
Closeable_close(srcStream, srcUrl, false);
|
|
Closeable_close(trgStream, srcUrl, false);
|
|
}
|
|
UpdateFilModifiedTime(trgUrl, QueryFil(srcUrl).ModifiedTime()); // must happen after file is closed
|
|
}
|
|
FileOutputStream TryToUnHideFile(File trgFil, Io_url trgUrl) {
|
|
FileOutputStream trgStream = null;
|
|
if (trgFil.exists()) { // WORKAROUND: java fails when writing to hidden files; unmark hidden and try again
|
|
Process p = null;
|
|
try {
|
|
String d = "attrib -H \"" + trgUrl.Xto_api() + "\"";
|
|
p = Runtime.getRuntime().exec(d);
|
|
} catch (IOException e1) {
|
|
e1.printStackTrace();
|
|
}
|
|
try {
|
|
p.waitFor();
|
|
} catch (InterruptedException e1) {
|
|
// TODO Auto-generated catch block
|
|
e1.printStackTrace();
|
|
}
|
|
try {trgStream = new FileOutputStream(trgFil);}
|
|
catch (FileNotFoundException e) {
|
|
return null;
|
|
}
|
|
}
|
|
return trgStream;
|
|
}
|
|
@Override public void MoveFil(IoEngine_xrg_xferFil args) {
|
|
Io_url srcUrl = args.Src(), trgUrl = args.Trg();
|
|
String src_api = srcUrl.Xto_api(), trg_api = trgUrl.Xto_api();
|
|
if (String_.Eq(src_api, trg_api)) return; // ignore command if src and trg is same; EX: C:\a.txt -> C:\a.txt should be noop
|
|
File srcFil = new File(src_api), trgFil = new File(trg_api);
|
|
|
|
// if drive is same, then rename file
|
|
if (String_.Eq(srcUrl.OwnerRoot().Raw(), trgUrl.OwnerRoot().Raw())) {
|
|
boolean overwrite = args.Overwrite();
|
|
if (!srcFil.exists() && args.MissingFails()) throw IoErr.FileNotFound("move", srcUrl);
|
|
if (trgFil.exists()) {
|
|
Chk_TrgFil_Overwrite(overwrite, trgUrl);
|
|
MarkFileWritable(trgFil, trgUrl, args.ReadOnlyFails(), "move");
|
|
DeleteFil_lang(trgFil, args.Trg()); // overwrite is specified and file is writable -> delete
|
|
}
|
|
if (!ExistsDir(trgUrl.OwnerDir())) CreateDir(trgUrl.OwnerDir());
|
|
srcFil.renameTo(trgFil);
|
|
}
|
|
// else copy fil and delete
|
|
else {
|
|
if (!srcFil.exists() && !args.MissingFails()) return;
|
|
CopyFil(args);
|
|
DeleteFil_lang(srcFil, srcUrl);
|
|
}
|
|
}
|
|
void Chk_TrgFil_Overwrite(boolean overwrite, Io_url trg) {
|
|
if (!overwrite)
|
|
throw Err_.new_invalid_op("trgFile exists but overwriteFlag not set").Args_add("trg", trg.Xto_api());
|
|
}
|
|
@Override public void MoveDir(Io_url src, Io_url trg) {
|
|
String srcStr = src.Xto_api(), trgStr = trg.Xto_api();
|
|
File srcFil = new File(srcStr), trgFil = new File(trgStr);
|
|
if (trgFil.exists()) {throw Err_.new_invalid_op("cannot move dir if trg exists").Args_add("src", src, "trg", trg);}
|
|
if (String_.Eq(src.OwnerRoot().Raw(), trg.OwnerRoot().Raw())) {
|
|
srcFil.renameTo(trgFil);
|
|
}
|
|
else {
|
|
XferDir(IoEngine_xrg_xferDir.copy_(src, trg));
|
|
}
|
|
}
|
|
protected static void Closeable_close(Closeable closeable, Io_url url, boolean throwErr) {Closeable_close(closeable, url.Xto_api(), throwErr);}
|
|
protected static void Closeable_close(Closeable closeable, String url_str, boolean throwErr) {
|
|
if (closeable == null) return;
|
|
try {closeable.close();}
|
|
catch (IOException e) {
|
|
if (throwErr)
|
|
throw Err_.new_exc(e, "io", "close object failed", "class", Type_adp_.NameOf_obj(closeable), "url", url_str);
|
|
// else
|
|
// UsrDlg_._.Finally("failed to close FileChannel", "url", url, "apiErr", Err_.Message_err_arg(e));
|
|
}
|
|
}
|
|
|
|
File Fil_(Io_url url) {return new File(url.Xto_api());}
|
|
boolean Fil_Exists(File fil) {return fil.exists();}
|
|
boolean Fil_ReadOnly(File fil) {return !fil.canWrite();}
|
|
boolean Fil_Delete(File fil) {return fil.delete();}
|
|
void Fil_Writable(File fil) {
|
|
if (!Op_sys.Cur().Tid_is_drd())
|
|
IoEngine_system_xtn.SetWritable(fil, true);
|
|
}
|
|
private static Err Err_text_unsupported_encoding(String encodingName, String text, String url_str, Exception e) {
|
|
return Err_.new_exc(e, "io", "text is in unsupported encoding").Args_add("encodingName", encodingName, "text", text, "url", url_str);
|
|
}
|
|
boolean user_agent_needs_resetting = true;
|
|
@Override public Io_stream_rdr DownloadFil_as_rdr(IoEngine_xrg_downloadFil xrg) {
|
|
Io_stream_rdr_http rdr = new Io_stream_rdr_http(xrg);
|
|
rdr.Open();
|
|
return rdr;
|
|
}
|
|
@Override public boolean DownloadFil(IoEngine_xrg_downloadFil xrg) {
|
|
IoStream trg_stream = null;
|
|
java.io.BufferedInputStream src_stream = null;
|
|
java.net.URL src_url = null;
|
|
HttpURLConnection src_conn = null;
|
|
if (user_agent_needs_resetting) {user_agent_needs_resetting = false; System.setProperty("http.agent", "");}
|
|
boolean exists = Io_mgr.Instance.ExistsDir(xrg.Trg().OwnerDir());
|
|
Gfo_usr_dlg prog_dlg = null;
|
|
String src_str = xrg.Src();
|
|
Io_download_fmt xfer_fmt = xrg.Download_fmt();
|
|
prog_dlg = xfer_fmt.Usr_dlg();
|
|
if (!Web_access_enabled) {
|
|
if (prog_dlg != null) {
|
|
if (session_fil == null) session_fil = prog_dlg.Log_wkr().Session_dir().GenSubFil("internet.txt");
|
|
prog_dlg.Log_wkr().Log_msg_to_url_fmt(session_fil, "download disabled: src='~{0}' trg='~{1}'", xrg.Src(), xrg.Trg().Raw());
|
|
}
|
|
return false;
|
|
}
|
|
try {
|
|
trg_stream = Io_mgr.Instance.OpenStreamWrite(xrg.Trg());
|
|
src_url = new java.net.URL(src_str);
|
|
src_conn = (HttpURLConnection)src_url.openConnection();
|
|
// src_conn.setReadTimeout(5000); // do not set; if file does not exist, will wait 5 seconds before timing out; want to fail immediately
|
|
String user_agent = xrg.User_agent(); if (user_agent != null) src_conn.setRequestProperty("User-Agent", user_agent);
|
|
long content_length = Long_.parse_or(src_conn.getHeaderField("Content-Length"), IoItmFil.Size_invalid_int);
|
|
xrg.Src_content_length_(content_length);
|
|
if (xrg.Src_last_modified_query()) // NOTE: only files will have last modified (api calls will not); if no last_modified, then src_conn will throw get nullRef; avoid nullRef
|
|
xrg.Src_last_modified_(DateAdp_.unixtime_lcl_ms_(src_conn.getLastModified()));
|
|
if (xrg.Exec_meta_only()) return true;
|
|
src_stream = new java.io.BufferedInputStream(src_conn.getInputStream());
|
|
if (!exists) {
|
|
Io_mgr.Instance.CreateDir(xrg.Trg().OwnerDir()); // dir must exist for OpenStreamWrite; create dir at last possible moment in case stream does not exist.
|
|
}
|
|
byte[] download_bfr = new byte[Download_bfr_len]; // NOTE: download_bfr was originally member variable; DATE:2013-05-03
|
|
xfer_fmt.Bgn(content_length);
|
|
int count = 0;
|
|
while ((count = src_stream.read(download_bfr, 0, Download_bfr_len)) != -1) {
|
|
if (xrg.Prog_cancel()) {
|
|
src_stream.close();
|
|
trg_stream.Rls();
|
|
Io_mgr.Instance.DeleteFil(xrg.Trg());
|
|
}
|
|
xfer_fmt.Prog(count);
|
|
trg_stream.Write(download_bfr, 0, count);
|
|
}
|
|
if (prog_dlg != null) {
|
|
xfer_fmt.Term();
|
|
if (session_fil == null) session_fil = prog_dlg.Log_wkr().Session_dir().GenSubFil("internet.txt");
|
|
prog_dlg.Log_wkr().Log_msg_to_url_fmt(session_fil, "download pass: src='~{0}' trg='~{1}'", src_str, xrg.Trg().Raw());
|
|
}
|
|
return true;
|
|
}
|
|
catch (Exception exc) {
|
|
xrg.Rslt_err_(exc);
|
|
if (Type_adp_.Eq_typeSafe(exc, java.net.UnknownHostException.class)) xrg.Rslt_(IoEngine_xrg_downloadFil.Rslt_fail_host_not_found);
|
|
else if (Type_adp_.Eq_typeSafe(exc, java.io.FileNotFoundException.class)) xrg.Rslt_(IoEngine_xrg_downloadFil.Rslt_fail_file_not_found);
|
|
else xrg.Rslt_(IoEngine_xrg_downloadFil.Rslt_fail_unknown);
|
|
if (prog_dlg != null && !xrg.Prog_cancel()) {
|
|
if (session_fil == null) session_fil = prog_dlg.Log_wkr().Session_dir().GenSubFil("internet.txt");
|
|
prog_dlg.Log_wkr().Log_msg_to_url_fmt(session_fil, "download fail: src='~{0}' trg='~{1}' error='~{2}'", src_str, xrg.Trg().Raw(), Err_.Message_lang(exc));
|
|
}
|
|
if (trg_stream != null) {
|
|
try {
|
|
trg_stream.Rls();
|
|
DeleteFil_api(IoEngine_xrg_deleteFil.new_(xrg.Trg()));
|
|
}
|
|
catch (Exception e2) {Err_.Noop(e2);}
|
|
}
|
|
return false;
|
|
}
|
|
finally {
|
|
xrg.Prog_running_(false);
|
|
try {
|
|
if (src_stream != null) src_stream.close();
|
|
if (src_conn != null) src_conn.disconnect();
|
|
src_conn.getInputStream().close();
|
|
} catch (Exception exc) {
|
|
Err_.Noop(exc);
|
|
}
|
|
if (trg_stream != null) trg_stream.Rls();
|
|
}
|
|
} Io_url session_fil; Bry_bfr prog_fmt_bfr;
|
|
byte[] download_bfr; static final int Download_bfr_len = Io_mgr.Len_kb * 128;
|
|
public static Err Err_Fil_NotFound(Io_url url) {
|
|
return Err_.new_(IoEngineArgs.Instance.Err_FileNotFound, "file not found", "url", url.Xto_api()).Trace_ignore_add_1_();
|
|
}
|
|
public static Err Err_Fil_NotFound(Exception e, Io_url url) {
|
|
return Err_.new_exc(e, "io", "file not found", "url", url.Xto_api()).Trace_ignore_add_1_();
|
|
}
|
|
void MarkFileWritable(File fil, Io_url url, boolean readOnlyFails, String op) {
|
|
if (Fil_ReadOnly(fil)) {
|
|
if (readOnlyFails) // NOTE: java will always allow final files to be deleted; programmer api is responsible for check
|
|
throw Err_.new_(IoEngineArgs.Instance.Err_ReadonlyFileNotWritable, "writable operation attempted on readOnly file", "op", op, "url", url.Xto_api());
|
|
else
|
|
Fil_Writable(fil);
|
|
}
|
|
}
|
|
void DeleteFil_lang(File fil, Io_url url) {
|
|
boolean rv = Fil_Delete(fil);
|
|
if (!rv)
|
|
throw Err_.new_(IoEngineArgs.Instance.Err_IoException, "file not deleted", "url", url.Xto_api());
|
|
}
|
|
IoEngineUtl utl = IoEngineUtl.new_();
|
|
public static IoEngine_system new_() {return new IoEngine_system();} IoEngine_system() {}
|
|
static final String GRP_KEY = "Io_engine";
|
|
public static boolean Web_access_enabled = true;
|
|
}
|
|
class IoEngineArgs {
|
|
public int LoadFilStr_BufferSize = 4096 * 256;
|
|
public String LoadFilStr_Encoding = "UTF-8";
|
|
public String Err_ReadonlyFileNotWritable = "gplx.ios.ReadonlyFileNotWritable";
|
|
public String Err_FileNotFound = "gplx.ios.FileNotFound";
|
|
public String Err_IoException = "gplx.ios.IoException";
|
|
public static final IoEngineArgs Instance = new IoEngineArgs();
|
|
}
|
|
class IoEngine_system_xtn {
|
|
// PATCH.DROID:VerifyError if file.setExecutable is referenced directly in IoEngine_system. However, if placed in separate class
|
|
public static void SetExecutable(java.io.File file, boolean v) {file.setExecutable(v);}
|
|
public static void SetWritable(java.io.File file, boolean v) {file.setWritable(v);}
|
|
}
|
|
class Io_download_http {
|
|
public static boolean User_agent_reset_needed = true;
|
|
public static void User_agent_reset() {
|
|
User_agent_reset_needed = false;
|
|
System.setProperty("http.agent", ""); // need to set http.agent to '' in order for "User-agent" to take effect
|
|
}
|
|
public static void Save_to_fsys(IoEngine_xrg_downloadFil xrg) {
|
|
Io_stream_rdr_http rdr = new Io_stream_rdr_http(xrg);
|
|
IoStream trg_stream = null;
|
|
try {
|
|
boolean exists = Io_mgr.Instance.ExistsDir(xrg.Trg().OwnerDir());
|
|
if (!exists)
|
|
Io_mgr.Instance.CreateDir(xrg.Trg().OwnerDir()); // dir must exist for OpenStreamWrite; create dir at last possible moment in case stream does not exist.
|
|
trg_stream = Io_mgr.Instance.OpenStreamWrite(xrg.Trg());
|
|
byte[] bfr = new byte[Download_bfr_len];
|
|
rdr.Open();
|
|
while (rdr.Read(bfr, 0, Download_bfr_len) != Read_done) {
|
|
}
|
|
}
|
|
finally {
|
|
rdr.Rls();
|
|
if (trg_stream != null) trg_stream.Rls();
|
|
}
|
|
if (xrg.Rslt() != IoEngine_xrg_downloadFil.Rslt_pass)
|
|
Io_mgr.Instance.DeleteFil_args(xrg.Trg()).MissingFails_off().Exec();
|
|
}
|
|
public static final int Read_done = -1;
|
|
public static final int Download_bfr_len = Io_mgr.Len_kb * 128;
|
|
}
|
|
class Io_stream_rdr_http implements Io_stream_rdr {
|
|
public Io_stream_rdr_http(IoEngine_xrg_downloadFil xrg) {
|
|
this.xrg = xrg;
|
|
} private IoEngine_xrg_downloadFil xrg;
|
|
public byte Tid() {return Io_stream_.Tid_raw;}
|
|
public boolean Exists() {return exists;} private boolean exists = false;
|
|
public Io_url Url() {return url;} public Io_stream_rdr Url_(Io_url v) {url = v; return this;} private Io_url url;
|
|
public long Len() {return len;} public Io_stream_rdr Len_(long v) {len = v; return this;} private long len = IoItmFil.Size_invalid; // NOTE: must default size to -1; DATE:2014-06-21
|
|
private String src_str; private HttpURLConnection src_conn; private java.io.BufferedInputStream src_stream;
|
|
private Io_download_fmt xfer_fmt; private Gfo_usr_dlg prog_dlg;
|
|
private boolean read_done = true, read_failed = false;
|
|
public Io_stream_rdr Open() {
|
|
if (Io_download_http.User_agent_reset_needed) Io_download_http.User_agent_reset();
|
|
if (!IoEngine_system.Web_access_enabled) {
|
|
read_done = read_failed = true;
|
|
if (prog_dlg != null)
|
|
prog_dlg.Log_wkr().Log_msg_to_url_fmt(session_fil, "download disabled: src='~{0}' trg='~{1}'", xrg.Src(), xrg.Trg().Raw());
|
|
return this;
|
|
}
|
|
src_str = xrg.Src();
|
|
xfer_fmt = xrg.Download_fmt(); prog_dlg = xfer_fmt.Usr_dlg();
|
|
try {
|
|
src_conn = (HttpURLConnection)new java.net.URL(src_str).openConnection();
|
|
String user_agent = xrg.User_agent();
|
|
if (user_agent != null) src_conn.setRequestProperty("User-Agent", user_agent); // NOTE: must be set right after openConnection
|
|
// src_conn.setReadTimeout(5000); // do not set; if file does not exist, will wait 5 seconds before timing out; want to fail immediately
|
|
long content_length = Long_.parse_or(src_conn.getHeaderField("Content-Length"), IoItmFil.Size_invalid_int);
|
|
xrg.Src_content_length_(content_length);
|
|
this.len = content_length;
|
|
if (xrg.Src_last_modified_query()) // NOTE: only files will have last modified (api calls will not); if no last_modified, then src_conn will throw get nullRef; avoid nullRef
|
|
xrg.Src_last_modified_(DateAdp_.unixtime_lcl_ms_(src_conn.getLastModified()));
|
|
if (xrg.Exec_meta_only()) {
|
|
read_done = true;
|
|
return this;
|
|
}
|
|
read_done = false;
|
|
this.exists = Int_.In(src_conn.getResponseCode(), 200, 301); // ASSUME: response code of 200 (OK) or 301 (Redirect) means that file exists; note that content_length seems to always be -1; DATE:2015-05-20
|
|
src_stream = new java.io.BufferedInputStream(src_conn.getInputStream());
|
|
xfer_fmt.Bgn(content_length);
|
|
}
|
|
catch (Exception e) {Err_handle(e);}
|
|
return this;
|
|
}
|
|
public void Open_mem(byte[] v) {}
|
|
public Object Under() {return src_stream;}
|
|
public int Read(byte[] bry, int bgn, int len) {
|
|
if (read_done) return Io_download_http.Read_done;
|
|
if (xrg.Prog_cancel()) {read_failed = true; return Io_download_http.Read_done;}
|
|
try {
|
|
int read = src_stream.read(bry, bgn, len);
|
|
xfer_fmt.Prog(read);
|
|
return read;
|
|
}
|
|
catch (Exception e) {
|
|
Err_handle(e);
|
|
return Io_download_http.Read_done;
|
|
}
|
|
}
|
|
private Io_url session_fil = null;
|
|
private boolean rls_done = false;
|
|
public long Skip(long len) {return 0;}
|
|
public void Rls() {
|
|
if (rls_done) return;
|
|
try {
|
|
read_done = true;
|
|
if (prog_dlg != null) {
|
|
xfer_fmt.Term();
|
|
}
|
|
if (session_fil == null && prog_dlg != null) session_fil = prog_dlg.Log_wkr().Session_dir().GenSubFil("internet.txt");
|
|
if (read_failed) {
|
|
}
|
|
else {
|
|
if (prog_dlg != null)
|
|
prog_dlg.Log_wkr().Log_msg_to_url_fmt(session_fil, "download pass: src='~{0}' trg='~{1}'", src_str, xrg.Trg().Raw());
|
|
xrg.Rslt_(IoEngine_xrg_downloadFil.Rslt_pass);
|
|
}
|
|
xrg.Prog_running_(false);
|
|
}
|
|
catch (Exception e) {Err_.Noop(e);} // ignore close errors; also Err_handle calls Rls() so it would be circular
|
|
finally {
|
|
try {if (src_stream != null) src_stream.close();}
|
|
catch (Exception e) {Err_.Noop(e);} // ignore failures when cleaning up
|
|
if (src_conn != null) src_conn.disconnect();
|
|
src_stream = null;
|
|
src_conn = null;
|
|
rls_done = true;
|
|
}
|
|
}
|
|
private void Err_handle(Exception exc) {
|
|
read_done = read_failed = true;
|
|
len = -1;
|
|
xrg.Rslt_err_(exc);
|
|
if (Type_adp_.Eq_typeSafe(exc, java.net.UnknownHostException.class)) xrg.Rslt_(IoEngine_xrg_downloadFil.Rslt_fail_host_not_found);
|
|
else if (Type_adp_.Eq_typeSafe(exc, java.io.FileNotFoundException.class)) xrg.Rslt_(IoEngine_xrg_downloadFil.Rslt_fail_file_not_found);
|
|
else xrg.Rslt_(IoEngine_xrg_downloadFil.Rslt_fail_unknown);
|
|
if (prog_dlg != null && !xrg.Prog_cancel()) {
|
|
if (session_fil == null) session_fil = prog_dlg.Log_wkr().Session_dir().GenSubFil("internet.txt");
|
|
prog_dlg.Log_wkr().Log_msg_to_url_fmt(session_fil, "download fail: src='~{0}' trg='~{1}' error='~{2}'", src_str, xrg.Trg().Raw(), Err_.Message_lang(exc));
|
|
}
|
|
this.Rls();
|
|
}
|
|
}
|