package abstrasy.libraries.io;
/**
* Abstrasy Interpreter
*
* Copyright : Copyright (c) 2006-2012, Luc Bruninx.
*
* Concédée sous licence EUPL, version 1.1 uniquement (la «Licence»).
*
* Vous ne pouvez utiliser la présente oeuvre que conformément à la Licence.
* Vous pouvez obtenir une copie de la Licence à l’adresse suivante:
*
* http://www.osor.eu/eupl
*
* Sauf obligation légale ou contractuelle écrite, le logiciel distribué sous
* la Licence est distribué "en l’état", SANS GARANTIES OU CONDITIONS QUELLES
* QU’ELLES SOIENT, expresses ou implicites.
*
* Consultez la Licence pour les autorisations et les restrictions
* linguistiques spécifiques relevant de la Licence.
*
*
* @author Luc Bruninx
* @version 1.0
*/
import abstrasy.Bivalence;
import abstrasy.Buffer;
import abstrasy.Hash;
import abstrasy.Interpreter;
import abstrasy.Node;
import abstrasy.SELF;
import abstrasy.Tools;
import abstrasy.externals.AExtCachable;
import abstrasy.externals.AExtClonable;
import abstrasy.externals.AExtTools;
import abstrasy.externals.AExtVObject;
import abstrasy.externals.OptAccess;
import abstrasy.externals.OptAccessList;
import abstrasy.externals.OptAccessProvider;
import abstrasy.interpreter.InterpreterException;
import abstrasy.interpreter.ORef;
import abstrasy.interpreter.StdErrors;
import java.io.ByteArrayOutputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ref.WeakReference;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.Socket;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;
public class External_File extends OptAccessProvider implements AExtClonable,
AExtCachable,
AExtVObject {
// l'adaptateur est-il libre?
private boolean opened = false;
// encodage des caractères
private String charset = System.getProperty("file.encoding");
// séquence de fin de ligne
private String eolStr = "\n";
private byte[] eol_bytes = eolStr.getBytes();
// liste des caractères EOL à filtrer
private String eolFilterStr = "\r\n";
private int connectTimeOut = 0;
private int readTimeOut = 0;
private File file = null;
private URLConnection urlConnection = null;
private String protocol = null;
private String tcpIP = null;
private String tcpHost = null;
private int tcpPort = 0;
private Socket socket = null;
private FileInputStream fileInputStream = null;
private InputStream input = null;
private FileOutputStream fileOutputStream = null;
private OutputStream output = null;
private boolean autoflush = true;
/*
* ---------------------------------------------------------------------------------
*
* Gestion des options
* ===================
*
*
* 'connect-timeout: ]0-NUMBER]
* Indique le nombre de millisecondes avant le time-out à la connexion
* d'une url. Si est = à 0, indique qu'il n'y a pas de time-out. Dans ce cas,
* l'attente est infinie.
*/
private OptAccess oa_connect_timeout = new OptAccess("connect-timeout") {
public Node getNode() {
return new Node(connectTimeOut);
}
public void setNode(Node optNode) throws Exception {
optNode.requireNodeType(Node.TYPE_NUMBER);
connectTimeOut = Math.min(0, (int) optNode.getNumber());
}
};
/*
* 'read-timeout: ]0-NUMBER]
* Indique le nombre de milliseconde avant que le time-out de lecture
* d'une url soit atteint. Si l'option est à 0, il n'y a pas de limite de
* la durée d'attente (il s'agit d'une attente à l'infini).
*/
private OptAccess oa_read_timeout = new OptAccess("read-timeout") {
public Node getNode() {
return new Node(readTimeOut);
}
public void setNode(Node optNode) throws Exception {
optNode.requireNodeType(Node.TYPE_NUMBER);
readTimeOut = Math.min(0, (int) optNode.getNumber());
}
};
/*
* 'charset:
* Quel est l'encodage des caractères. Par défaut, il s'agit de
* l'encodage du système d'exploitation.
*/
private OptAccess oa_charset = new OptAccess("charset") {
public Node getNode() {
return charset == null ? Node.createNothing(): new Node(charset);
}
public void setNode(Node optNode) throws Exception {
optNode.requireNodeType(Node.TYPE_STRING);
setCharset(optNode.getString());
}
};
private OptAccess oa_autoflush = new OptAccess("autoflush") {
public Node getNode() {
return new Node(isAutoflush() ? Node.TRUE: Node.FALSE);
}
public void setNode(Node optNode) throws Exception {
optNode.requireNodeType(Node.VTYPE_TRUEEQUIVALENT);
setAutoflush(Node.isTrueEquivalent(optNode));
}
};
private OptAccess oa_eol_sequence = new OptAccess("eol-sequence") {
public Node getNode() {
return new Node(eolStr);
}
public void setNode(Node optNode) throws Exception {
optNode.requireNodeType(Node.TYPE_STRING);
setEolStr(optNode.getString());
}
};
private OptAccess oa_eol_filter = new OptAccess("eol-filter") {
public Node getNode() {
return new Node(eolFilterStr);
}
public void setNode(Node optNode) throws Exception {
optNode.requireNodeType(Node.TYPE_STRING);
setEolFilterStr(optNode.getString());
}
};
private OptAccess oa_Protocol = new OptAccess("Protocol") {
public Node getNode() {
return protocol == null ? Node.NOTHING_FINAL_NODE: new Node(protocol);
}
public void setNode(Node optNode) throws Exception {
throw new InterpreterException(StdErrors.Illegal_access_to_final_value);
}
};
private OptAccess oa_Host = new OptAccess("Host") {
public Node getNode() {
return tcpHost == null ? Node.NOTHING_FINAL_NODE: new Node(tcpHost);
}
public void setNode(Node optNode) throws Exception {
throw new InterpreterException(StdErrors.Illegal_access_to_final_value);
}
};
private OptAccess oa_IP = new OptAccess("IP") {
public Node getNode() {
return tcpIP == null ? Node.NOTHING_FINAL_NODE: new Node(tcpIP);
}
public void setNode(Node optNode) throws Exception {
throw new InterpreterException(StdErrors.Illegal_access_to_final_value);
}
};
private OptAccess oa_Port = new OptAccess("Port") {
public Node getNode() {
return new Node(tcpPort);
}
public void setNode(Node optNode) throws Exception {
throw new InterpreterException(StdErrors.Illegal_access_to_final_value);
}
};
private OptAccess[] DEFAULT_OPTIONS = new OptAccess[] { oa_charset,
oa_eol_sequence,
oa_eol_filter,
oa_autoflush };
private OptAccess[] SOCKET_OPTIONS = new OptAccess[] { oa_charset,
oa_eol_sequence,
oa_eol_filter,
oa_autoflush,
oa_connect_timeout,
oa_read_timeout,
oa_Protocol };
public OptAccess[] getDEFAULT_OPTIONS() {
return DEFAULT_OPTIONS;
}
public OptAccess[] getSOCKET_OPTIONS() {
return SOCKET_OPTIONS;
}
/*
* --------------------------------------------------------------------------------------
*/
/**
* Permet de fermer tous les flux et fichiers ouverts.
*
* Cette méthode a une vocation interne et peut être liée à un hook.
*
* Si les fichiers sont déjà fermés, il ne se passe rien.
*
* @throws Exception
*/
public void internal_close() throws Exception {
// Data*Stream
if (output != null) {
output.close();
output = null;
}
if (input != null) {
input.close();
input = null;
}
// File*Stream
if (fileOutputStream != null) {
fileOutputStream.close();
fileOutputStream = null;
}
if (fileInputStream != null) {
fileInputStream.close();
fileInputStream = null;
}
// File
if (file != null) {
file = null;
}
// url
if (urlConnection != null) {
urlConnection = null;
}
// socket
if (socket != null) {
socket.close();
socket = null;
}
// tcpHost
if (tcpHost != null) {
tcpHost = null;
}
// tcpIp
if (tcpIP != null) {
tcpIP = null;
}
// protocol
if (protocol != null) {
protocol = null;
}
//
connectTimeOut = 0;
readTimeOut = 0;
charset = System.getProperty("file.encoding");
eolStr = "\n";
eol_bytes = eolStr.getBytes();
eolFilterStr = "\r\n";
//
opened = false;
}
/**
*Utilise un OutputStream et crée un BufferedOutputStream et un DataOutputStream à partir de celui-ci.
* @param out
*/
public void setOutputStream(OutputStream out) {
output = out;
opened = true;
}
/**
* Utilise un InputStream et crée un BufferedInputStream et un DataInputStream à partir de celui-ci.
* @param in
*/
public void setInputStream(InputStream in) {
input = in;
opened = true;
}
/**
* Clé du hook...
*/
private Object myHookKey;
/**
* Ajoute un hook en cas où on oublierait de fermer les fichiers et les flux...
*
* Ce hook sera automatiquement exécuté lors de l'arrêt de l'interpréteur, à moins
* que le fichier soit refermé manuellement. De cette manière, les fichiers devraient
* conserver un état stable.
*
*/
private void putHook() {
final WeakReference<External_File> myself = new WeakReference<External_File>(this);
Runnable hook = new Runnable() {
public void run() {
External_File me = myself.get();
if (me != null) {
if (Interpreter.isDebugMode()) {
Interpreter.Log("Io:File Hook : close " + me + "...");
}
try {
me.internal_close();
}
catch (Exception e) {
Interpreter.Log(" -> Exception: " + e.toString());
}
}
}
};
myHookKey = Interpreter.supervisor_PUTHOOK(hook);
}
/**
* On retire le hook...
*/
private void removeHook() {
Interpreter.supervisor_REMOVEHOOK(myHookKey);
myHookKey = null;
}
public External_File() throws Exception {
this.eol_bytes = this.eolStr.getBytes(charset);
this.setOptAccessList(new OptAccessList(DEFAULT_OPTIONS));
}
public void setCharset(String charset) throws Exception {
this.charset = charset;
this.eol_bytes = this.eolStr.getBytes(charset);
}
public String getCharset() {
return charset;
}
/*
* La séquence EOL est ajoutée intégralement à la fin de la sortie :writeln.
* Le dernier caractère de la séquence EOL termine la lecture de :readln.
*/
public void setEolStr(String eol_sequence) throws Exception {
if (eol_sequence.length() < 1)
throw new Exception("EOL cannot be an empty string");
this.eolStr = eol_sequence;
this.eol_bytes = this.eolStr.getBytes(charset);
}
public String getEolStr() {
return eolStr;
}
/*
* Les caractères EolFilter sont retirés de la chaîne obtenue par :readln.
* Il est donc possible que EolFilter soit une chaîne vide. Dans ce cas,
* les caractères EOL restent inclus dans la chaîne de résultat.
*/
public void setEolFilterStr(String eolFilterStr) {
this.eolFilterStr = eolFilterStr;
}
public String getEolFilterStr() {
return eolFilterStr;
}
public OutputStream getOutput() {
return output;
}
public InputStream getInput() {
return input;
}
public byte[] getEol_bytes() {
return eol_bytes;
}
public void setOpened(boolean opened) {
this.opened = opened;
}
public boolean isOpened() {
return opened;
}
public void setProtocol(String protocol) {
this.protocol = protocol;
}
public String getProtocol() {
return protocol;
}
public void setTcpHost(String tcpHost) {
this.tcpHost = tcpHost;
}
public String getTcpHost() {
return tcpHost;
}
public void setTcpPort(int tcpPort) {
this.tcpPort = tcpPort;
}
public int getTcpPort() {
return tcpPort;
}
public void setSocket(Socket socket) {
this.socket = socket;
}
public Socket getSocket() {
return socket;
}
public void setOa_Protocol(OptAccess oa_Protocol) {
this.oa_Protocol = oa_Protocol;
}
public OptAccess getOa_Protocol() {
return oa_Protocol;
}
public void setOa_Host(OptAccess oa_Host) {
this.oa_Host = oa_Host;
}
public OptAccess getOa_Host() {
return oa_Host;
}
public void setOa_Port(OptAccess oa_Port) {
this.oa_Port = oa_Port;
}
public OptAccess getOa_Port() {
return oa_Port;
}
public void setTcpIP(String tcpIP) {
this.tcpIP = tcpIP;
}
public String getTcpIP() {
return tcpIP;
}
public void setOa_IP(OptAccess oa_IP) {
this.oa_IP = oa_IP;
}
public OptAccess getOa_IP() {
return oa_IP;
}
public void setAutoflush(boolean autoflush) {
this.autoflush = autoflush;
}
public boolean isAutoflush() {
return autoflush;
}
/*****************************************************************************************************************
*
* Classe ExtURL privée. Cette classe offre une abstraction supplémentaire au niveau de la gestion des ressources
* décrites sous la forme d'urls.
*
* Les protocoles émulés sont les suivants:
*
* stdout:// : Flux de sortie standard.
* stdin:// : Flux d'entrée standard.
* stderr:// : Flux de sortie standard pour les messages d'erreur.
*
* tcp://host:port : Ouverture du port sur le host indiqué à l'aide d'un socket TCP.
* ssl+tcp://host:port : Idem, mais au travers d'une couche sécurisée SSL.
*
* file://path : Accès à un fichier en écriture à l'aide du protocol file.
*
* Les protocoles gérés par Java sont les suivants:
*
* file://path : Accès à un fichier en lecture seule.
*
* jar://path : Accès à une ressource d'une archive jar en lecture seule.
*
* Les protocoles assistés (c-à-d gérés par Java, mais nécessitant une abstraction supplémentaire):
*
* http://... : Support de GET, PUT et POST, ainsi que les headers HTTP.
* https://... : Idem.
*
* ftp://... : Sélection du mode d'accès écriture/lecture.
*
*/
private class ExtURL {
String urlStr = null;
String protocol = null;
String host = null;
int port = 0;
URL realUrl = null;
public ExtURL(String url) throws MalformedURLException {
this.urlStr = url;
String u = url;
/*
* protocoles émulés d'abord...
*/
if (u.equalsIgnoreCase("stdout://") || u.equalsIgnoreCase("stdin://") || u.equalsIgnoreCase("stderr://")) {
/*
* les protocoles émulés stdout://, stdin:// et stderr://
*/
protocol = u.substring(0, u.indexOf(':')).toLowerCase();
}
else if (u.toLowerCase().startsWith("tcp://") || u.toLowerCase().startsWith("ssl+tcp://")) {
/*
* les protocole émulés tcp://host:port et ssl+tcp://host:port
*/
protocol = urlStr.substring(0, urlStr.indexOf(':')).toLowerCase();
u = u.substring(protocol.length() + 3, u.length());
int lpp = u.lastIndexOf(':');
if (lpp < 0 || lpp >= u.length()) {
throw new MalformedURLException(url);
}
try {
port = Integer.parseInt(u.substring(lpp + 1, u.length()));
}
catch (Exception e) {
throw new MalformedURLException(url);
}
host = u.substring(0, lpp);
}
else {
/*
* protocoles non-émulés... On laisse donc la main au système Java...
*/
realUrl = new URL(urlStr);
}
}
String getProtocol() {
if (realUrl != null) {
return realUrl.getProtocol();
}
else {
return protocol;
}
}
URI toURI() throws URISyntaxException {
if (realUrl != null) {
return realUrl.toURI();
}
else {
throw new URISyntaxException(urlStr, "Cannot be converted to URI");
}
}
URLConnection openConnection() throws IOException {
return realUrl.openConnection();
}
String getHost() {
if (realUrl != null) {
return realUrl.getHost();
}
else {
return host;
}
}
int getPort() {
if (realUrl != null) {
return realUrl.getPort();
}
else {
return port;
}
}
}
/**********************************************************************************************************/
/*
* Ouvre le fichier en mode R/W+ sur un buffer.
*
* (:open-buffer! buffer)
*
* On peut donc écrire des données dans le buffer (les nouvelles données s'ajoute
* au buffer). Lorsqu'on lit des données, on retire des données du buffer.
*/
public Node external_mutator_open_buffer(Node startAt) throws Exception {
startAt.isGoodArgsCnt(2);
SELF.require_SELF_mutable();
if (opened) {
throw new InterpreterException(StdErrors.extend(StdErrors.Already_used, "File already open"));
}
Buffer buffer = ((External_Buffer) AExtTools.getArgExternalInstance(startAt, 1, External_Buffer.class, Node.ACCESSVTYPE_MUTABLE_WRITELOCK)).getBuffer();
setInputStream(buffer.getInputStream());
setOutputStream(buffer.getOutputStream());
this.setOptAccessList(new OptAccessList(this.getDEFAULT_OPTIONS()));
// pas besoin de placer un hook...
return null;
}
/*
* Ouverture d'un fichier sur disque.
*
* (:open-file! PATHNAME MODE)
*
* Ouvre le fichier dont le chemin PATHNAME est spécifié en respectant le mode MODE:
* "r" = Lecture
* "w" = Ecriture
* "a" = Ajout à la fin du fichier
*/
public Node external_mutator_open_file(Node startAt) throws Exception {
startAt.isGoodArgsCnt(3);
SELF.require_SELF_mutable();
if (opened) {
throw new InterpreterException(StdErrors.extend(StdErrors.Already_used, "File already open"));
}
opened = true;
file = new File(startAt.getSubNode(1, Node.TYPE_STRING).getString());
String mode = startAt.getSubNode(2, Node.TYPE_STRING).getString().toLowerCase().trim();
if (mode.equals("r")) {
fileInputStream = new FileInputStream(file);
input = fileInputStream;
}
else if (mode.equals("w")) {
fileOutputStream = new FileOutputStream(file);
output = fileOutputStream;
}
else if (mode.equals("a")) {
fileOutputStream = new FileOutputStream(file, true);
output = fileOutputStream;
}
else {
throw new InterpreterException(StdErrors.extend(StdErrors.Invalid_parameter, "Mode not supported"));
}
this.setOptAccessList(new OptAccessList(this.getDEFAULT_OPTIONS()));
/*
* On place un hook en cas de problème ou d'oubli...
*/
putHook();
return null;
}
public Node external_mutator_open_url(Node startAt) throws Exception {
/*
* formes:
* -------
*
* 1. (open-url "url"):
* Ouvre la ressource indiqué par l'url. Par défaut, celle-ci sera ouverte en lecture seule.
* Cependant en fonction du protocole utilisé, la ressource pourra aussi être ouverte en écriture.
*
* En voici un récapitulatif:
* =========================
* tcp://host:port -> lecture/écriture
* ssl+tcp://host:port -> lecture/écriture
* stdout:// -> écriture seule
* stdin:// -> lecture seule
* stderr:// -> écriture seule
*
* 2. (open-url "url" "r"):
* Ouvre le fichier indiqué par l'url en lecture seul.
*
* 3. (open-url "url" "w"):
* Ouvre le fichier indiqué par l'url en écriture.
*
* 4. (open-url "url" "rw"):
* Ouvre le fichier indiqué par l'url en lecture/écriture.
*
* 4. (open-url "url" "GET"):
* Ouvre le fichier indiqué par l'url en lecture en appliquant la commande GET.
* Le seul propotole possible est HTTP.
*
* 5. (open-url "url" "GET" [<"header" "value">...]):
* Ouvre l'url en lecture en appliquant la méthode GET avec une liste de propriétés Header.
* Le seul protocole possible est HTTP.
*
* 6. (open-url "url" "POST" "datas"/Buffer):
* Ouvre l'url en lecture en appliquant la méthode POST et en envoyant la chaîne datas
* encodée par défaut ou Buffer. Le seul protocole supporté est HTTP.
*
* 7. (open-url "url" "POST" "datas"/Buffer [<"header" "value">...]):
* Ouvre l'url en lecture en appliquant la méthode POST, la liste de propriétés Headers et
* en envoyant la chaîne datas ou Buffer. Le protocole supporté est HTTP.
*
* 8. (open-url "url" "PUT" "datas"/Buffer):
* Ouvre l'url en appliquant la méthode PUT et en envoyant la chaîne datas
* encodée par défaut ou Buffer. Le seul protocole supporté est HTTP.
*
* 9. (open-url "url" "PUT" "datas"/Buffer [<"header" "value">...]):
* Ouvre l'url en lecture en appliquant la méthode POST, la liste de propriétés Headers et
* en envoyant la chaîne datas ou Buffer. Le protocole supporté est HTTP.
*
* protocoles supportés:
* --------------------
* de 1 à 3: file: jar: http: https: ftp:
* autres : http: https: (notamment webdav) uniquement.
*
*/
if (opened) {
throw new InterpreterException(StdErrors.extend(StdErrors.Already_used, "File already open"));
}
opened = true;
startAt.isGoodArgsLength(false, 2);
SELF.require_SELF_mutable();
ExtURL url = new ExtURL(startAt.getSubNode(1, Node.TYPE_STRING).getString());
/*
* déterminer le protocol pour la suite...
*/
protocol = url.getProtocol();
/*
* protocol peut être:
*
* - file :
* ----
* Dans ce cas, le fichier est ouvert en lecture seul.
* Il n'y a pas non plus ce commande comme dans le cas du http.
* Deuxième paramètre facultatif peut être "r" ou "w".
*
* Note:
* ----
* Dans le cas du mode "w", il s'agit d'une émualation parce que java n'implémente
* pas l'accès en écriture au travers du protocole file:.
*
* Rappel:
* ------
* Sous Windows, la forme de l'url utilisant le protocole file:// est hérité du Java.
* Ainsi, pour accéder à un fichier sur le disque dur C:, l'url pourra prendre les formes
* suivantes (c'est assez déroutant, il faut l'avouer):
* file:/C:\\dossier\\fichier.ext
* file:///C:\\dossier\\fichier.ext
* file:/C|\\dossier\\fichier.ext
* file:///C|\\dossier\\fichier.ext
* file:/C|/dossier/fichier.ext
* ou encore...
* file://C:/dossier/fichier.ext
*
* Sous Linux et Unix, la forme est plus claire:
* file:///home/utilisateur/fichier
*
* - jar :
* ---
* Dans ce cas, le fichier d'archive est toujours ouvert en lecture seul.
* Il n'y a pas de commande comme en http.
* Deuxième paramètre facultatif possible "r".
*
* - http ou https :
* -------------
* Dans ce cas, il faut prendre en compte les éventuels paramètres d'en-têtes et le request.
* En outre, même s'il y a écriture de données vers la sortie, il est nécessaire de lire le
* résultat de retour dans tous les cas.
*
* -> envoie de données (request : GET, POST, PUT)
* <- lecture du résultat (response).
*
* - ftp :
* ---
* Dans ce cas, il faut déterminer si l'url est ouverte en lecture ou en écriture.
* Il ne faut donc tenir compte que le deuxième paramètre puisse être "r" ou "w".
*
* Note:
* ----
* L'url d'accès ftp a la forme suivante: "ftp://<login>:<pass>@<server>/<path>".
*
* Attention:
* ---------
* Si l'url est ouverte en écriture, les nouvelles données envoyées écrasent celles qui
* étaient précédemment dans le fichier à partir du début.
*
*/
String mode = null;
Node props = null;
Node datas = null;
byte[] buffer = null;
String old_c = null; // paramètres timeOut
String old_r = null;
int max_i = startAt.size() - 1;
/*
* propriétés headers...
*/
if (startAt.elementAt(max_i).getSymbolicValue().getQType()==Node.TYPE_CLIST) {
// il y a des propriétés header...
// La gestion des erreurs est plus loin...
props = startAt.getSubNode(max_i--, Node.TYPE_HASH);
}
/*
* 2ième paramètres...
*/
int i_ = 2;
if (i_ <= max_i) {
/*
* il existe...
*/
mode = startAt.getSubNode(i_++, Node.TYPE_STRING).getString().toUpperCase().trim();
/*
* cas des protocoles http:// et https:// ...
*/
if (protocol.equalsIgnoreCase("http") || protocol.equalsIgnoreCase("https")) {
// GET, POST et PUT possible uniquement en http/https...
if (!(mode.equals("GET") || mode.equals("POST") || mode.equals("PUT"))) {
throw new InterpreterException(128010, "Unsupported request methode");
}
}
/*
* cas des protocoles ftp:// et file://...
*/
else if (protocol.equalsIgnoreCase("ftp") || protocol.equalsIgnoreCase("file")) {
if (!(mode.equalsIgnoreCase("r") || mode.equalsIgnoreCase("w"))) {
throw new InterpreterException(128015, "Unsupported access methode");
}
}
/*
* cas des protocoles jar:// et stdin://
*/
else if (protocol.equalsIgnoreCase("jar") || protocol.equalsIgnoreCase("stdin")) {
if (!(mode.equalsIgnoreCase("r"))) {
throw new InterpreterException(128015, "Unsupported access methode");
}
}
/*
* cas des protocoles tcp:// et ssl+tcp://
*/
else if (protocol.equalsIgnoreCase("tcp") || protocol.equalsIgnoreCase("ssl+tcp")) {
if (!(mode.equalsIgnoreCase("rw"))) {
throw new InterpreterException(128015, "Unsupported access methode");
}
}
/*
* cas des protocoles stdout:// stderr://
*/
else if (protocol.equalsIgnoreCase("stdout") || protocol.equalsIgnoreCase("stderr")) {
if (!(mode.equalsIgnoreCase("w"))) {
throw new InterpreterException(128015, "Unsupported access methode");
}
}
/*
* autre non supporté....
*/
else {
throw new InterpreterException(128011, "Unsupported protocol");
}
}
/*
* des données à envoyer ? Uniquement pour http/https...
*/
if (i_ <= max_i) {
if (!protocol.equalsIgnoreCase("http") && !protocol.equalsIgnoreCase("https")) {
// uniquement possible avec le protocole http/https...
throw new InterpreterException(128016, "Unsupported request datas");
}
// datas...
datas = startAt.getSubNode(i_++, Node.TYPE_STRING | Node.TYPE_BYTES | Node.VTYPE_DELEGABLE);
buffer = Node.node2VBytes(datas).getBytes().getArray();
}
/*
* gestion d'erreurs: http GET envoie des données ????
*/
if (datas != null && mode != null && mode.equals("GET")) {
throw new InterpreterException(128012, "GET request with data body");
}
/*
* gestion d'erreur: des propriétés d'en-tête avec un autre protocol que http/https ????
*/
if (props != null && (!protocol.equalsIgnoreCase("http") && !protocol.equalsIgnoreCase("https"))) {
throw new InterpreterException(128013, "Cannot handle header properties in request");
}
try {
/*
* d'abord les protocoles émulés...
*/
/**
* cas particulier de file:// en écriture...
*
* Il s'agit d'une émulation...
*
*/
if (protocol.equalsIgnoreCase("file") && mode != null && mode.equalsIgnoreCase("w")) {
File f = new File(url.toURI());
output = new FileOutputStream(f);
this.setOptAccessList(new OptAccessList(this.getDEFAULT_OPTIONS()));
this.getOptAccessList().add(oa_Protocol);
}
/**
* cas de tcp://
*/
else if (protocol.equalsIgnoreCase("tcp")) {
tcpHost = url.getHost();
tcpPort = url.getPort();
if (tcpPort < 0 || tcpPort > 65535) {
throw new InterpreterException(StdErrors.extend(StdErrors.Out_of_range, "" + tcpPort));
}
//System.out.println("host:"+tcpHost); // debug
//System.out.println("port:"+tcpPort); // debug
socket = new Socket(tcpHost, tcpPort);
if (readTimeOut > 0) {
socket.setSoTimeout(readTimeOut);
}
tcpIP = socket.getInetAddress().getHostAddress();
tcpHost = socket.getInetAddress().getHostName();
input = socket.getInputStream();
output = socket.getOutputStream();
this.setOptAccessList(new OptAccessList(this.getSOCKET_OPTIONS()));
this.getOptAccessList().set(oa_Host);
this.getOptAccessList().set(oa_IP);
this.getOptAccessList().set(oa_Port);
}
/**
* cas de ssl+tcp://
*/
else if (protocol.equalsIgnoreCase("ssl+tcp")) {
tcpHost = url.getHost();
tcpPort = url.getPort();
if (tcpPort < 0 || tcpPort > 65535) {
throw new InterpreterException(StdErrors.extend(StdErrors.Out_of_range, "" + tcpPort));
}
SocketFactory socketFactory = SSLSocketFactory.getDefault();
socket = socketFactory.createSocket(tcpHost, tcpPort);
if (readTimeOut > 0) {
socket.setSoTimeout(readTimeOut);
}
tcpIP = socket.getInetAddress().getHostAddress();
tcpHost = socket.getInetAddress().getHostName();
input = socket.getInputStream();
output = socket.getOutputStream();
this.setOptAccessList(new OptAccessList(this.getSOCKET_OPTIONS()));
this.getOptAccessList().set(oa_Host);
this.getOptAccessList().set(oa_IP);
this.getOptAccessList().set(oa_Port);
}
/**
* cas de stdout://
*/
else if (protocol.equalsIgnoreCase("stdout")) {
output = System.out;
this.setOptAccessList(new OptAccessList(this.getDEFAULT_OPTIONS()));
this.getOptAccessList().add(oa_Protocol);
}
/**
* cas de stdout://
*/
else if (protocol.equalsIgnoreCase("stderr")) {
output = System.err;
this.setOptAccessList(new OptAccessList(this.getDEFAULT_OPTIONS()));
this.getOptAccessList().add(oa_Protocol);
}
/**
* cas de stdin://
*/
else if (protocol.equalsIgnoreCase("stdin")) {
input = System.in;
this.setOptAccessList(new OptAccessList(this.getDEFAULT_OPTIONS()));
this.getOptAccessList().add(oa_Protocol);
}
/*
* autres protocoles...
*/
else {
/**
* sinon dans les autres cas, on ouvre réellement une connexion vers l'url...
*/
urlConnection = url.openConnection();
tcpHost = url.getHost();
tcpPort = url.getPort();
/*
* timeout
*/
if (connectTimeOut > 0) {
urlConnection.setConnectTimeout(connectTimeOut);
}
if (readTimeOut > 0) {
urlConnection.setReadTimeout(readTimeOut);
}
/*
* pas de cache et toujours en lecture (par défaut)...
*/
urlConnection.setUseCaches(false);
urlConnection.setDoInput(true);
/**
* Typiquement HTTP
*
* Remarque: HttpsURLConnection est une sous-classe de HttpURLConnection.
* -------- Donc le protocole http est respecté en https.
*/
if (urlConnection instanceof HttpURLConnection) {
HttpURLConnection httpCon = (HttpURLConnection) urlConnection;
if (props != null) {
// il y a des propriétés header...
Hash hash=props.getHash();
ArrayList<String> ks=hash.keys_strings();
for (int i = 0; i < ks.size(); i++) {
String header_s = ks.get(i);
String value_s = Node.node2VString(hash.get(header_s)).getString();
Interpreter.Log(" HTTP-Header: " + header_s + " : " + value_s);
httpCon.setRequestProperty(header_s, value_s);
}
}
if (mode != null && (mode.equals("POST") || mode.equals("PUT"))) {
// c'est la méthode POST...
if (mode.equals("PUT")) {
Interpreter.Log(" HTTP PUT: " + url.toString());
}
else {
Interpreter.Log(" HTTP POST: " + url.toString());
}
urlConnection.setDoOutput(true);
httpCon.setRequestMethod(mode);
output = urlConnection.getOutputStream();
// envoyer...
output.write(buffer);
if (isAutoflush())
output.flush();
}
input = urlConnection.getInputStream();
this.setOptAccessList(new OptAccessList(this.getSOCKET_OPTIONS()));
if (tcpHost != null)
this.getOptAccessList().set(oa_Host);
if (tcpPort > 0)
this.getOptAccessList().set(oa_Port);
}
/**
* Autres protocoles...
*/
else {
/*
* par défaut méthode "r"... (r)ead...
*/
if (mode == null || (mode != null && mode.equalsIgnoreCase("r"))) {
Interpreter.Log(" " + protocol + " read : " + url.toString());
input = urlConnection.getInputStream();
}
/*
* sinon, méthode "w"... (w)rite...
*/
else {
Interpreter.Log(" " + protocol + " write : " + url.toString());
output = urlConnection.getOutputStream();
}
this.setOptAccessList(new OptAccessList(this.getSOCKET_OPTIONS()));
if (tcpHost != null)
this.getOptAccessList().set(oa_Host);
if (tcpPort > 0)
this.getOptAccessList().set(oa_Port);
}
}
}
catch (Exception e) {
throw e;
}
/*
* On place un Hook en cas où on oublie de fermer File comme il faut...
*/
putHook();
return null;
}
/*
* (:close!)
*
* Ferme le fichier.
*
* Effectue un flush() si nécessaire.
*/
public Node external_mutator_close(Node startAt) throws Exception {
startAt.isGoodArgsCnt(1);
SELF.require_SELF_mutable();
internal_close();
/*
* On retire le hook puisque les fichiers sont fermé correctement.
*/
removeHook();
return null;
}
/*
* Retourne la quantité de données prête à lire en nombre d'octets.
*
*/
public Node external_available(Node startAt) throws Exception {
startAt.isGoodArgsCnt(1);
return new Node(input.available());
}
/*
* TRUE/FALSE <- (:opened?)
*
* Retourne TRUE si le fichier est ouvert.
*/
public Node external_is_opened(Node startAt) throws Exception {
startAt.isGoodArgsCnt(1);
return new Node(opened ? Node.TRUE: Node.FALSE);
}
public Node external_mutator_read_buffer(Node startAt) throws Exception {
startAt.isGoodArgsCnt(1, 2);
SELF.require_SELF_mutable();
Buffer buffer = new Buffer();
buffer.setCharset(charset);
if (startAt.size() == 2) {
int sz = (int) startAt.getSubNode(1, Node.TYPE_NUMBER).getNumber();
byte[] buf = new byte[sz];
int cread = input.read(buf);
if (cread > 0) {
buffer.write_bytes(buf, cread);
}
else {
return Node.createNothing();
}
}
else {
byte[] buf = new byte[4096];
int cread;
while ((cread = input.read(buf)) != -1) {
if (cread > 0) {
buffer.write_bytes(buf, cread);
}
}
if (buffer.size() == 0 && cread == -1) {
return Node.createNothing();
}
}
External_Buffer res = new External_Buffer();
res.setBuffer(buffer);
return Node.createExternal(res);
}
public Node external_mutator_read_chunked_buffer(Node startAt) throws Exception {
startAt.isGoodArgsCnt(1);
SELF.require_SELF_mutable();
Buffer buffer = new Buffer();
buffer.setCharset(charset);
int sz = 0;
try {
sz = read_int();
}
catch (EOFException eofe) {
return Node.createNothing();
}
if (sz > 0) {
byte[] buf = new byte[sz];
int cread = input.read(buf);
if (cread > 0) {
buffer.write_bytes(buf, cread);
}
if (buffer.size() == 0 && cread == -1) {
return Node.createNothing();
}
}
External_Buffer res = new External_Buffer();
res.setBuffer(buffer);
return Node.createExternal(res);
}
public Node external_mutator_write_buffer(Node startAt) throws Exception {
startAt.isGoodArgsCnt(2);
SELF.require_SELF_mutable();
Buffer buffer = ((External_Buffer) AExtTools.getArgExternalInstance(startAt, 1, External_Buffer.class, Node.ACCESSVTYPE_MUTABLE_WRITELOCK)).getBuffer();
if (buffer.length() > 0) {
byte[] buf = buffer.read_bytes();
output.write(buf);
output.flush();
}
return null;
}
public Node external_mutator_write_chunked_buffer(Node startAt) throws Exception {
startAt.isGoodArgsCnt(2);
SELF.require_SELF_mutable();
Buffer buffer = ((External_Buffer) AExtTools.getArgExternalInstance(startAt, 1, External_Buffer.class, Node.ACCESSVTYPE_MUTABLE_WRITELOCK)).getBuffer();
int len = buffer.length();
write_int(len);
if (buffer.length() > 0) {
byte[] buf = buffer.read_bytes();
output.write(buf);
}
if (isAutoflush())
output.flush();
return null;
}
public Node external_mutator_read_byte(Node startAt) throws Exception {
startAt.isGoodArgsCnt(1);
SELF.require_SELF_mutable();
int cread = -1;
try {
cread = input.read();
}
catch (EOFException e) {
cread = -1;
}
if (cread == -1)
return Node.createNothing();
else
return new Node(cread);
}
public Node external_mutator_read(Node startAt) throws Exception {
startAt.isGoodArgsCnt(1);
SELF.require_SELF_mutable();
ByteArrayOutputStream barray = new ByteArrayOutputStream();
byte[] buf = new byte[4096];
int cread;
while ((cread = input.read(buf)) != -1) {
barray.write(buf, 0, cread);
}
if (barray.size() == 0 && (cread == -1))
return Node.createNothing();
else
return new Node(barray.toString(charset));
}
public Node external_mutator_readln(Node startAt) throws Exception {
startAt.isGoodArgsCnt(1);
SELF.require_SELF_mutable();
String res = "";
ArrayList<Integer> bytes = new ArrayList<Integer>();
int cread = -1;
boolean cont = true;
boolean eof = false;
while (cont) {
try {
cread = input.read();
}
catch (EOFException e) {
cont = false;
eof = true;
}
if (cread != -1) {
bytes.add(new Integer(cread));
/*
* On arrête lorsque le dernier caractère de la séquence est trouvé.
*/
if (cread == eol_bytes[eol_bytes.length - 1]) {
cont = false;
}
}
else {
eof = true;
cont = false;
}
}
if (bytes.size() == 0 && eof)
return Node.createNothing();
byte[] buffer = new byte[bytes.size()];
for (int i = 0; i < bytes.size(); i++) {
buffer[i] = bytes.get(i).byteValue();
}
String res1;
if (charset != null) {
res1 = new String(buffer, 0, buffer.length, charset);
}
else {
res1 = new String(buffer, 0, buffer.length);
}
//System.out.print(res1);
for (int i = 0; i < res1.length(); i++) {
char c = res1.charAt(i);
/*
* On retire les caractère eolFilter la chaîne résultante.
*/
if (eolFilterStr.indexOf(c) < 0) {
res = res + c;
}
}
return new Node(res);
}
public Node external_mutator_write_byte(Node startAt) throws Exception {
startAt.isGoodArgsCnt(2);
SELF.require_SELF_mutable();
output.write((((int) startAt.getSubNode(1, Node.TYPE_NUMBER).getNumber()) & 0xff));
if (isAutoflush())
output.flush();
return null;
}
public Node external_mutator_write(Node startAt) throws Exception {
startAt.isGoodArgsLength(false, 1);
SELF.require_SELF_mutable();
for (int i = 1; i < startAt.size(); i++) {
if (charset != null) {
output.write(Node.node2VString(startAt.getSubNode(i, Node.VTYPE_VALUABLE)).getString().getBytes(charset));
}
else {
output.write(Node.node2VString(startAt.getSubNode(i, Node.VTYPE_VALUABLE)).getString().getBytes());
}
}
if (isAutoflush())
output.flush();
return null;
}
public Node external_mutator_flush(Node startAt) throws Exception {
startAt.isGoodArgsCnt(1);
SELF.require_SELF_mutable();
output.flush();
return null;
}
public Node external_mutator_writeln(Node startAt) throws Exception {
startAt.isGoodArgsLength(false, 1);
SELF.require_SELF_mutable();
for (int i = 1; i < startAt.size(); i++) {
if (charset != null) {
output.write(Node.node2VString(startAt.getSubNode(i, Node.VTYPE_VALUABLE)).getString().getBytes(charset));
}
else {
output.write(Node.node2VString(startAt.getSubNode(i, Node.VTYPE_VALUABLE)).getString().getBytes());
}
}
output.write(eol_bytes);
if (isAutoflush())
output.flush();
return null;
}
public void write_int(int v) throws IOException {
output.write((v >>> 24) & 0xFF);
output.write((v >>> 16) & 0xFF);
output.write((v >>> 8) & 0xFF);
output.write(v & 0xFF);
}
public int read_int() throws IOException {
int v1 = ((int) input.read()) & 0xFF;
int v2 = ((int) input.read()) & 0xFF;
int v3 = ((int) input.read()) & 0xFF;
int v4 = ((int) input.read()) & 0xFF;
return (v1 << 24) | (v2 << 16) | (v3 << 8) | v4;
}
public Node external_mutator_save(Node startAt) throws Exception {
startAt.isGoodArgsCnt(2);
SELF.require_SELF_mutable();
//System.out.print(startAt);
String serial2str = startAt.getSubNode(1, Node.VTYPE_VALUABLE).toString();
byte[] sbuff = serial2str.getBytes("UTF-8");
write_int(sbuff.length);
output.write(sbuff);
if (isAutoflush())
output.flush();
return null;
}
public Node external_mutator_save_gz(Node startAt) throws Exception {
startAt.isGoodArgsCnt(2);
SELF.require_SELF_mutable();
//System.out.print(startAt);
String serial2str = startAt.getSubNode(1, Node.VTYPE_VALUABLE).toString();
byte[] sbuff = serial2str.getBytes("UTF-8");
write_int(sbuff.length);
output.write(Tools.compress(sbuff));
if (isAutoflush())
output.flush();
return null;
}
public Node external_mutator_load(Node startAt) throws Exception {
startAt.isGoodArgsCnt(1);
SELF.require_SELF_mutable();
int bsize;
try {
bsize = read_int();
}
catch (EOFException eofe) {
return Node.createNothing();
}
byte[] sbuff = new byte[bsize];
int verif = input.read(sbuff);
if (verif != bsize) {
return Node.createNothing();
}
String serial2str = new String(sbuff, "UTF-8");
Interpreter interpreter = Interpreter.interpr_getNewChildInterpreter();
interpreter.setSource(serial2str);
interpreter.compile();
return interpreter.execute();
}
public Node external_mutator_load_gz(Node startAt) throws Exception {
startAt.isGoodArgsCnt(1);
SELF.require_SELF_mutable();
int bsize;
try {
bsize = read_int();
}
catch (EOFException eofe) {
return Node.createNothing();
}
byte[] sbuff = new byte[bsize];
int verif = input.read(sbuff);
if (verif != bsize) {
return Node.createNothing();
}
String serial2str = new String(Tools.decompress(sbuff), "UTF-8");
Interpreter interpreter = Interpreter.interpr_getNewChildInterpreter();
interpreter.setSource(serial2str);
interpreter.compile();
return interpreter.execute();
}
public Object clone_my_self(Bivalence bival) {
return null;
}
/*
* ----------------------------------------------------------------------------
*
* Optimisation par cache d'instanciantion (mars 2012) rev 1.0-6321.0
*
* ----------------------------------------------------------------------------
*/
private static ORef _optim_symbols_cache_ = new ORef();
@Override
public Object getSymbolsCache() {
return _optim_symbols_cache_.getRef();
}
@Override
public void setSymbolsCache(Object cache) {
_optim_symbols_cache_.setRef(cache);
}
}