Logiciel de gestion de plan de feu pour les troupes de théâtre amateur.
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.
 
 

736 lines
25 KiB

package misc;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.CookieHandler;
import java.net.CookieManager;
import java.net.CookiePolicy;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.text.MessageFormat;
import java.text.NumberFormat;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Random;
import java.util.TimeZone;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import misc.Config;
import misc.ProgressState;
import misc.Util;
public class RemoteUpdate {
// ========================================
static long delay = 2000;
static CookieManager cookieManager = new CookieManager();
static {
try {
((CookieManager) CookieHandler.getDefault ()).setCookiePolicy (CookiePolicy.ACCEPT_ALL);
} catch (Exception e) {
cookieManager.setCookiePolicy (CookiePolicy.ACCEPT_ALL);
CookieHandler.setDefault (cookieManager);
}
}
// ========================================
static public final String backExtention = "back";
static public final int bufSize = 1024*1024;
static public final SimpleDateFormat dateFormat = new SimpleDateFormat ("yyyyMMdd");
static public final SimpleDateFormat timeFormat = new SimpleDateFormat ("yyyyMMddHHmmss");
static public final SimpleDateFormat displayFormat = new SimpleDateFormat ("dd/MM/yyyy HH:mm:ss");
static public final NumberFormat numberFormat = NumberFormat.getInstance ();
static public enum CheckPeriod { NoCheck, Day, Week, Month, Year; };
static public final List<String> excludeFileName = Arrays.asList ("timestamp");
static public enum TodoFile {LocalRemove, Download, NoChange, Upload, RemoteRemove};
static public class FileInfo {
long date, size;
public FileInfo (long date, long size) { this.date = date; this.size = size; }
public String toString () { return "{"+displayFormat.format (new Date (date))+" "+size+"}"; }
static public String getDate (FileInfo fileInfo) { return fileInfo == null ? "" : displayFormat.format (fileInfo.date); }
static public String getSize (FileInfo fileInfo) { return fileInfo == null ? "" : ""+fileInfo.size; }
};
static public FilenameFilter mirrorFilter = new FilenameFilter () {
public boolean accept (File dir, String name) {
return ! (excludeFileName.contains (name) || name.endsWith ("."+backExtention) || name.endsWith (".new"));
}
};
// ========================================
static public class FileDescriptor {
// ----------------------------------------
public TodoFile todo = TodoFile.NoChange;
public FileInfo local, remote;
public String toString () { return ""+todo+" "+local+" "+remote; }
// ----------------------------------------
public void updateTodo (TodoFile action) {
todo = TodoFile.NoChange;
if (remote == null) {
switch (action) {
case LocalRemove:
case Upload:
todo = action;
}
return;
}
if (local == null) {
switch (action) {
case RemoteRemove:
case Download:
todo = action;
}
return;
}
if (local.size != remote.size) {
switch (action) {
case LocalRemove:
todo = TodoFile.Download;
break;
case RemoteRemove:
todo = TodoFile.Upload;
break;
default:
todo = action;
}
return;
}
if (local.date+delay < remote.date) {
if (action == TodoFile.Download || action == TodoFile.LocalRemove)
todo = TodoFile.Download;
return;
}
if (local.date > remote.date+delay) {
if (action == TodoFile.Upload || action == TodoFile.RemoteRemove)
todo = TodoFile.Upload;
//System.err.print (" "+local.date+" "+remote.date+" "+action);
return;
}
}
// ----------------------------------------
};
// ========================================
public class Mirror {
String token;
File localRepository;
FilenameFilter mirrorFilter = RemoteUpdate.mirrorFilter;
TreeSet<String> excludeFiles;
TreeMap<String, FileInfo> localFiles, remoteFiles;
TreeMap<String, FileDescriptor> allFiles;
// ----------------------------------------
public Mirror (File root, String... params) {
token = params[0];
if (params.length > 1) {
localRepository = root;
for (int i = 1; i < params.length; i++)
localRepository = new File (localRepository, params[i]);
} else
localRepository = new File (root, token);
}
public String toString () { return token+": "+localFiles+" "+remoteFiles; }
public synchronized void update () {
updateRemoteFiles ();
updateLocalFiles ();
if (remoteFiles == null)
return;
TreeSet<String> allFilesName = new TreeSet<String> ();
allFilesName.addAll (localFiles.keySet ());
allFilesName.addAll (remoteFiles.keySet ());
allFiles = new TreeMap<String, FileDescriptor> ();
for (String fileName : allFilesName)
allFiles.put (fileName, new FileDescriptor ());
for (String fileName : localFiles.keySet ())
allFiles.get (fileName).local = localFiles.get (fileName);
for (String fileName : remoteFiles.keySet ())
allFiles.get (fileName).remote = remoteFiles.get (fileName);
}
public void updateTodo (TodoFile action) {
if (allFiles == null)
return;
for (String fileName : allFiles.keySet ()) {
//System.err.print ("coucou : "+fileName+" ");
allFiles.get (fileName).updateTodo (action);
//System.err.println ();
}
}
public long getSize (TreeSet<String> filesName, TodoFile action) {
long result = 0;
if (allFiles == null || action == TodoFile.NoChange)
return result;
boolean remote = false;
switch (action) {
case LocalRemove:
case Download:
remote = true;
}
for (String fileName : filesName) {
FileDescriptor file = allFiles.get (fileName);
if (file == null)
continue;
try {
if (file.todo == action)
result += remote ? file.remote.size : file.local.size;
} catch (Exception e) {
}
}
return result;
}
public TreeSet<String> getFileAction (TodoFile action) {
TreeSet<String> result = new TreeSet<String> ();
if (allFiles == null)
return result;
for (String fileName : allFiles.keySet ())
if (allFiles.get (fileName).todo == action)
result.add (fileName);
return result;
}
public synchronized boolean hasNewVersion () {
update ();
return check (TodoFile.Download).size () > 0;
}
public synchronized TreeSet<String> check (TodoFile action) {
updateTodo (action);
return getFileAction (action);
}
public TreeSet<String> performe (TodoFile action, ProgressState progressState) {
try {
TreeSet<String> files = check (action);
if (progressState != null)
progressState.init (Bundle.getMessage (""+action), (int) getSize (files, action));
switch (action) {
case LocalRemove:
return localRemove (this, files, progressState);
case RemoteRemove:
return remoteRemove (this, files, progressState);
case Download:
return getRemoteFiles (this, files, progressState);
case Upload:
return putRemoteFiles (this, files, progressState);
}
} finally {
if (progressState != null)
progressState.end ();
}
return new TreeSet<String> ();
}
public String getInfo (TreeSet<String> files, TodoFile action) {
return MessageFormat.format (Bundle.getMessage ("DownloadInfo"),
Bundle.getLabel (Util.toCapital (token)),
allFiles == null ? 0 : 1, files.size (), Util.toNumIn2Units (getSize (files, action)));
}
public FileDescriptor getInfo (String fileName) {
return allFiles.get (fileName);
}
// ----------------------------------------
private void updateLocalFiles () {
excludeFiles = new TreeSet<String> ();
localFiles = new TreeMap<String, FileInfo> ();
updateLocalFiles (localRepository, token);
}
private void updateLocalFiles (File localRepository, String name) {
if (localRepository.isFile ()) {
if (excludeFileName.contains (name) || name.endsWith ("."+backExtention)) {
excludeFiles.add (name);
return;
}
localFiles.put (name, new FileInfo (localRepository.lastModified (), localRepository.length ()));
return;
}
if (!localRepository.isDirectory ())
return;
for (String child : localRepository.list ()) {
updateLocalFiles (new File (localRepository, child), name+"/"+child);
}
}
private void getExcludeFiles (File localRepository, String name) {
if (localRepository.isFile ()) {
localFiles.put (name, new FileInfo (localRepository.lastModified (), localRepository.length ()));
return;
}
if (!localRepository.isDirectory ())
return;
for (String child : localRepository.list (mirrorFilter)) {
if (excludeFileName.contains (child))
continue;
updateLocalFiles (new File (localRepository, child), name+"/"+child);
}
}
// ----------------------------------------
private void updateRemoteFiles () {
try {
allFiles = null;
remoteFiles = null;
URLConnection urlConnection = getUrlConnection ("zipList", token);
urlConnection.connect ();
remoteFiles = new TreeMap<String, FileInfo> ();
receiveZip (urlConnection, new ZipLineReader () {
public void readLine (String line) {
ParsePosition pos = new ParsePosition (0);
try {
long date = timeFormat.parse (line, pos).getTime ();
pos.setIndex (pos.getIndex ()+1);
long size = numberFormat.parse (line, pos).longValue ();
String fileName = line.substring (pos.getIndex ()+1);
remoteFiles.put (fileName, new FileInfo (date, size));
} catch (Exception e) {
}
}}, token);
} catch (Exception e) {
System.err.println (e);
//e.printStackTrace ();
}
}
// ----------------------------------------
};
// ========================================
public String protocol;
public String serverName;
public String versionName;
public String requestModel;
public Mirror[] mirrors;
public RemoteUpdate (String protocol, String serverName, String versionName, String requestModel, String []... mirrorsParams) {
this.protocol = protocol;
this.serverName = serverName;
this.versionName = versionName;
this.requestModel = requestModel;
File root = Config.getPWD ().getParentFile ();
this.mirrors = new Mirror[mirrorsParams.length];
int idx = 0;
for (String [] params : mirrorsParams)
mirrors[idx++] = new Mirror (root, params);
}
public CheckPeriod currentPeriod () {
return CheckPeriod.valueOf (CheckPeriod.class, Config.getString ("CheckPeriod", ""+CheckPeriod.Month));
}
public boolean hasNewVersion () {
CheckPeriod period = currentPeriod ();
Date lastCheck = new Date ();
try {
lastCheck = dateFormat.parse (Config.getString ("LastCheck", "20150401"));
} catch (Exception e) {
}
Date today = new Date ();
long age = (today.getTime ()-lastCheck.getTime ())/(24*60*60*1000);
switch (period) {
case NoCheck:
return false;
case Day:
if (age < 1)
return false;
break;
case Week:
if (age < 7)
return false;
break;
case Month:
if (age < 31)
return false;
break;
case Year:
if (age < 366)
return false;
break;
}
Config.setString ("LastCheck", dateFormat.format (today));
for (Mirror mirror : mirrors)
if (mirror.hasNewVersion ())
return true;
return false;
}
// ========================================
private URLConnection getUrlConnection (String cmd, String arg)
throws IOException {
return getUrlConnection (MessageFormat.format (requestModel, versionName, cmd, arg));
}
private URLConnection getUrlConnection (String uri)
throws IOException {
URL url = new URL (protocol+"://"+serverName+uri);
URLConnection urlConnection = url.openConnection ();
urlConnection.setRequestProperty ("User-Agent", "Adecwatt Agent");
urlConnection.setUseCaches (true);
return urlConnection;
}
static private Random random = new Random ();
static private final String charset = "UTF-8";
static private final String LINE_FEED = "\r\n";
static public String getBoundary () {
// XXX we must check not apears in message :-(
return "=-="+Long.toString (random.nextLong (), 36).toUpperCase ()+"=-=";
}
// ========================================
public abstract class ZipBuilder {
public abstract void writeEntry (String token, ZipOutputStream zipOut) throws IOException;
};
public class TextZipBuilder extends ZipBuilder {
private TreeSet<String> filesName;
public TextZipBuilder (TreeSet<String> filesName) { this.filesName = filesName; }
public void writeEntry (String token, ZipOutputStream zipOut) throws IOException {
ZipEntry zipOutEntry = new ZipEntry (token);
zipOut.putNextEntry (zipOutEntry);
PrintWriter entryWriter = new PrintWriter (new OutputStreamWriter (zipOut));
for (String fileName : filesName)
entryWriter.print (fileName+"\n");
entryWriter.flush ();
zipOut.closeEntry ();
}
};
public abstract class ZipFileReader {
public abstract boolean readEntry (ZipInputStream zipIn, String entryName, String token) throws IOException;
};
public abstract class ZipLineReader extends ZipFileReader {
public boolean readEntry (ZipInputStream zipIn, String entryName, String token) throws IOException {
if (! entryName.equals (token))
return true;
BufferedReader in = new BufferedReader (new InputStreamReader (zipIn));
for (;;) {
String line = in.readLine ();
if (line == null)
break;
if ("".equals (line))
continue;
readLine (line);
}
return false;
}
public abstract void readLine (String line);
};
// ========================================
public void receiveZip (URLConnection urlConnection, ZipFileReader zipFileReader, String token) {
try {
String contentType = urlConnection.getContentType ();
if (contentType == null || !contentType.startsWith ("application/zip"))
return;
ZipInputStream zipIn = new ZipInputStream (urlConnection.getInputStream ());
for (;;) {
ZipEntry zipInEntry = zipIn.getNextEntry ();
if (zipInEntry == null)
break;
String entryName = zipInEntry.getName ();
if (!entryName.startsWith (token))
continue;
if (!zipFileReader.readEntry (zipIn, entryName, token))
break;
}
} catch (Exception e) {
e.printStackTrace ();
}
}
// ========================================
public URLConnection sendZip (String cmd, String token, ZipBuilder zipBuilder) {
try {
URLConnection urlConnection = getUrlConnection (cmd, token);
String boundary = getBoundary ();
urlConnection.setRequestProperty ("Content-Type", "multipart/form-data; boundary="+boundary);
urlConnection.setDoOutput (true);
urlConnection.setDoInput (true);
OutputStream httpStream = urlConnection.getOutputStream ();
PrintWriter httpWriter = new PrintWriter (new OutputStreamWriter (httpStream, charset), true);
httpWriter.append ("--"+boundary).append (LINE_FEED);
httpWriter.append ("Content-Disposition: form-data; name=\""+cmd+"\"; filename=\""+cmd+"\"").append (LINE_FEED);
httpWriter.append ("Content-Type: application/zip").append (LINE_FEED);
httpWriter.append ("Content-Transfer-Encoding: binary").append (LINE_FEED);
httpWriter.append (LINE_FEED).flush ();
ByteArrayOutputStream memStream = new ByteArrayOutputStream ();
ZipOutputStream zipOut = new ZipOutputStream (memStream);
zipBuilder.writeEntry (token, zipOut);
zipOut.flush ();
zipOut.close ();
httpWriter.flush ();
httpStream.write (memStream.toByteArray ());
httpWriter.append (LINE_FEED).flush ();
httpWriter.append ("--"+boundary+"--").append(LINE_FEED);
httpWriter.close ();
urlConnection.connect ();
return urlConnection;
} catch (Exception e) {
e.printStackTrace ();
}
return null;
}
// ========================================
public TreeSet<String> localRemove (Mirror mirror, TreeSet<String> filesName, ProgressState progressState) {
if (filesName == null)
filesName = new TreeSet<String> ();
if (mirror.excludeFiles != null)
filesName.addAll (mirror.excludeFiles);
TreeSet<String> removed = new TreeSet<String> ();
for (String fileName : filesName) {
if (! fileName.startsWith (mirror.token))
continue;
fileName = fileName.substring (mirror.token.length ());
File oldFile = new File (mirror.localRepository.getAbsolutePath ()+fileName);
if (!oldFile.delete ())
System.err.println ("coucou pb delete: "+oldFile);
removed.add (fileName);
}
return removed;
}
// ========================================
public TreeSet<String> remoteRemove (Mirror mirror, TreeSet<String> filesName, ProgressState progressState) {
if (filesName == null)
filesName = new TreeSet<String> ();
if (mirror.excludeFiles != null)
filesName.addAll (mirror.excludeFiles);
TreeSet<String> removed = new TreeSet<String> ();
if (filesName.size () < 1)
return removed;
URLConnection urlConnection = sendZip ("zipRemove", mirror.token, new TextZipBuilder (filesName));
receiveZip (urlConnection, new ZipLineReader () {
public void readLine (String line) {
removed.add (line);
}}, mirror.token);
return removed;
}
// ========================================
public TreeSet<String> getRemoteFiles (Mirror mirror, TreeSet<String> filesName, ProgressState progressState) {
TreeSet<String> downloaded = new TreeSet<String> ();
try {
if (filesName.size () < 1)
return downloaded;
URLConnection urlConnection = sendZip ("zipGets", mirror.token, new TextZipBuilder (filesName));
byte[] tmp = new byte[bufSize];
receiveZip (urlConnection, new ZipFileReader () {
public boolean readEntry (ZipInputStream zipIn, String entryName, String token) throws IOException {
File newFile = mirror.localRepository;
for (String item : entryName.substring (mirror.token.length ()).split ("/")) {
if (item == null || item.isEmpty ())
continue;
newFile = new File (newFile, item);
}
File dirFile = newFile.getParentFile ();
dirFile.mkdirs ();
File tmpFile = File.createTempFile ("download", "tmp", dirFile);
tmpFile.deleteOnExit ();
Util.copy (zipIn, new FileOutputStream (tmpFile), tmp, progressState, false, true);
// XXX tmpFile.setLastModified (zipInEntry.getLastModifiedTime ().toMillis ());
tmpFile.setLastModified (mirror.allFiles.get (entryName).remote.date);
if (progressState != null && progressState.isInterrupted ())
return false;
Util.backup (newFile, Util.getExtention (newFile), backExtention);
try {
Files.move (tmpFile.toPath (), newFile.toPath (), StandardCopyOption.REPLACE_EXISTING);
} catch (Exception e) {
}
if (tmpFile.exists ()) {
System.err.println ("can't change open file!");
tmpFile.renameTo (new File (""+newFile+".new"));
}
downloaded.add (entryName);
return true;
}}, mirror.token);
} catch (Exception e) {
e.printStackTrace ();
}
return downloaded;
}
// ========================================
public static boolean newFileExists (File file) {
if (file.isFile () && file.exists () && "new".equals (Util.getExtention (file)))
return true;
if (!file.isDirectory ())
return false;
for (File subFile : file.listFiles (new FileFilter () {
public boolean accept (File file2) {
return file2.isDirectory () || (file2.isFile () && "new".equals (Util.getExtention (file2)));
}
}))
if (newFileExists (subFile))
return true;
return false;
}
public static void renameNewFile (File file) {
if (file.isFile () && file.exists () && "new".equals (Util.getExtention (file))) {
try {
File newFile = new File (file.getParentFile (), Util.getBase (file));
Files.move (file.toPath (), newFile.toPath (), StandardCopyOption.REPLACE_EXISTING);
} catch (Exception e) {
}
}
if (!file.isDirectory ())
return;
for (File subFile : file.listFiles (new FileFilter () {
public boolean accept (File file2) {
return file2.isDirectory () || (file2.isFile () && "new".equals (Util.getExtention (file2)));
}
}))
renameNewFile (subFile);
}
// ========================================
public static void launch (File jarFile) {
try {
Runtime.getRuntime ().exec ("java -jar "+jarFile.getName (), null, jarFile.getParentFile ());
System.exit (0);
} catch (Exception e) {
e.printStackTrace ();
}
}
// ========================================
public TreeSet<String> putRemoteFiles (Mirror mirror, TreeSet<String> filesName, ProgressState progressState) {
TreeSet<String> uploaded = new TreeSet<String> ();
if (filesName == null || filesName.size () < 1)
return uploaded;
try {
byte[] tmp = new byte[bufSize];
TimeZone timeZone = TimeZone.getDefault ();
URLConnection urlConnection = sendZip ("zipPuts", mirror.token, new ZipBuilder () {
public void writeEntry (String token, ZipOutputStream zipOut) throws IOException {
for (String fileName : filesName) {
if (! fileName.startsWith (token))
continue;
ZipEntry zipOutEntry = new ZipEntry (fileName);
File file = new File (mirror.localRepository, fileName.substring (token.length ()));
long dateMs = file.lastModified ();
dateMs -= timeZone.getOffset (dateMs);
zipOutEntry.setTime (dateMs);
zipOut.putNextEntry (zipOutEntry);
PrintWriter entryWriter = new PrintWriter (new OutputStreamWriter (zipOut));
Util.copy (new FileInputStream (file), zipOut, tmp, progressState, true, false);
entryWriter.flush ();
zipOut.closeEntry ();
}
}});
receiveZip (urlConnection, new ZipLineReader () {
public void readLine (String line) {
uploaded.add (line);
}}, mirror.token);
} catch (Exception e) {
e.printStackTrace ();
}
return uploaded;
}
// ========================================
public String getRoles (String login) {
try {
URLConnection urlConnection = getUrlConnection ("getRoles", login);
urlConnection.connect ();
BufferedReader br = new BufferedReader (new InputStreamReader (urlConnection.getInputStream ()));
String roles = br.readLine ();
for (;;) {
String line = br.readLine ();
if (line == null)
break;
}
if (roles.indexOf ("|") < 0)
return null;
return roles;
} catch (Exception e) {
return null;
}
}
public void logoutDokuwiki () {
try {
URLConnection urlConnection = getUrlConnection ("?do=logout");
urlConnection.connect ();
forceOpenConnection (urlConnection);
} catch (Exception e) {
}
}
public void loginDokuwiki (String login, String password) {
try {
// page de connexion
URLConnection urlConnection = getUrlConnection ("?do=login");
urlConnection.connect ();
String sectok = null;
BufferedReader br = new BufferedReader (new InputStreamReader (urlConnection.getInputStream ()));
for (;;) {
String line = br.readLine ();
if (line == null)
break;
if (line.indexOf ("name=\"sectok\"") < 0)
continue;
Pattern p = Pattern.compile (".*<([^<]*name=\"sectok\"[^>]*)>.*");
Matcher m = p.matcher (line);
if (!m.matches ())
break;
line = m.group (1);
p = Pattern.compile (".*value=\"([^\"]*)\".*");
m = p.matcher (line);
if (!m.matches ())
break;
sectok = m.group (1);
break;
}
if (sectok == null)
return;
// envoie de mot de passe
urlConnection = getUrlConnection ("?do=login");
urlConnection.setDoOutput (true);
urlConnection.setDoInput (true);
urlConnection.setRequestProperty ("Content-Type", "application/x-www-form-urlencoded");
String post =
"sectok="+sectok+"&"+
"id=debut&"+
"do=login&"+
"u="+URLEncoder.encode (login, charset)+"&"+
"p="+URLEncoder.encode (password, charset);
urlConnection.setRequestProperty ("Content-Length", ""+post.getBytes ().length);
OutputStream httpStream = urlConnection.getOutputStream ();
PrintWriter httpWriter = new PrintWriter (new OutputStreamWriter (httpStream, charset), true);
httpWriter.append (post).append (LINE_FEED).flush ();
urlConnection.connect ();
forceOpenConnection (urlConnection);
} catch (Exception e) {
e.printStackTrace ();
}
}
// ========================================
static public void forceOpenConnection (URLConnection urlConnection) {
try {
BufferedReader br = new BufferedReader (new InputStreamReader (urlConnection.getInputStream ()));
for (;;) {
String line = br.readLine ();
if (line == null)
break;
}
} catch (Exception e) {
}
}
// ========================================
}