Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Rejoin channels with keys when reconnecting

Keep track of channel keys in the Channel objects.  To get the channel
key initially, send a MODE request when joining the channel and grab
the key from the reply.  Later key changes are picked up from the
server's MODE notifications in the onSetChannelKey/onRemoveChannelKey
methods.
  • Loading branch information...
commit b89916183467b6f4142d80860ee4f483102b8168 1 parent e6be035
@mkowalchuk authored
View
2  application/src/org/yaaic/activity/ConversationActivity.java
@@ -654,7 +654,7 @@ public void onClick(DialogInterface dialog, int id) {
return;
}
binder.getService().getConnection(server.getId()).setAutojoinChannels(
- server.getCurrentChannelNames()
+ server.getCurrentChannelNamesWithKeys()
);
server.setStatus(Status.CONNECTING);
binder.connect(server);
View
64 application/src/org/yaaic/irc/IRCConnection.java
@@ -22,6 +22,7 @@
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Map;
import java.util.Vector;
import java.util.regex.Pattern;
@@ -38,6 +39,7 @@
import org.yaaic.model.Server;
import org.yaaic.model.ServerInfo;
import org.yaaic.model.Status;
+import org.yaaic.utils.ModeParser;
import android.content.Intent;
@@ -390,6 +392,9 @@ protected void onJoin(String target, String sender, String login, String hostnam
target
);
service.sendBroadcast(intent);
+
+ this.sendRawLine("MODE " + target);
+
} else if (service.getSettings().showJoinPartAndQuit()) {
Message message = new Message(
service.getString(R.string.message_join, sender),
@@ -802,13 +807,19 @@ protected void onVoice(String target, String sourceNick, String sourceLogin, Str
@Override
protected void onRemoveChannelKey(String target, String sourceNick, String sourceLogin, String sourceHostname, String key)
{
+ Conversation conversation = server.getConversation(target);
+
Message message = new Message(service.getString(R.string.message_remove_channel_key, sourceNick));
message.setColor(Message.COLOR_BLUE);
- server.getConversation(target).addMessage(message);
+ conversation.addMessage(message);
service.sendBroadcast(
Broadcast.createConversationIntent(Broadcast.CONVERSATION_MESSAGE, server.getId(), target)
);
+
+ if( conversation.getType() == Conversation.TYPE_CHANNEL ) {
+ ((Channel) conversation).setKey("");
+ }
}
/**
@@ -817,13 +828,19 @@ protected void onRemoveChannelKey(String target, String sourceNick, String sourc
@Override
protected void onSetChannelKey(String target, String sourceNick, String sourceLogin, String sourceHostname, String key)
{
+ Conversation conversation = server.getConversation(target);
+
Message message = new Message(service.getString(R.string.message_set_channel_key, sourceNick, key));
message.setColor(Message.COLOR_BLUE);
- server.getConversation(target).addMessage(message);
+ conversation.addMessage(message);
service.sendBroadcast(
Broadcast.createConversationIntent(Broadcast.CONVERSATION_MESSAGE, server.getId(), target)
);
+
+ if( conversation.getType() == Conversation.TYPE_CHANNEL ) {
+ ((Channel) conversation).setKey(key);
+ }
}
/**
@@ -1107,12 +1124,18 @@ protected void onServerResponse(int code, String response)
return;
}
+ if (code == 324) {
+ // Reply to MODE #channel
+ processModeReply(response);
+ return;
+ }
+
if (code >= 200 && code < 300) {
// Skip 2XX responses
return;
}
- if (code == 353 || code == 366 || code == 332 || code == 333) {
+ if (code == 353 || code == 366 || code == 332 || code == 333 || code == 329) {
return;
}
@@ -1144,7 +1167,7 @@ public void onDisconnect()
super.onDisconnect();
if (service.getSettings().isReconnectEnabled() && server.getStatus() != Status.DISCONNECTED) {
- setAutojoinChannels(server.getCurrentChannelNames());
+ setAutojoinChannels(server.getCurrentChannelNamesWithKeys());
server.setStatus(Status.CONNECTING);
service.connect(server);
@@ -1295,6 +1318,39 @@ private void updateNickMatchPattern()
mNickMatch = Pattern.compile("(?:^|[\\s?!'�:;,.])"+Pattern.quote(getNick())+"(?:[\\s?!'�:;,.]|$)", Pattern.CASE_INSENSITIVE);
}
+
+
+ /**
+ * Process the MODE reply.
+ */
+ protected void processModeReply(String responseString)
+ {
+ try {
+ ModeParser.ChannelModeReply response = ModeParser.parseModeReply(responseString);
+
+ Conversation conversation = server.getConversation(response.getChannelName());
+ if( conversation == null || conversation.getType() != Conversation.TYPE_CHANNEL ) {
+ return;
+ }
+
+ Channel channel = (Channel) conversation;
+ Map<Character, String> modes = response.getChannelModes();
+
+ if( modes.containsKey('k') ) {
+ String key = modes.get('k');
+ channel.setKey(key);
+ }
+ else {
+ channel.setKey("");
+ }
+ }
+ catch(ModeParser.InvalidModeStringException ime) {
+ // do nothing
+ }
+ }
+
+
+
@Override
public void dispose()
{
View
25 application/src/org/yaaic/model/Channel.java
@@ -29,6 +29,8 @@
{
private String topic;
+ private String key;
+
/**
* Create a new channel object
*
@@ -38,6 +40,7 @@ public Channel(String name)
{
super(name);
this.topic = "";
+ this.key = "";
}
/**
@@ -68,4 +71,26 @@ public String getTopic()
{
return topic;
}
+
+ /**
+ * Get the channel's key
+ *
+ * @return The channel's key, or an empty string if the channel has no key
+ */
+ public String getKey()
+ {
+ return key;
+ }
+
+ /**
+ * Set the channel's key
+ *
+ * @param key The key of the channel
+ */
+ public void setKey(String key)
+ {
+ this.key = key;
+ }
+
+
}
View
11 application/src/org/yaaic/model/Server.java
@@ -395,14 +395,21 @@ public String getSelectedConversation()
*
* @return
*/
- public ArrayList<String> getCurrentChannelNames()
+ public ArrayList<String> getCurrentChannelNamesWithKeys()
{
ArrayList<String> channels = new ArrayList<String>();
Collection<Conversation> mConversations = conversations.values();
for (Conversation conversation : mConversations) {
if (conversation.getType() == Conversation.TYPE_CHANNEL) {
- channels.add(conversation.getName());
+ Channel channel = (Channel) conversation;
+ String name = channel.getName();
+ String key = channel.getKey();
+ if( key.length() > 0 ) {
+ channels.add(name + " " + key);
+ } else {
+ channels.add(name);
+ }
}
}
View
159 application/src/org/yaaic/utils/ModeParser.java
@@ -0,0 +1,159 @@
+/*
+Yaaic - Yet Another Android IRC Client
+
+Copyright 2011 Michael Kowalchuk
+
+This file is part of Yaaic.
+
+Yaaic is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Yaaic is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Yaaic. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.yaaic.utils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+public class ModeParser
+{
+ public static class InvalidModeStringException extends Exception
+ {
+ private static final long serialVersionUID = -386634658872128990L;
+ }
+
+ public static class ChannelModeReply
+ {
+ private final String channelName;
+ private final Map<Character, String> channelModes;
+
+ public ChannelModeReply(String channelName, Map<Character, String> effectiveModes) {
+ this.channelName = channelName;
+ this.channelModes = effectiveModes;
+ }
+ public String getChannelName() {
+ return channelName;
+ }
+ public Map<Character, String> getChannelModes() {
+ return channelModes;
+ }
+ }
+
+ /**
+ * Parses a numeric 324 MODE reply, which is a list of the current modes
+ * that apply to a channel.
+ */
+ public static ChannelModeReply parseModeReply(String reply) throws InvalidModeStringException
+ {
+ String[] parts = reply.split(" ");
+ if( parts.length < 3 ) {
+ throw new InvalidModeStringException();
+ }
+
+ String channelName = parts[1];
+ String modeString = parts[2];
+ ArrayList<String> modeParams = new ArrayList<String>();
+ for(int i = 3; i < parts.length; ++i) {
+ modeParams.add(parts[i]);
+ }
+
+ List<ModeChange> modeChanges = ModeParser.parseModeChanges(modeString, modeParams);
+ Map<Character,String> effectiveModes = new HashMap<Character,String>();
+
+ for(ModeChange modeChange : modeChanges) {
+ if( modeChange.getAction() == ModeChange.ACTION.REMOVING_MODE ) {
+ effectiveModes.remove(modeChange.getMode());
+ }
+ else if( modeChange.getAction() == ModeChange.ACTION.ADDING_MODE ) {
+ effectiveModes.put(modeChange.getMode(), modeChange.getParam());
+ }
+ }
+
+ return new ChannelModeReply(channelName, effectiveModes);
+ }
+
+
+ /**
+ * A single mode change, e.g., +k key
+ */
+ public static class ModeChange
+ {
+ private final ACTION action;
+ private final char mode;
+ private final String param;
+
+ public enum ACTION {
+ ADDING_MODE,
+ REMOVING_MODE
+ };
+
+ public ModeChange(ACTION action, char mode, String param) {
+ this.action = action;
+ this.mode = mode;
+ this.param = param;
+ }
+ public ModeChange(ACTION action, char mode) {
+ this(action, mode, "");
+ }
+ public ACTION getAction() {
+ return action;
+ }
+ public char getMode() {
+ return mode;
+ }
+ public String getParam() {
+ return param;
+ }
+
+ };
+
+ /**
+ * Combine a mode string (e.g., +sn-k), and a list of mode parameters.
+ */
+ public static List<ModeChange> parseModeChanges(String modeString, List<String> parameters) throws InvalidModeStringException
+ {
+ LinkedList<ModeChange> modes = new LinkedList<ModeChange>();
+
+ ModeChange.ACTION action = null;
+ Iterator<String> paramsIterator = parameters.iterator();
+ for(int i = 0; i < modeString.length(); ++i) {
+ char atPos = modeString.charAt(i);
+
+ if( atPos == '+' ) {
+ action = ModeChange.ACTION.ADDING_MODE;
+ }
+ else if( atPos == '-' ) {
+ action = ModeChange.ACTION.REMOVING_MODE;
+ }
+ else {
+ if( action == null ) {
+ throw new InvalidModeStringException();
+ }
+
+ if( "ovklb".contains(""+atPos) ) {
+ if( !paramsIterator.hasNext() ) {
+ throw new InvalidModeStringException();
+ }
+ modes.add( new ModeChange(action, atPos, paramsIterator.next() ) );
+ }
+ else {
+ modes.add( new ModeChange(action, atPos) );
+ }
+ }
+ }
+ return modes;
+ }
+}
View
1  test/src/org/yaaic/test/AllTests.java
@@ -36,6 +36,7 @@ public static Test suite() {
suite.addTest(org.yaaic.test.model.AllTests.suite());
suite.addTest(org.yaaic.test.receiver.AllTests.suite());
suite.addTest(org.yaaic.test.scenario.AllTests.suite());
+ suite.addTest(org.yaaic.test.utils.AllTests.suite());
//$JUnit-END$
return suite;
}
View
39 test/src/org/yaaic/test/utils/AllTests.java
@@ -0,0 +1,39 @@
+/*
+Yaaic - Yet Another Android IRC Client
+
+Copyright 2011 Michael Kowalchuk
+
+This file is part of Yaaic.
+
+Yaaic is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Yaaic is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Yaaic. If not, see <http://www.gnu.org/licenses/>.
+*/
+package org.yaaic.test.utils;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * All util tests
+ *
+ * @author Michael Kowalchuk <michael.kowalchuk@gmail.com>
+ */
+public class AllTests {
+ public static Test suite() {
+ TestSuite suite = new TestSuite("Utils-Tests");
+ //$JUnit-BEGIN$
+ suite.addTestSuite(ModeParserTest.class);
+ //$JUnit-END$
+ return suite;
+ }
+}
View
123 test/src/org/yaaic/test/utils/ModeParserTest.java
@@ -0,0 +1,123 @@
+/*
+Yaaic - Yet Another Android IRC Client
+
+Copyright 2011 Michael Kowalchuk
+
+This file is part of Yaaic.
+
+Yaaic is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Yaaic is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Yaaic. If not, see <http://www.gnu.org/licenses/>.
+*/
+package org.yaaic.test.utils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.yaaic.utils.ModeParser;
+import org.yaaic.utils.ModeParser.ChannelModeReply;
+import org.yaaic.utils.ModeParser.ModeChange;
+import org.yaaic.utils.ModeParser.InvalidModeStringException;
+
+public class ModeParserTest extends TestCase {
+
+ public void testEmptyModeString() throws InvalidModeStringException
+ {
+ List<ModeChange> modeChanges = ModeParser.parseModeChanges("+", new ArrayList<String>());
+ assertEquals( 0, modeChanges.size() );
+ }
+
+ public void testAddingAction() throws InvalidModeStringException
+ {
+ List<ModeChange> modeChanges = ModeParser.parseModeChanges("+s", new ArrayList<String>());
+ assertEquals( 1, modeChanges.size() );
+
+ ModeChange modeChange = modeChanges.get(0);
+ assertEquals( 's', modeChange.getMode());
+ assertEquals( ModeChange.ACTION.ADDING_MODE, modeChange.getAction() );
+ }
+
+ public void testRemovingAction() throws InvalidModeStringException
+ {
+ List<ModeChange> modeChanges = ModeParser.parseModeChanges("-s", new ArrayList<String>());
+ assertEquals( 1, modeChanges.size() );
+
+ ModeChange modeChange = modeChanges.get(0);
+ assertEquals( 's', modeChange.getMode());
+ assertEquals( ModeChange.ACTION.REMOVING_MODE, modeChange.getAction() );
+ }
+
+ public void testMissingParameter()
+ {
+ try {
+ ModeParser.parseModeChanges("+k", new ArrayList<String>());
+ fail();
+ }
+ catch(InvalidModeStringException ime) {
+ }
+ }
+
+ public void testChannelName() throws InvalidModeStringException
+ {
+ String replyString = "testuser #test +ns";
+ ChannelModeReply reply = ModeParser.parseModeReply(replyString);
+ assertEquals("#test", reply.getChannelName());
+ }
+
+ public void testNoModesSpecified() throws InvalidModeStringException
+ {
+ String replyString = "testuser #test +";
+ ChannelModeReply reply = ModeParser.parseModeReply(replyString);
+ assertEquals( 0, reply.getChannelModes().size());
+ }
+
+ public void testModesWithoutParameters() throws InvalidModeStringException
+ {
+ String replyString = "testuser #test +ns";
+ ChannelModeReply reply = ModeParser.parseModeReply(replyString);
+ Map<Character, String> modes = reply.getChannelModes();
+ assertTrue( modes.containsKey('n'));
+ assertTrue( modes.containsKey('s'));
+ }
+
+ public void testModesWithParameters() throws InvalidModeStringException
+ {
+ String replyString = "testuser #test +ks passkey";
+ ChannelModeReply reply = ModeParser.parseModeReply(replyString);
+ Map<Character, String> modes = reply.getChannelModes();
+ assertTrue( modes.containsKey('s'));
+ assertEquals( "passkey", modes.get('k') );
+ }
+
+ public void testMissingModes()
+ {
+ String replyString = "testuser #test";
+ try {
+ ModeParser.parseModeReply(replyString);
+ fail();
+ }
+ catch(InvalidModeStringException ime) {
+ }
+ }
+
+ public void testAddingAndRemovingInSingleModeString() throws InvalidModeStringException
+ {
+ String replyString = "testuser #test +s-s";
+ ChannelModeReply reply = ModeParser.parseModeReply(replyString);
+ Map<Character, String> modes = reply.getChannelModes();
+ assertFalse( modes.containsKey('s'));
+ }
+
+}
Please sign in to comment.
Something went wrong with that request. Please try again.