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) {
	}
    }

    // ========================================
}
