Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/main/java/com/gmail/inverseconduit/bot/Program.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public Program(ChatInterface chatInterface) throws IOException {
bots.add(DefaultBot.create(chatInterface));
bots.add(new InteractionBot(chatInterface));
bots.forEach(chatInterface::subscribe);
LOGGER.log(Level.FINE, "Basic component setup complete");
LOGGER.log(Level.INFO, "Basic component setup complete");
}

/**
Expand All @@ -56,7 +56,7 @@ public Program(ChatInterface chatInterface) throws IOException {
* Additionally all bots that were created on startup are started.
*/
public void startup() {
LOGGER.log(Level.FINER, "Beginning startup process");
LOGGER.log(Level.INFO, "Beginning startup process");
for (Integer room : config.getRooms()) {
// FIXME: isn't always Stackoverflow
chatInterface.joinChat(new SeChatDescriptor.DescriptorBuilder(SESite.STACK_OVERFLOW).setRoom(() -> room).build());
Expand Down
89 changes: 80 additions & 9 deletions src/main/java/com/gmail/inverseconduit/chat/StackExchangeChat.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,25 @@
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.xml.sax.SAXException;

import com.gargoylesoftware.htmlunit.*;
import com.gargoylesoftware.htmlunit.html.HtmlForm;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.gargoylesoftware.htmlunit.util.NameValuePair;
import com.gargoylesoftware.htmlunit.util.UrlUtils;
import com.gargoylesoftware.htmlunit.util.WebConnectionWrapper;
import com.gmail.inverseconduit.SESite;
import com.gmail.inverseconduit.datatype.*;
import com.gmail.inverseconduit.utils.PrintUtils;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.gson.Gson;

public class StackExchangeChat implements ChatInterface {
Expand Down Expand Up @@ -59,19 +70,75 @@ public StackExchangeChat() {
*/
@Override
public boolean login(ProviderDescriptor descriptor, CredentialsProvider credentials) {
HtmlPage loginPage = getLoginPage(descriptor);
if (null == loginPage) { return false; }
if (descriptor.getDescription().toString().matches("(?!.*?(?:stackoverflow|meta)).+"))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not make stackexchange the if-case and handle the problematic login in a separate method?

loggedIn = openIdLogin(credentials.getIdentificator(), credentials.getAuthenticator());
else {
HtmlPage loginPage = getLoginPage(descriptor);
if (null == loginPage) { return false; }

HtmlForm loginForm = extractLoginForm(credentials.getIdentificator(), credentials.getAuthenticator(), loginPage);
HtmlForm loginForm = extractLoginForm(credentials.getIdentificator(), credentials.getAuthenticator(), loginPage);

WebResponse response = submitLoginForm(loginForm);
if (null == response) { return false; }
WebResponse response = submitLoginForm(loginForm);
if (null == response) { return false; }

loggedIn = (response.getStatusCode() == 200);
logLoginMessage(descriptor.getDescription().toString(), credentials.getIdentificator(), response);
loggedIn = (response.getStatusCode() == 200);
logLoginMessage(descriptor.getDescription().toString(), credentials.getIdentificator(), response);
}
return loggedIn;
}

public boolean openIdLogin(String email, String password) {
try {
final DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
{ // Log into Stack Exchange.
WebResponse getRes = webClient.loadWebResponse(new WebRequest(UrlUtils.toUrlUnsafe("https://openid.stackexchange.com/account/login"), HttpMethod.GET));
if (getRes == null) {
LOGGER.log(Level.SEVERE, "Could not get OpenID fkey.");
return false;
}

String response = getRes.getContentAsString();
LOGGER.info("Got response from fetching fkey of OpenID login:" + response);

WebRequest post = new WebRequest(UrlUtils.toUrlUnsafe("https://openid.stackexchange.com/account/login/submit"), HttpMethod.POST);
try {
post.setRequestParameters(ImmutableList.of(new NameValuePair("email", email), new NameValuePair("password", password),
new NameValuePair("fkey", builder.parse(getRes.getContentAsStream()).getElementById("fkey").getAttribute("value"))));
} catch(SAXException die) {
LOGGER.log(Level.SEVERE, "Parsing response text as DOM has caused a parse exception.", die);
return false;
}
WebResponse postRes = webClient.loadWebResponse(post);
if (null == postRes || !Strings.isNullOrEmpty(postRes.getResponseHeaderValue("p3p"))) {
LOGGER.log(Level.SEVERE, "Could not post to submit of OpenID.");
return false;
}
}

{ // Log into Stack Exchange Chat.
WebResponse getRes = webClient.loadWebResponse(new WebRequest(UrlUtils.toUrlUnsafe("http://stackexchange.com/users/chat-login"), HttpMethod.GET));
WebRequest post = new WebRequest(UrlUtils.toUrlUnsafe("http://chat.stackexchange.com/users/login/global"), HttpMethod.POST);
try {
Document dom = builder.parse(getRes.getContentAsStream());
post.setRequestParameters(ImmutableList.of(new NameValuePair("authToken", dom.getElementById("authToken").getAttribute("value")), new NameValuePair("nonce",
dom.getElementById("nonce").getAttribute("value"))));
} catch(SAXException die) {
LOGGER.log(Level.SEVERE, "Parsing response text as DOM has caused a parse exception.", die);
return false;
}
post.setAdditionalHeaders(ImmutableMap.of("Origin", "http://chat.stackexchange.com", "Referer", "http://chat.stackexchange.com"));
WebResponse postRes = webClient.loadWebResponse(post);
String response = postRes.getContentAsString();
return response.contains("Welcome");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems extremely brittle to me. Probably a failed login gets us a http status code 403 instead. That would probably be better

}
} catch(ParserConfigurationException e) {
// Ignore - virtually impossible.
} catch(IOException e) {
LOGGER.log(Level.SEVERE, "Encountered IO-Exception when trying to login: ", e);
}
return false;
}

private WebResponse submitLoginForm(HtmlForm loginForm) {
WebResponse response;
try {
Expand Down Expand Up @@ -187,7 +254,7 @@ private String handleMessageOversize(final SeChatDescriptor descriptor, String m
StringBuilder messageBuilder = new StringBuilder(500);
for (String token : messageTokens) {
if (messageBuilder.length() + token.length() < 495) {
messageBuilder.append(" " + token);
messageBuilder.append(' ').append(token);
}
else {
LOGGER.log(Level.FINER, "Message split part: " + messageBuilder.toString());
Expand Down Expand Up @@ -230,7 +297,11 @@ private boolean sendMessage(String restRootUrl, String fkey, String message) {
}

/**
* {@inheritDoc}
* Queries the 5 latest messages for all chatrooms and enqueues them to
* the subscribed {@link ChatWorker Workers}, respecting the already handled
* timestamps as maintained internally.
*
* @see ChatInterface#queryMessages()
*/
@Override
public void broadcast(final String message) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ public String evaluateGroovy(ChatMessage msg, String commandText) {
}
LOGGER.info("Result:" + result);
return result == null
? String.format(":%d [tag:groovy]: no result", msg.getMessageId())
: String.format(":%d [tag:groovy]: %s", msg.getMessageId(), result.toString());
? "[tag:groovy]: no result"
: String.format("[tag:groovy]: %s", result.toString());
}

public void evaluateAndCache(String commandText) {
Expand Down