234 lines
10 KiB
Java
234 lines
10 KiB
Java
package InnaIrcBot.ProvidersConsumers;
|
|
|
|
import InnaIrcBot.Config.StorageFile;
|
|
import InnaIrcBot.LogDriver.BotDriver;
|
|
import InnaIrcBot.ReconnectControl;
|
|
|
|
import java.io.*;
|
|
import java.net.InetAddress;
|
|
import java.net.InetSocketAddress;
|
|
import java.net.Socket;
|
|
import java.net.UnknownHostException;
|
|
import java.nio.charset.StandardCharsets;
|
|
import java.util.Collections;
|
|
import java.util.HashMap;
|
|
import java.util.Map;
|
|
|
|
|
|
public class DataProvider implements Runnable {
|
|
private StorageFile configFile;
|
|
private String serverName;
|
|
private String userNick;
|
|
private BufferedReader rawStreamReader;
|
|
|
|
private boolean ableToRun = true;
|
|
/**
|
|
* Initiate connection and prepare input/output streams for run()
|
|
* */
|
|
public DataProvider(StorageFile storageFile){
|
|
this.configFile = storageFile;
|
|
this.serverName = storageFile.getServerName();
|
|
|
|
int port = storageFile.getServerPort();
|
|
|
|
try {
|
|
InetAddress inetAddress = InetAddress.getByName(serverName);
|
|
Socket socket = new Socket(); // TODO: set timeout?
|
|
for (int i=0; i<5; i++) {
|
|
socket.connect(new InetSocketAddress(inetAddress, port), 5000); // 10sec = 10000
|
|
if (socket.isConnected())
|
|
break;
|
|
}
|
|
if (socket.isConnected())
|
|
System.out.println("Socket connected");
|
|
else {
|
|
System.out.println("Unable to connect to remote server after 5 retry.");
|
|
this.ableToRun = false;
|
|
return;
|
|
}
|
|
|
|
if (!StreamProvider.setStream(serverName, socket)) {
|
|
this.ableToRun = false;
|
|
return;
|
|
}
|
|
|
|
InputStream inStream = socket.getInputStream();
|
|
InputStreamReader isr = new InputStreamReader(inStream, StandardCharsets.UTF_8); //TODO set charset in options;
|
|
this.rawStreamReader = new BufferedReader(isr);
|
|
} catch(UnknownHostException e){
|
|
this.ableToRun = false;
|
|
System.out.println("Internal issue: DataProvider->constructor caused unknown host exception:\n\t"+e); // caused by InetAddress
|
|
} catch (IOException e){
|
|
this.ableToRun = false;
|
|
System.out.println("Internal issue: DataProvider->constructor caused I/O exception\n\t"+e); // caused by Socket
|
|
}
|
|
}
|
|
//HANDLE ALWAYS in case thread decided to die
|
|
private void close(){
|
|
StreamProvider.delStream(serverName);
|
|
}
|
|
|
|
public void run(){
|
|
if (!ableToRun) {
|
|
this.close();
|
|
return;
|
|
}
|
|
|
|
if(!this.initConnection(rawStreamReader)) {
|
|
this.close();
|
|
return;
|
|
}
|
|
|
|
//Prepare logDriver for using in threads.
|
|
if(!BotDriver.setFileDriver(serverName, configFile.getLogDriver(), configFile.getLogDriverParameters())) {
|
|
this.close();
|
|
return;
|
|
}
|
|
|
|
/* Used for sending data into consumers objects*/
|
|
Map<String, PrintWriter> channelsMap = Collections.synchronizedMap(new HashMap<String, PrintWriter>());
|
|
try {
|
|
PipedOutputStream streamOut = new PipedOutputStream(); // K-K-K-KOMBO \m/
|
|
|
|
BufferedReader streamBufferedReader = new BufferedReader(
|
|
new InputStreamReader(
|
|
new PipedInputStream(streamOut), StandardCharsets.UTF_8)
|
|
);
|
|
|
|
Runnable consumer = new SystemConsumer(streamBufferedReader, userNick, channelsMap, this.configFile);
|
|
new Thread(consumer).start();
|
|
channelsMap.put("", new PrintWriter(streamOut)); // Not sure that PrintWriter is thread-safe..
|
|
} catch (IOException e){
|
|
System.out.println("Internal issue: DataProvider->run() I/O exception while initialized child objects.\n\t"+e); // caused by Socket
|
|
this.close();
|
|
return;
|
|
}
|
|
////////////////////////////////////// Start loop //////////////////////////////////////////////////////////////
|
|
|
|
try {
|
|
String rawMessage;
|
|
String[] rawStrings; // prefix[0] command[1] command-parameters\r\n[2]
|
|
//if there is no prefix, you should assume the message came from your client.
|
|
String chan;
|
|
// Say 'yes, we need reconnect if connection somehow died'
|
|
ReconnectControl.register(serverName);
|
|
|
|
while ((rawMessage = rawStreamReader.readLine()) != null) {
|
|
//System.out.println(rawMessage);
|
|
if (rawMessage.startsWith(":")) {
|
|
rawStrings = rawMessage
|
|
.substring(1)
|
|
.split(" :?", 3); // Removing ':'
|
|
|
|
|
|
chan = rawStrings[2].replaceAll("(\\s.?$)|(\\s.+?$)", "");
|
|
|
|
//System.out.println("\tChannel: "+chan+"\n\tAction: "+rawStrings[1]+"\n\tSender: "+rawStrings[0]+"\n\tMessage: "+rawStrings[2]+"\n");
|
|
|
|
if (rawStrings[1].equals("QUIT") || rawStrings[1].equals("NICK")) { // replace regex
|
|
for (PrintWriter value : channelsMap.values()) {
|
|
value.println(rawStrings[1] + " " + rawStrings[0] + " " + rawStrings[2]);
|
|
value.flush();
|
|
}
|
|
} else if (channelsMap.containsKey(chan)) {
|
|
channelsMap.get(chan).println(rawStrings[1] + " " + rawStrings[0] + " " + rawStrings[2]);
|
|
channelsMap.get(chan).flush();
|
|
} else {
|
|
channelsMap.get("").println(rawStrings[1] + " " + rawStrings[0] + " " + rawStrings[2]);
|
|
channelsMap.get("").flush();
|
|
}
|
|
} else if (rawMessage.startsWith("PING :")) {
|
|
pingSrvResponse(rawMessage);
|
|
} else {
|
|
System.out.println("Not a valid response=" + rawMessage);
|
|
}
|
|
}
|
|
} catch (IOException e){
|
|
System.out.println("Socket issue: I/O exception"); //Connection closed. TODO: MAYBE try reconnect
|
|
}
|
|
finally {
|
|
for (PrintWriter p :channelsMap.values()) {
|
|
p.close();
|
|
}
|
|
this.close();
|
|
}
|
|
}
|
|
|
|
private void pingSrvResponse(String rawData){
|
|
StreamProvider.writeToStream(serverName,"PONG :" + rawData.replace("PING :", ""));
|
|
}
|
|
/**
|
|
* Initiate connection before starting main routine.
|
|
* */
|
|
private boolean initConnection(BufferedReader genericStreamReader){
|
|
int nickTail = 0; // handle appendix to nickname
|
|
|
|
this.userNick = configFile.getUserNick();
|
|
|
|
if (this.userNick == null || this.userNick.isEmpty()) {
|
|
System.out.println("Configuration issue: no nickname specified.");
|
|
return false;
|
|
}
|
|
String rawMessage;
|
|
|
|
StreamProvider.writeToStream(serverName,"NICK "+this.userNick);
|
|
StreamProvider.writeToStream(serverName,"USER "+configFile.getUserIdent()+" 8 * :"+configFile.getUserRealName()); // TODO: Add usermode 4 rusnet
|
|
|
|
if (!configFile.getUserNickPass().isEmpty() && (!configFile.getUserNickAuthStyle().isEmpty() && configFile.getUserNickAuthStyle().toLowerCase().equals("freenode")))
|
|
StreamProvider.writeToStream(serverName,"PRIVMSG NickServ :IDENTIFY "+configFile.getUserNickPass());
|
|
else if (!configFile.getUserNickPass().isEmpty() && (!configFile.getUserNickAuthStyle().isEmpty() && configFile.getUserNickAuthStyle().toLowerCase().equals("rusnet")))
|
|
StreamProvider.writeToStream(serverName,"NickServ IDENTIFY "+configFile.getUserNickPass());
|
|
else if (!configFile.getUserNickPass().isEmpty())
|
|
System.out.println("Configuration issue: Unable to determinate method of user nick identification (by password): could be \"freenode\" or \"rusnet\"\nSee \"userNickAuthStyle\".");
|
|
|
|
try {
|
|
// 431 ERR_NONICKNAMEGIVEN how can we get this?
|
|
// 432 ERR_ERRONEUSNICKNAME covered
|
|
// 433 ERR_NICKNAMEINUSE covered
|
|
// 436 ERR_NICKCOLLISION
|
|
while ((rawMessage = genericStreamReader.readLine()) != null){
|
|
System.out.println(rawMessage);
|
|
if (rawMessage.startsWith("PING :")) {
|
|
pingSrvResponse(rawMessage);
|
|
}
|
|
if (rawMessage.contains(" 001 ")) {
|
|
|
|
StringBuilder message = new StringBuilder();
|
|
|
|
if (!configFile.getUserMode().trim().isEmpty()){
|
|
String modes = configFile.getUserMode();
|
|
modes = modes.replaceAll("[\t\\s]", "");
|
|
|
|
for(char c :modes.toCharArray()) {
|
|
message.append("MODE ");
|
|
message.append(userNick);
|
|
message.append(" +");
|
|
message.append(c);
|
|
message.append("\n");
|
|
}
|
|
}
|
|
StreamProvider.writeToStream(serverName,message.toString());
|
|
|
|
return true;
|
|
}
|
|
else if (rawMessage.contains(" 433 ")) {
|
|
if (this.userNick.substring(userNick.length()-1, userNick.length()-1).equals("|"))
|
|
this.userNick = this.userNick.substring(0,userNick.length()-1)+Integer.toString(nickTail++);
|
|
else
|
|
this.userNick = this.userNick+"|"+Integer.toString(nickTail++);
|
|
|
|
StreamProvider.writeToStream(serverName,"NICK "+this.userNick);
|
|
}
|
|
else if (rawMessage.contains(" 432 ")) {
|
|
System.out.println("Configuration issue: Nickname contains unacceptable characters (432 ERR_ERRONEUSNICKNAME).");
|
|
return false;
|
|
}
|
|
}
|
|
} catch (IOException e){
|
|
System.out.println("Internal issue: DataProvider->initConnection() caused I/O exception.");
|
|
return false;
|
|
}
|
|
return false;
|
|
}
|
|
}
|