├── .gitignore ├── README.md ├── TorChat ├── .classpath ├── .gitignore ├── .project └── src │ └── prof7bit │ ├── reactor │ ├── Handle.java │ ├── ListenPort.java │ ├── ListenPortHandler.java │ ├── Reactor.java │ ├── TCP.java │ ├── TCPHandler.java │ ├── XConnectionClosedHere.java │ ├── XConnectionClosedRemote.java │ ├── XSocksConnectionError.java │ ├── XSocksHandshakeError.java │ └── package-info.java │ └── torchat │ └── core │ ├── Client.java │ ├── ClientHandler.java │ ├── Connection.java │ ├── MessageBuffer.java │ ├── Msg.java │ ├── MsgUnknown.java │ ├── Msg_not_implemented.java │ ├── Msg_ping.java │ ├── Msg_pong.java │ ├── XMessageParseException.java │ └── XTorChatException.java ├── TorChatAndroid ├── .classpath ├── .project ├── AndroidManifest.xml ├── ic_launcher-web.png ├── lint.xml ├── project.properties ├── res │ ├── drawable-hdpi-v11 │ │ └── ic_stat_service_running.png │ ├── drawable-hdpi-v9 │ │ └── ic_stat_service_running.png │ ├── drawable-hdpi │ │ ├── ic_action_quit.png │ │ ├── ic_action_settings.png │ │ ├── ic_launcher.png │ │ └── ic_stat_service_running.png │ ├── drawable-ldpi-v11 │ │ └── ic_stat_service_running.png │ ├── drawable-ldpi-v9 │ │ └── ic_stat_service_running.png │ ├── drawable-ldpi │ │ ├── ic_action_quit.png │ │ ├── ic_action_settings.png │ │ ├── ic_launcher.png │ │ └── ic_stat_service_running.png │ ├── drawable-mdpi-v11 │ │ └── ic_stat_service_running.png │ ├── drawable-mdpi-v9 │ │ └── ic_stat_service_running.png │ ├── drawable-mdpi │ │ ├── ic_action_quit.png │ │ ├── ic_action_settings.png │ │ ├── ic_launcher.png │ │ └── ic_stat_service_running.png │ ├── drawable-xhdpi-v11 │ │ └── ic_stat_service_running.png │ ├── drawable-xhdpi-v9 │ │ └── ic_stat_service_running.png │ ├── drawable-xhdpi │ │ ├── ic_action_quit.png │ │ ├── ic_action_settings.png │ │ ├── ic_launcher.png │ │ └── ic_stat_service_running.png │ ├── layout │ │ └── l_roster.xml │ ├── menu │ │ └── m_roster.xml │ ├── values-v11 │ │ └── styles.xml │ ├── values-v14 │ │ └── styles.xml │ └── values │ │ ├── strings.xml │ │ └── styles.xml └── src │ └── prof7bit │ └── torchat │ └── android │ ├── gui │ └── TorChat.java │ └── service │ ├── Backend.java │ └── PrintlnRedirect.java └── TorChatTests ├── .classpath ├── .gitignore ├── .project └── src └── prof7bit ├── reactor └── TestTCP.java └── torchat └── core └── TestMessageBuffer.java /.gitignore: -------------------------------------------------------------------------------- 1 | TorChatAndroid/gen 2 | TorChatAndroid/assets 3 | TorChatAndroid/bin 4 | TorChatAndroid/.settings 5 | TorChatAndroid/build.xml 6 | TorChatAndroid/local.properties 7 | TorChatAndroid/proguard-project.txt 8 | TorChatAndroid/doc 9 | TorChatAndroid/libs 10 | 11 | TorChat/bin 12 | 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TorChat for Android 2 | 3 | I'm going to implement everything in Java. This is still incomplete, not much to see here at the moment. 4 | -------------------------------------------------------------------------------- /TorChat/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /TorChat/.gitignore: -------------------------------------------------------------------------------- 1 | /.settings 2 | -------------------------------------------------------------------------------- /TorChat/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | TorChat 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.jdt.core.javanature 16 | 17 | 18 | -------------------------------------------------------------------------------- /TorChat/src/prof7bit/reactor/Handle.java: -------------------------------------------------------------------------------- 1 | package prof7bit.reactor; 2 | 3 | import java.io.IOException; 4 | import java.nio.channels.SelectableChannel; 5 | 6 | /** 7 | * Abstract base class for TCP and ListenPort. 8 | * 9 | * All descendants of the Handle class will be registered with a Reactor 10 | * and they will all have their own event handler interface to which the 11 | * Reactor will dispatch the events they subscribed for. 12 | * 13 | * @author Bernd Kreuss 14 | */ 15 | public abstract class Handle { 16 | 17 | protected SelectableChannel channel; 18 | protected Reactor reactor; 19 | 20 | protected void registerWithReactor(int ops){ 21 | reactor.register(this, ops); 22 | } 23 | 24 | public void close(IOException reason){ 25 | reactor.requestCloseHandle(this, reason); 26 | } 27 | 28 | public void close(String reason){ 29 | close(new XConnectionClosedHere(reason)); 30 | } 31 | 32 | abstract void doEventClose(IOException e); 33 | } 34 | -------------------------------------------------------------------------------- /TorChat/src/prof7bit/reactor/ListenPort.java: -------------------------------------------------------------------------------- 1 | package prof7bit.reactor; 2 | 3 | import java.io.IOException; 4 | import java.net.InetSocketAddress; 5 | import java.nio.channels.SelectionKey; 6 | import java.nio.channels.ServerSocketChannel; 7 | import java.nio.channels.SocketChannel; 8 | 9 | /** 10 | * This class represents a network port listening for incoming TCP connections. 11 | * It is a thin wrapper around ServerSocketChannel. ListenPortHandler.onAccept() must be 12 | * implemented by the application and will be fired when an incoming TCP 13 | * connection is accepted. 14 | * 15 | * @author Bernd Kreuss 16 | */ 17 | public class ListenPort extends Handle{ 18 | 19 | /** 20 | * The application's event handler will be assigned here 21 | */ 22 | private ListenPortHandler eventHandler; 23 | 24 | public ListenPort(Reactor r, ListenPortHandler eh){ 25 | checkReactor(r); 26 | checkHandler(eh); 27 | reactor = r; 28 | eventHandler = eh; 29 | } 30 | 31 | private void checkReactor(Reactor r) { 32 | if (r == null){ 33 | throw new IllegalArgumentException("ListenPort reactor must not be null"); 34 | } 35 | } 36 | 37 | private void checkHandler(ListenPortHandler eh) { 38 | if (eh == null){ 39 | throw new IllegalArgumentException("ListenPort event handler must not be null"); 40 | } 41 | } 42 | 43 | /** 44 | * bind the port and start listening for incoming TCP connections. 45 | * Every incoming TCP connection will now be automatically accepted and 46 | * every time this happens the onAccept() handler will be called along 47 | * with a newly instantiated TCP object. 48 | * 49 | * @param port the port to bind 50 | * @throws IOException socket cannot be opened or bound 51 | */ 52 | public void listen(int port) throws IOException{ 53 | ServerSocketChannel ssc = ServerSocketChannel.open(); 54 | ssc.socket().setReuseAddress(true); 55 | ssc.socket().bind(new InetSocketAddress(port)); 56 | ssc.configureBlocking(false); 57 | channel = ssc; 58 | registerWithReactor(SelectionKey.OP_ACCEPT); 59 | } 60 | 61 | /** 62 | * called by the reactor. 63 | */ 64 | protected void doEventAccept() throws IOException{ 65 | ServerSocketChannel ssc = (ServerSocketChannel) channel; 66 | SocketChannel sc = ssc.accept(); 67 | TCP tcp = new TCP(reactor, sc); 68 | TCPHandler eh = eventHandler.onAccept(tcp); 69 | tcp.setEventHandler(eh); 70 | } 71 | 72 | /** 73 | * called by the reactor when the listening socket is shut down (normally 74 | * only when the application terminates). Not much need to pass this event 75 | * to the application, just ignore it. 76 | */ 77 | @Override 78 | void doEventClose(IOException e) { 79 | // ListenPort will just ignore this event 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /TorChat/src/prof7bit/reactor/ListenPortHandler.java: -------------------------------------------------------------------------------- 1 | package prof7bit.reactor; 2 | 3 | /** 4 | * The application must provide an implementation of this interface 5 | * to be able to receive notifications about events 6 | */ 7 | public interface ListenPortHandler { 8 | TCPHandler onAccept(TCP tcp); 9 | } -------------------------------------------------------------------------------- /TorChat/src/prof7bit/reactor/Reactor.java: -------------------------------------------------------------------------------- 1 | package prof7bit.reactor; 2 | 3 | import java.io.IOException; 4 | import java.nio.channels.ClosedChannelException; 5 | import java.nio.channels.SelectionKey; 6 | import java.nio.channels.Selector; 7 | import java.nio.channels.SocketChannel; 8 | import java.util.Iterator; 9 | import java.util.Queue; 10 | import java.util.concurrent.ConcurrentLinkedQueue; 11 | 12 | 13 | 14 | /** 15 | * This implements the reactor pattern on top of the java.nio.Selector class. 16 | * 17 | * @author Bernd Kreuss 18 | */ 19 | public class Reactor extends Thread{ 20 | 21 | /** 22 | * The underlying java.nio.Selector used by this reactor. 23 | */ 24 | private Selector selector; 25 | 26 | /** 27 | * tasks that need to be run between two selector.select() calls 28 | * by the rector thread, such as registration of channels, etc 29 | * are enqueued here and will be run immediately before select() 30 | */ 31 | private Queue pendingTasks = new ConcurrentLinkedQueue(); 32 | 33 | /** 34 | * Internal flag to signal thread termination request. 35 | */ 36 | private Boolean terminating = false; 37 | 38 | /** 39 | * Create a new Reactor object and start it. This is usually one of the 40 | * first things the application does. After this the application can go 41 | * on and create ListenPorts and TCP objects and pass them a reference 42 | * to this reactor. Before the application ends the reactor's close() 43 | * method should be called to cleanly terminate and join the thread. 44 | * 45 | * @throws IOException if I/O error occurs 46 | */ 47 | public Reactor() throws IOException{ 48 | selector = Selector.open(); 49 | start(); 50 | } 51 | 52 | /** 53 | * Request the Reactor thread and all associated network activity to 54 | * be shut down in an orderly manner and terminated as soon as possible. 55 | * This method will block until it actually *is* terminated. All open 56 | * handles associated with this reactor will be closed, all TCP disconnect 57 | * handlers will have been called before this method returns. 58 | * 59 | * @throws InterruptedException if interrupt() while in the join() call 60 | */ 61 | public void close() throws InterruptedException{ 62 | addTask(new CloseAllAndTerminateRequest()); 63 | join(); 64 | } 65 | 66 | /** 67 | * This is the thread's run method. Don't call this, its called 68 | * automatically when the thread starts. It will end itself and 69 | * clean up everything as soon as possible when close() is called. 70 | */ 71 | @Override 72 | public void run(){ 73 | try { 74 | while(!terminating){ 75 | select(0); 76 | } 77 | } catch (Exception e) { 78 | System.err.println("WTF??? BUG: fatal error in select loop"); 79 | e.printStackTrace(); 80 | System.exit(1); 81 | } 82 | 83 | try { 84 | selector.close(); 85 | } catch (Exception e) { 86 | System.err.println("WTF??? BUG: fatal error when closing selector"); 87 | e.printStackTrace(); 88 | System.exit(1); 89 | } 90 | } 91 | 92 | /** 93 | * This method is called in an infinite loop to wait for events and 94 | * dispatch them. It will perform all pending registration requests, then 95 | * block until an event on one of the registered Handle objects happens 96 | * and then dispatch them one after the other to their event handlers. 97 | * After this it will return and has to be called again. 98 | * 99 | * @param timeout maximum milliseconds to block, 0 means infinite time. 100 | * @throws IOException shouldn't ever happen if used correctly. 101 | */ 102 | private void select(long timeout) throws IOException { 103 | 104 | // perform any pending registration or cancellation requests 105 | while (!pendingTasks.isEmpty()){ 106 | pendingTasks.poll().run(); 107 | } 108 | 109 | selector.select(timeout); 110 | 111 | for (Iterator iter = selector.selectedKeys().iterator(); iter.hasNext();) { 112 | SelectionKey key = iter.next(); 113 | iter.remove(); 114 | try { 115 | if (key.isConnectable()) { 116 | // outgoing connection established or connection failed. 117 | // (either finishConnect throws or it succeeds) 118 | ((SocketChannel) key.channel()).finishConnect(); 119 | ((TCP) key.attachment()).doEventConnect(); 120 | } 121 | 122 | if (key.isAcceptable()) { 123 | // new incoming connection 124 | ((ListenPort) key.attachment()).doEventAccept(); 125 | } 126 | 127 | if (key.isReadable()) { 128 | // bytes received or disconnect. 129 | ((TCP) key.attachment()).doEventRead(); 130 | } 131 | 132 | if (key.isWritable()) { 133 | // send buffer has become empty again, can write more data. 134 | // Only connections that currently have anything queued will 135 | // temporarily register for this operation and they will 136 | // unregister themselves once their queue becomes empty. 137 | ((TCP) key.attachment()).doEventWrite(); 138 | } 139 | 140 | } catch (IOException e) { 141 | // on any IO exception we simply close the Handle 142 | // which will make it fire its onDisconnect() event. 143 | requestCloseHandle((Handle) key.attachment(), e); 144 | } 145 | } 146 | } 147 | 148 | /** 149 | * Register a Handle object with this Reactor or change its registration. 150 | * This method is automatically invoked by the handle objects themselves. 151 | * This method is thread safe and will not block. All registration requests 152 | * will be queued to be run from the Reactor thread immediately before the 153 | * next call to selector.select(). This method is automatically called by 154 | * the Handle objects themselves when needed. 155 | * 156 | * @param h The TCP or ListenPort that wishes to register 157 | * @param ops interested operations (SelectionKey.OP_XXX bitmap) 158 | */ 159 | protected void register(Handle h, int ops){ 160 | addTask(new RegistrationRequest(h, ops)); 161 | } 162 | 163 | /** 164 | * Request the handle to be closed. This will also make it fire 165 | * the onDisonnect() handler if it is a TCP connection. This method 166 | * is automatically called by the handle objects themselves. 167 | * 168 | * @param h the Handle to close 169 | * @param reason IOException that should be passed to the event handler 170 | */ 171 | protected void requestCloseHandle (Handle h, IOException reason){ 172 | addTask(new CloseRequest(h, reason)); 173 | } 174 | 175 | /** 176 | * enqueue additional code to be run (once) before the next select() call 177 | * 178 | * @param r Runnable object containing the code 179 | */ 180 | private void addTask(Runnable r){ 181 | pendingTasks.offer(r); 182 | selector.wakeup(); 183 | } 184 | 185 | /** 186 | * A request to register a Handle with this Reactor. Instances of this 187 | * class can be enqueued to be run between two calls to selector.select() 188 | */ 189 | private class RegistrationRequest implements Runnable { 190 | private Handle handle; 191 | private int operations; 192 | 193 | public RegistrationRequest(Handle h, int ops){ 194 | handle = h; 195 | operations = ops; 196 | } 197 | 198 | @Override 199 | public void run(){ 200 | try { 201 | handle.channel.register(handle.reactor.selector, operations, handle); 202 | } catch (ClosedChannelException e) { 203 | e.printStackTrace(); 204 | // nothing we can do here, just ignore it 205 | } 206 | } 207 | } 208 | 209 | /** 210 | * A request to close a handle. Closing a handle should not happen from 211 | * a different thread while the selector is currently waiting on it, so 212 | * we simply make this a queued request to be run from the selector thread. 213 | * This will also fire the onDisconnect() event because the selector won't 214 | * be able to do it anymore after the handle is closed and unregistered. 215 | */ 216 | private class CloseRequest implements Runnable { 217 | private Handle handle; 218 | private IOException reason; 219 | 220 | public CloseRequest(Handle handle, IOException reason){ 221 | this.handle = handle; 222 | this.reason = reason; 223 | } 224 | 225 | @Override 226 | public void run(){ 227 | try { 228 | handle.channel.close(); 229 | handle.doEventClose(reason); 230 | } catch (IOException e) { 231 | e.printStackTrace(); 232 | // ignore 233 | } 234 | } 235 | } 236 | 237 | /** 238 | * This is a sub-request of CloseAllAndTerminateRequest 239 | */ 240 | private class TerminateRequest implements Runnable{ 241 | 242 | @Override 243 | public void run(){ 244 | terminating = true; 245 | } 246 | } 247 | 248 | /** 249 | * This will be enqueued to shut down and terminate the reactor, it 250 | * will also close all handles and all disconnect handlers will fire 251 | */ 252 | private class CloseAllAndTerminateRequest implements Runnable { 253 | 254 | @Override 255 | public void run() { 256 | for (SelectionKey key:selector.keys()) { 257 | addTask(new CloseRequest((Handle) key.attachment(), 258 | new XConnectionClosedHere("reactor shutdown"))); 259 | } 260 | addTask(new TerminateRequest()); 261 | } 262 | } 263 | } 264 | -------------------------------------------------------------------------------- /TorChat/src/prof7bit/reactor/TCP.java: -------------------------------------------------------------------------------- 1 | package prof7bit.reactor; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.IOException; 5 | import java.net.InetSocketAddress; 6 | import java.net.SocketAddress; 7 | import java.nio.ByteBuffer; 8 | import java.nio.channels.SelectionKey; 9 | import java.nio.channels.SocketChannel; 10 | import java.util.Locale; 11 | import java.util.Queue; 12 | import java.util.concurrent.ConcurrentLinkedQueue; 13 | 14 | /** 15 | * An Instance of this class represents a TCP connection. The application 16 | * must implement the ListenPortHandler interface and assign it to the eventHandler 17 | * member in order to receive onConnect(), onDisconnect() and onReceive() 18 | * events. TCP is a thin wrapper around java.nio.SocketChannel. TCP also 19 | * implements asynchronous sending of outgoing data. Every TCP object is 20 | * associated with a Reactor object and automatically subscribes to the 21 | * needed events to make the above things happen. 22 | * 23 | * @author Bernd Kreuss 24 | */ 25 | public class TCP extends Handle { 26 | 27 | /** 28 | * The application's event handler will be assigned here 29 | */ 30 | private TCPHandler eventHandler; 31 | 32 | /** 33 | * This holds a queue of unsent or partially sent ByteBuffers if more 34 | * data has been attempted to send than the underlying network socket 35 | * could handle at once. 36 | */ 37 | private Queue unsent = new ConcurrentLinkedQueue(); 38 | 39 | /** 40 | * This signals that we may not yet subscribe to OP_WRITE and not yet send 41 | * queued data because we are still talking to the socks proxy. The socks 42 | * connection handler itself does not use the send queue at all. 43 | */ 44 | private boolean insideSocksHandshake = false; 45 | 46 | /** 47 | * Construct a new incoming TCP. 48 | * The Framework will call this constructor automatically for incoming 49 | * connections. Note that we do not yet initialize the event handler here, 50 | * for incoming connections this will happen *after* the onAccept() has 51 | * returned. See the code of ListenPort.doEventAccept() for details. 52 | * 53 | * @param r the reactor that should manage this TCP object 54 | * @param sc the underlying connected SocketChannel 55 | * @throws IOException if I/O error occurs 56 | */ 57 | public TCP(Reactor r, SocketChannel sc) throws IOException { 58 | System.out.println(this.toString() + " incoming constructor"); 59 | initMembers(sc, r); 60 | registerWithReactor(SelectionKey.OP_READ); 61 | } 62 | 63 | /** 64 | * Construct a new outgoing connection. 65 | * This will create the Handle object and initiate the connect. It will 66 | * not block and the application can immediately start using the send() 67 | * method (which also will not block), even if the connection has not yet 68 | * been established. Some time later the appropriate eventHandler method will 69 | * be fired once the connection succeeds or fails. 70 | * 71 | * @param r the reactor that should manage this TCP object 72 | * @param addr the server to connect to 73 | * @param port the port of the server to connect to 74 | * @param eh the event handler that will receive the events 75 | * @throws IOException if I/O error occurs 76 | */ 77 | public TCP(Reactor r, String addr, int port, TCPHandler eh) throws IOException{ 78 | System.out.println(this.toString() + " outgoing constructor"); 79 | connect(r, addr, port, eh); 80 | } 81 | 82 | /** 83 | * Construct a new outgoing TCP connection through a socks4a proxy. 84 | * Towards the application this behaves exactly like the other constructor, 85 | * you can immediately start sending (queued), etc. The only difference is 86 | * this will connect through a socks4a proxy (4a means the socks proxy will 87 | * resolve host names) 88 | * 89 | * @param r The reactor that should manage this TCP object 90 | * @param addr The server to connect to 91 | * @param port The port of the server to connect to 92 | * @param eh The event handler of the application, may NOT be null 93 | * @param proxy_addr address of the socks proxy 94 | * @param proxy_port port of the socks proxy 95 | * @param proxy_user user to use in socks4 authentication 96 | * @throws IOException if an I/O error occurs while initializing socket 97 | */ 98 | public TCP(Reactor r, String addr, int port, TCPHandler eh, String proxy_addr, int proxy_port, String proxy_user) throws IOException{ 99 | checkEventHandler(eh); 100 | // the socks handler will upon successful connection replace itself 101 | // with the event handler that was provided by the application. 102 | Socks4aHandler sockshandler = new Socks4aHandler(this, addr, port, proxy_user, eh); 103 | connect(r, proxy_addr, proxy_port, sockshandler); 104 | } 105 | 106 | /** 107 | * this is only meant to be used from inside the constructor 108 | */ 109 | private void connect(Reactor r, String addr, int port, TCPHandler eh) throws IOException { 110 | SocketChannel sc = SocketChannel.open(); 111 | initMembers(sc, r); 112 | setEventHandler(eh); 113 | SocketAddress sa = new InetSocketAddress(addr, port); 114 | if (sc.connect(sa)){ 115 | // according to documentation it is possible for local connections 116 | // to succeed instantaneously. Then there won't be an OP_CONNECT 117 | // event later, we must call the handler directly from here. 118 | doEventConnect(); 119 | }else{ 120 | // this is what normally happens in most cases 121 | registerWithReactor(SelectionKey.OP_CONNECT); 122 | } 123 | } 124 | 125 | /** 126 | * initialize some member variables, used during construction 127 | */ 128 | private void initMembers(SocketChannel sc, Reactor r) throws IOException{ 129 | sc.configureBlocking(false); 130 | sc.socket().setTcpNoDelay(true); 131 | channel = sc; 132 | reactor = r; 133 | } 134 | 135 | /** 136 | * The application must use this to register its event handler if it 137 | * receives a new incoming connection. For outgoing connections this 138 | * has happened in the constructor already. 139 | * 140 | * @param eventHandler ListenPortHandler implementation provided by application 141 | */ 142 | public void setEventHandler(TCPHandler eventHandler) { 143 | checkEventHandler(eventHandler); 144 | this.eventHandler = eventHandler; 145 | } 146 | 147 | private void checkEventHandler(TCPHandler eh){ 148 | if (eh == null){ 149 | throw new IllegalArgumentException("TCP event handler must not be null"); 150 | } 151 | } 152 | 153 | /** 154 | * Send the bytes in the buffer asynchronously. Data will be enqueued 155 | * for sending and OP_WRITE events will be used to send it from the Reactor 156 | * thread until all data has been sent. This method can be used even before 157 | * the underlying connection has actually been established yet, data will 158 | * be queued and sent upon successful connect. 159 | * 160 | * @param buf the ByteBuffer containing the bytes to be sent. 161 | */ 162 | public void send(ByteBuffer buf){ 163 | buf.position(0); 164 | unsent.offer(buf); 165 | if (insideSocksHandshake){ 166 | return; 167 | } 168 | if (((SocketChannel) channel).isConnected()){ 169 | registerWithReactor(SelectionKey.OP_READ | SelectionKey.OP_WRITE); 170 | } 171 | // if not yet connected or if still inside socks handshake then 172 | // subscribing OP_WRITE will be deferred until connection is complete. 173 | // The Socks handler itself will bypass the queue and write directly. 174 | } 175 | 176 | /** 177 | * this is used only during socks connect, here don't want to use the 178 | * send queue because the queue contains data sent from the application 179 | * which must not be mixed with data sent during the socks handshake. 180 | * 181 | * @param buf ByteBuffer with data to send 182 | * @throws IOException 183 | */ 184 | private void sendNow(ByteBuffer buf) throws IOException{ 185 | SocketChannel sc = (SocketChannel) channel; 186 | while (buf.hasRemaining()){ 187 | sc.write(buf); 188 | } 189 | } 190 | 191 | /** 192 | * This method is automatically called by the Reactor. It can handle 193 | * receive events and also detect a disconnect from the remote host. 194 | * 195 | * @throws IOException if that happens the reactor will close the 196 | * connection and fire the onDisconnect() event. 197 | */ 198 | protected void doEventRead() throws IOException{ 199 | System.out.println(this.toString() + " doEventRead()"); 200 | ByteBuffer buf = ByteBuffer.allocate(2048); 201 | SocketChannel sc = (SocketChannel)channel; 202 | int numRead = sc.read(buf); 203 | if (numRead == -1){ 204 | // this will make the reactor close the channel 205 | // and then fire our onDisconnect() event. 206 | throw new XConnectionClosedRemote("closed by foreign host"); 207 | }else{ 208 | buf.position(0); 209 | buf.limit(numRead); 210 | eventHandler.onReceive(buf); 211 | } 212 | } 213 | 214 | /** 215 | * This method is automatically called by the Reactor. The IOException 216 | * object tells the reason why exactly it had to be closed. This event 217 | * also occurs when a connect attempt failed. 218 | */ 219 | @Override 220 | protected void doEventClose(IOException e){ 221 | System.out.println(this.toString() + " doEventClose() " + e.getMessage()); 222 | eventHandler.onDisconnect(e); 223 | } 224 | 225 | /** 226 | * This method is automatically called by the Reactor. Here we can also 227 | * register OP_WRITE if we have queued data to send already. 228 | * 229 | * Note that we will NOT register OP_WRITE if we did connect to a socks 230 | * proxy, in this case the queued data must wait even longer, the socks 231 | * handler will finally fire doEventConnect() a second time and then we 232 | * will be back to normal and the app will receive the connect event. 233 | */ 234 | protected void doEventConnect() { 235 | System.out.println(this.toString() + " doEventConnect()"); 236 | 237 | if (unsent.isEmpty() | insideSocksHandshake){ 238 | registerWithReactor(SelectionKey.OP_READ); 239 | }else{ 240 | registerWithReactor(SelectionKey.OP_READ | SelectionKey.OP_WRITE); 241 | } 242 | eventHandler.onConnect(); 243 | } 244 | 245 | /** 246 | * Automatically called by the Reactor. This event will not be propagated 247 | * to the application, it is only used internally to automatically send 248 | * remaining data from the unsent queue. Any IOException happening here 249 | * will cause the channel to be closed and onDisconnect() to be fired. 250 | * Note that this event won't be fired during a Socks handshake, and the 251 | * Socks handler will bypass the send queue entirely. 252 | * 253 | * @throws IOException 254 | */ 255 | protected void doEventWrite() throws IOException{ 256 | System.out.println(this.toString() + " doEventWrite()"); 257 | SocketChannel sc = (SocketChannel)channel; 258 | 259 | // we will try to write as many buffers as possible in one event 260 | // we break on the first sign of congestion (partial buffer) 261 | while(true){ 262 | ByteBuffer buf = unsent.peek(); 263 | if (buf == null){ 264 | // we are done, queue is empty, re-register without OP_WRITE 265 | registerWithReactor(SelectionKey.OP_READ); 266 | break; 267 | } 268 | 269 | sc.write(buf); 270 | if (buf.hasRemaining()){ 271 | break; // congestion --> enough for the moment 272 | }else{ 273 | unsent.remove(); // ok --> try next buffer 274 | } 275 | } 276 | } 277 | 278 | /** 279 | * This event handler implements the client side of a Socks4a connection 280 | * request. After it has successfully succeeded the handler will replace 281 | * itself with the handler that the application has provided earlier and 282 | * normal TCP sending and receiving for the application may take place. 283 | */ 284 | private class Socks4aHandler implements TCPHandler{ 285 | private TCP tcp; 286 | private String address; 287 | private int port; 288 | private String user; // user-ID for Socks-Proxy 289 | private TCPHandler appHandler; 290 | 291 | /** 292 | * Create a new event handler to handle the socks 4a connection protocol 293 | * 294 | * @param tcp the TCP-object for which this handler will be used. This 295 | * must be a TCP that is already connected to the socks4a proxy 296 | * @param address The address or hostname to connect to 297 | * @param port The port to connect to 298 | * @param user User-ID used during Socks4a protocol 299 | * @param appHandler EeventHandler to install after connection succeeded 300 | */ 301 | public Socks4aHandler(TCP tcp, String address, int port, String user, TCPHandler appHandler){ 302 | this.tcp = tcp; 303 | this.address = address; 304 | this.port = port; 305 | this.user = user; 306 | this.appHandler = appHandler; 307 | tcp.insideSocksHandshake = true; 308 | } 309 | 310 | @Override 311 | public void onConnect() { 312 | System.out.println("socks4a onConnect()"); 313 | ByteArrayOutputStream req = new ByteArrayOutputStream(64); 314 | req.write(0x04); // socks version 4 315 | req.write(0x01); // request TCP stream connection 316 | 317 | // port of the server to connect to in big-endian 318 | req.write((byte) ((port & 0xff00) >> 8)); 319 | req.write((byte) (port & 0x00ff)); 320 | 321 | // deliberately invalid IP address to denote that we wish the 322 | // 4a variant of the socks protocol (proxy will resolve name) 323 | req.write(0x00); 324 | req.write(0x00); 325 | req.write(0x00); 326 | req.write(0x01); 327 | 328 | // User-ID, null-terminated string 329 | byte[] buser = user.getBytes(); 330 | req.write(buser, 0, buser.length); 331 | req.write(0x00); 332 | 333 | // host name to connect to, null-terminated string 334 | byte[] baddr = address.getBytes(); 335 | req.write(baddr, 0, baddr.length); 336 | req.write(0x00); 337 | 338 | // now we send the request. Note that we do not send it through the 339 | // queue, we send it immediately. After this the proxy will send us 340 | // an answer about success or failure, we handle that in onReceive() 341 | try { 342 | sendNow(ByteBuffer.wrap(req.toByteArray())); 343 | } catch (IOException e) { 344 | e.printStackTrace(); 345 | tcp.close(e); 346 | } 347 | } 348 | 349 | @Override 350 | public void onDisconnect(Exception e) { 351 | System.out.println("socks4a onDisconnect()"); 352 | appHandler.onDisconnect(e); 353 | } 354 | 355 | @Override 356 | public void onReceive(ByteBuffer buf) { 357 | System.out.println("socks4a onReceive()"); 358 | if (buf.limit() != 8){ 359 | tcp.close(new XSocksHandshakeError("malformed reply from socks proxy")); 360 | return; 361 | } 362 | byte status = buf.array()[1]; 363 | if (status != 0x5a){ 364 | String msg = String.format(Locale.ENGLISH, "socks4a error %d while connecting %s:%s", status, address, port); 365 | tcp.close(new XSocksConnectionError(msg, status)); 366 | return; 367 | } 368 | 369 | // tcp stream established, now hand over all control to the application 370 | tcp.setEventHandler(appHandler); 371 | tcp.insideSocksHandshake = false; 372 | tcp.doEventConnect(); 373 | } 374 | } 375 | } 376 | -------------------------------------------------------------------------------- /TorChat/src/prof7bit/reactor/TCPHandler.java: -------------------------------------------------------------------------------- 1 | package prof7bit.reactor; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | /** 6 | * The application must provide an implementation of this interface 7 | * to be able to receive notifications about events 8 | */ 9 | public interface TCPHandler { 10 | public void onConnect(); 11 | public void onDisconnect(Exception e); 12 | public void onReceive(ByteBuffer buf); 13 | } -------------------------------------------------------------------------------- /TorChat/src/prof7bit/reactor/XConnectionClosedHere.java: -------------------------------------------------------------------------------- 1 | package prof7bit.reactor; 2 | 3 | import java.io.IOException; 4 | 5 | public class XConnectionClosedHere extends IOException { 6 | private static final long serialVersionUID = 377644111604273744L; 7 | 8 | public XConnectionClosedHere(String detailMessage) { 9 | super(detailMessage); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /TorChat/src/prof7bit/reactor/XConnectionClosedRemote.java: -------------------------------------------------------------------------------- 1 | package prof7bit.reactor; 2 | 3 | import java.io.IOException; 4 | 5 | public class XConnectionClosedRemote extends IOException { 6 | private static final long serialVersionUID = 3587976820171006786L; 7 | 8 | public XConnectionClosedRemote(String detailMessage) { 9 | super(detailMessage); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /TorChat/src/prof7bit/reactor/XSocksConnectionError.java: -------------------------------------------------------------------------------- 1 | package prof7bit.reactor; 2 | 3 | import java.io.IOException; 4 | 5 | public class XSocksConnectionError extends IOException { 6 | private static final long serialVersionUID = 6259586626320066994L; 7 | private int statusCode; 8 | 9 | public XSocksConnectionError(String detail, int statusCode){ 10 | super(detail); 11 | this.statusCode = statusCode; 12 | } 13 | 14 | public int getStatusCode(){ 15 | return statusCode; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /TorChat/src/prof7bit/reactor/XSocksHandshakeError.java: -------------------------------------------------------------------------------- 1 | package prof7bit.reactor; 2 | 3 | import java.io.IOException; 4 | 5 | public class XSocksHandshakeError extends IOException { 6 | private static final long serialVersionUID = 7762999740312591839L; 7 | 8 | public XSocksHandshakeError(String detailMessage) { 9 | super(detailMessage); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /TorChat/src/prof7bit/reactor/package-info.java: -------------------------------------------------------------------------------- 1 | package prof7bit.reactor; 2 | -------------------------------------------------------------------------------- /TorChat/src/prof7bit/torchat/core/Client.java: -------------------------------------------------------------------------------- 1 | package prof7bit.torchat.core; 2 | 3 | import java.io.IOException; 4 | 5 | import prof7bit.reactor.ListenPort; 6 | import prof7bit.reactor.ListenPortHandler; 7 | import prof7bit.reactor.Reactor; 8 | import prof7bit.reactor.TCP; 9 | import prof7bit.reactor.TCPHandler; 10 | 11 | public class Client implements ListenPortHandler { 12 | private ClientHandler clientHandler; 13 | private Reactor reactor; 14 | private ListenPort listenPort; 15 | 16 | public Client(ClientHandler clientHandler, int port) throws IOException { 17 | this.clientHandler = clientHandler; 18 | this.reactor = new Reactor(); 19 | this.listenPort = new ListenPort(reactor, this); 20 | this.listenPort.listen(port); 21 | } 22 | 23 | public void close() throws InterruptedException { 24 | this.reactor.close(); 25 | } 26 | 27 | @Override 28 | public TCPHandler onAccept(TCP tcp) { 29 | Connection c = new Connection(tcp); 30 | return c; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /TorChat/src/prof7bit/torchat/core/ClientHandler.java: -------------------------------------------------------------------------------- 1 | package prof7bit.torchat.core; 2 | 3 | /** 4 | * Event handler for events that the TorChat client will produce. 5 | * The application must implement this and pass it to the Client 6 | * instance to be notified about events. 7 | * 8 | * @author Bernd Kreuss 9 | * 10 | */ 11 | public interface ClientHandler { 12 | // TODO 13 | } 14 | -------------------------------------------------------------------------------- /TorChat/src/prof7bit/torchat/core/Connection.java: -------------------------------------------------------------------------------- 1 | package prof7bit.torchat.core; 2 | 3 | import java.io.EOFException; 4 | import java.io.IOException; 5 | import java.nio.ByteBuffer; 6 | 7 | import prof7bit.reactor.TCPHandler; 8 | import prof7bit.reactor.Reactor; 9 | import prof7bit.reactor.TCP; 10 | 11 | /** 12 | * This class represents an established TorChat p2p connection, it can either 13 | * be an incoming or outgoing connection. Every buddy always needs both of them. 14 | * 15 | * @author Bernd Kreuss 16 | * 17 | */ 18 | public class Connection implements TCPHandler{ 19 | private TCP tcp; 20 | private byte[] bufIncomplete = new byte[0]; 21 | 22 | public void send(MessageBuffer b){ 23 | tcp.send(b.encodeForSending()); 24 | } 25 | 26 | /** 27 | * Here we have accepted an incoming connection, this constructor 28 | * was called by our Listener. The Handle exists and is connected 29 | * already, we can use it right away. 30 | * 31 | * @param c an already connected Handle object 32 | */ 33 | public Connection(TCP c){ 34 | tcp = c; 35 | } 36 | 37 | /** 38 | * Create a new outgoing connection through the Tor proxy (Socks4a) 39 | * The constructor will return immediately and a new Handle will be created 40 | * but it is not yet finished connecting. After some time either the 41 | * onConnect() or the onDisconnect() method will be called. We can already 42 | * start sending, it will be queued until connect succeeds. 43 | * 44 | * @param r the reactor that should monitor this connection 45 | * @param addr IP-address or host name to connect to 46 | * @param port Port to connect to 47 | * @throws IOException problems opening the local socket (not the connection itself) 48 | */ 49 | public Connection(Reactor r, String addr, int port) throws IOException{ 50 | tcp = new TCP(r, addr, port, this, "127.0.0.1", 9050, "TorChat"); 51 | } 52 | 53 | @Override 54 | public void onConnect() { 55 | // TODO Auto-generated method stub 56 | } 57 | 58 | @Override 59 | public void onDisconnect(Exception e) { 60 | System.out.println("Connection.onDisconnect: " + e.toString()); 61 | // TODO Auto-generated method stub 62 | } 63 | 64 | @Override 65 | public void onReceive(ByteBuffer bufReceived){ 66 | 67 | // bufTotal = existing data + new data 68 | int lenReceived = bufReceived.limit(); 69 | int lenIncomplete = 0; 70 | lenIncomplete = bufIncomplete.length; 71 | int lenTotal = lenIncomplete + lenReceived; 72 | byte[] bufTotal = new byte[lenTotal]; 73 | System.arraycopy(bufIncomplete, 0, bufTotal, 0, lenIncomplete); 74 | System.arraycopy(bufReceived.array(), 0, bufTotal, lenIncomplete, lenReceived); 75 | 76 | // split bufTotal at 0x0a and call onCompleteMesssage() with every chunk 77 | int posMsgStart = 0; 78 | int posDelimiter = 0; 79 | while (posDelimiter < lenTotal){ 80 | if (bufTotal[posDelimiter] == 0x0a){ 81 | int lenMsg = posDelimiter - posMsgStart; 82 | if (lenMsg > 0){ 83 | byte[] msg = new byte[lenMsg]; 84 | System.arraycopy(bufTotal, posMsgStart, msg, 0, lenMsg); 85 | onCompleteMessage(msg); 86 | } 87 | posMsgStart = posDelimiter + 1; 88 | posDelimiter = posMsgStart - 1; 89 | } 90 | posDelimiter++; 91 | } 92 | 93 | // copy remaining (incomplete) last message into bufIncomplete. 94 | int lenRemain = lenTotal - posMsgStart; 95 | if (lenRemain > 0){ 96 | bufIncomplete = new byte[lenRemain]; 97 | System.arraycopy(bufTotal, posMsgStart, bufIncomplete, 0, lenRemain); 98 | }else{ 99 | if (bufIncomplete.length > 0){ 100 | bufIncomplete = new byte[0]; 101 | } 102 | } 103 | } 104 | 105 | /** 106 | * This will be called for every complete message. It will try to 107 | * instantiate the appropriate message class for this type of message, 108 | * parse and enqueue it for processing. Unknown commands will result in a 109 | * MsgUnknown message to be enqueued, malformed messages (empty or unable 110 | * to parse) are a protocol violation and it will close the connection. 111 | * 112 | * @param bytes the raw transfer-encoded message, delimiters already stripped 113 | */ 114 | private void onCompleteMessage(byte[] bytes){ 115 | MessageBuffer buf = new MessageBuffer(bytes); 116 | try { 117 | String command = buf.readCommand(); 118 | Msg msg = getMsgInstanceFromCommand(command); 119 | msg.parse(buf); 120 | msg.execute(); // TODO: should enqueue it for executing in separate thread 121 | } catch (EOFException e) { 122 | // this would be thrown by readCommand() 123 | this.tcp.close("peer has sent empty message"); 124 | } catch (XMessageParseException e) { 125 | // this would be thrown by parse() 126 | this.tcp.close("peer has sent malformed message: " + e.getMessage()); 127 | } catch (Exception e) { 128 | // This would be thrown by getMsgInstanceFromCommand() 129 | // this should never happen and would be a bug in TorChat itself. 130 | System.err.println("Houston, we have a problem!"); 131 | e.printStackTrace(); 132 | this.tcp.close("internal protocol error"); 133 | } 134 | } 135 | 136 | /** 137 | * Instantiate and return the correct message for this command. 138 | * If the command can not be found then instantiate MsgUnknown. 139 | * 140 | * @param command String containing the command 141 | * @return an instance of the appropriate message class 142 | * @throws Exception missing or wrong constructor or illegal arguments 143 | * when calling constructor or other things that indicate wrongly 144 | * implemented message classes. This would be a bug in TorChat 145 | * itself or one of the Msg_xxxx classes and is not supposed to happen. 146 | */ 147 | private Msg getMsgInstanceFromCommand(String command) throws Exception{ 148 | try { 149 | String packageName = this.getClass().getPackage().getName(); 150 | Class C = Class.forName(packageName + ".Msg_" + command); 151 | return (Msg) C.getConstructor(Connection.class).newInstance(this); 152 | } catch (ClassNotFoundException e) { 153 | // this is normal, it happens for unknown incoming commands, in this 154 | // case we use the null-message which will just send the reply 155 | // "not_implemented" and otherwise does nothing. 156 | return new MsgUnknown(this); 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /TorChat/src/prof7bit/torchat/core/MessageBuffer.java: -------------------------------------------------------------------------------- 1 | package prof7bit.torchat.core; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.EOFException; 5 | import java.io.UnsupportedEncodingException; 6 | import java.nio.ByteBuffer; 7 | 8 | /** 9 | * This class is used when parsing a raw incoming message or when serializing 10 | * an outgoing message. It wraps a byte array for the serialized message data 11 | * and has methods to read and write parts of it and for handling TorChat's 12 | * transfer-encoding format. 13 | * 14 | * @author Bernd Kreuss 15 | * 16 | */ 17 | public class MessageBuffer extends ByteArrayOutputStream{ 18 | 19 | private int posRead = 0; 20 | 21 | /** 22 | * Constructor used when creating a new message for sending 23 | */ 24 | public MessageBuffer(){ 25 | super(256); // don't worry, it can grow automatically if needed 26 | } 27 | 28 | /** 29 | * Constructor used when creating a message from raw incoming data. The 30 | * binary decoding of the content will be performed by this constructor. 31 | * After this the message contents can be read. 32 | * 33 | * @param buf byte[] with exactly(!) one transfer-encoded message. 34 | */ 35 | public MessageBuffer(byte[] buf){ 36 | super(buf.length); 37 | reset(); 38 | decodeFromReceived(buf); 39 | resetReadPos(); 40 | } 41 | 42 | /** 43 | * Write a string to the buffer. If this is not the first write then write 44 | * an additional leading space (0x20) before actually writing the data. 45 | * Line breaks will be normalized to Unix line breaks (0x0a) and unicode 46 | * characters will be encoded UTF-8. 47 | * 48 | * @param s String may be unicode and may contain line breaks 49 | */ 50 | public void writeString(String s){ 51 | byte[] b = encodeString(s); 52 | writeBytes(b); 53 | } 54 | 55 | /** 56 | * Write string representation of decimal integer. If this is not the 57 | * first write then write an additional leading space (0x20) before 58 | * actually writing the data. 59 | * 60 | * @param n integer number to be written 61 | */ 62 | public void writeDecimal(int n){ 63 | writeString(String.format("%d", n)); 64 | } 65 | 66 | /** 67 | * Write binary data. If this is not the first write then write an 68 | * additional leading space (0x20) before actually writing the data. 69 | * The data is written exactly as is, no transformations take place. 70 | * 71 | * @param b byte[] containing the data to write 72 | */ 73 | public void writeBytes(byte[] b){ 74 | if (count > 0){ 75 | write(0x20); 76 | } 77 | write(b, 0, b.length); 78 | } 79 | 80 | /** 81 | * Read from current position until the next space (0x20) and return 82 | * a byte array. current position will be advanced to the position 83 | * after the space. The space is not included in the returned array. 84 | * It can also read empty strings (when 2 or more consecutive spaces 85 | * appear then it will read an empty string and advance the position 86 | * by 1, quasi reading the empty strings "between" the spaces). 87 | * If there is no more space until the end it will read until the end. 88 | * If position is at the end (nothing more to read) it will throw EOFException. 89 | * 90 | * @return newly allocated byte[] containing the read bytes 91 | * @throws EOFException if no more bytes to read 92 | */ 93 | public byte[] readBytes() throws EOFException{ 94 | int posDelimiter = posRead; 95 | if (posDelimiter >= count){ 96 | throw new EOFException("no more bytes to read"); 97 | } 98 | while (posDelimiter < count){ 99 | if (this.buf[posDelimiter] == ' '){ 100 | break; 101 | } 102 | posDelimiter++; 103 | } 104 | int lenRead = posDelimiter - posRead; 105 | return readNBytes(lenRead, 1); 106 | } 107 | 108 | 109 | /** 110 | * like readBytes but will always read all remaining bytes until the end 111 | * 112 | * @return newly allocated byte[] containing the read bytes 113 | * @throws EOFException if no more bytes to read 114 | */ 115 | public byte[] readBytesUntilEnd() throws EOFException{ 116 | return readNBytes(count - posRead, 1); 117 | } 118 | 119 | private byte[] readNBytes(int lenRead, int skip) throws EOFException{ 120 | if ((lenRead < 0) | (lenRead + posRead > count)){ 121 | throw new EOFException("no more bytes to read"); 122 | } 123 | byte[] result = new byte[lenRead]; 124 | System.arraycopy(buf, posRead, result, 0, lenRead); 125 | posRead += lenRead + skip; 126 | return result; 127 | } 128 | 129 | /** 130 | * Read Sting until the next space (0x20) and advance the position to the 131 | * place after that space. This method behaves exactly like readBytes() but 132 | * it will convert the read bytes into a String. The conversion to String 133 | * means it will assume UTF-8 encoding and try to decode it to unicode, 134 | * it will also trim() the string and normalize all line endings to 0x0a. 135 | * If position is at the end (nothing more to read) it will throw EOFException. 136 | * 137 | * @return String with read bytes. 138 | * @throws EOFException if nothing more to read 139 | */ 140 | public String readString() throws EOFException{ 141 | return decodeString(readBytes()); 142 | } 143 | 144 | /** 145 | * Read the command of the message or throw EOF if it is empty. 146 | * This will also reset the read position and after it has returned the 147 | * position will be right at the beginning of the message data. 148 | * 149 | * @return String containing the command 150 | * @throws EOFException if the command or the entire message is empty 151 | */ 152 | public String readCommand() throws EOFException{ 153 | resetReadPos(); 154 | String c = readString(); 155 | if (c.length() == 0){ 156 | throw new EOFException(); 157 | } 158 | return c; 159 | } 160 | 161 | /** 162 | * reset the read position to the beginning of the buffer. 163 | */ 164 | public void resetReadPos(){ 165 | posRead = 0; 166 | } 167 | 168 | /** 169 | * Apply the TorChat binary encoding to the message and append the message 170 | * delimiter 0x0a. The returned ByteBuffer can be used for writing to a 171 | * java.nio.Channel without any further processing. 172 | * 173 | * @return ByteBufer with the encoded message, ready for sending 174 | */ 175 | public ByteBuffer encodeForSending(){ 176 | // replace every \ with \/ 177 | // replace every 0x0a with \n 178 | // 179 | // the 10% increase is a conservative estimate, statistically it will 180 | // grow by less than 0.5%. Should it really ever happen to need more 181 | // then the ByteArrayOutputStream has the ability to grow dynamically. 182 | ByteArrayOutputStream b1 = new ByteArrayOutputStream((int)(this.count * 1.1)); 183 | for (int i=0; i 8 | * 9 | */ 10 | public class MsgUnknown extends Msg { 11 | 12 | public MsgUnknown(Connection connection) { 13 | super(connection); 14 | } 15 | 16 | @Override 17 | public void parse(MessageBuffer buf) throws XMessageParseException { 18 | // TODO Auto-generated method stub 19 | 20 | } 21 | 22 | @Override 23 | public MessageBuffer serialize() { 24 | MessageBuffer mb = new MessageBuffer(); 25 | // TODO Auto-generated method stub 26 | return mb; 27 | } 28 | 29 | @Override 30 | public void execute() { 31 | System.out.println("MsgUnknown.execute()"); 32 | // TODO Auto-generated method stub 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /TorChat/src/prof7bit/torchat/core/Msg_not_implemented.java: -------------------------------------------------------------------------------- 1 | package prof7bit.torchat.core; 2 | 3 | /** 4 | * This class handles the protocol message "not_implemented". 5 | * 6 | * @author Bernd Kreuss 7 | * 8 | */ 9 | public class Msg_not_implemented extends Msg { 10 | 11 | public Msg_not_implemented(Connection connection) { 12 | super(connection); 13 | } 14 | 15 | @Override 16 | public void parse(MessageBuffer buf) throws XMessageParseException { 17 | // TODO Auto-generated method stub 18 | 19 | } 20 | 21 | @Override 22 | public MessageBuffer serialize() { 23 | MessageBuffer mb = new MessageBuffer(); 24 | // TODO Auto-generated method stub 25 | return mb; 26 | } 27 | 28 | @Override 29 | public void execute() { 30 | System.out.println("Msg_not_implemented.execute()"); 31 | // don't do anything, just eat and ignore. 32 | // TODO maybe should log it 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /TorChat/src/prof7bit/torchat/core/Msg_ping.java: -------------------------------------------------------------------------------- 1 | package prof7bit.torchat.core; 2 | 3 | /** 4 | * This class handles the protocol message "ping". 5 | * 6 | * @author Bernd Kreuss 7 | * 8 | */ 9 | public class Msg_ping extends Msg { 10 | 11 | public Msg_ping(Connection connection) { 12 | super(connection); 13 | } 14 | 15 | @Override 16 | public void parse(MessageBuffer buf) throws XMessageParseException { 17 | // TODO Auto-generated method stub 18 | 19 | } 20 | 21 | @Override 22 | public MessageBuffer serialize() { 23 | MessageBuffer mb = new MessageBuffer(); 24 | // TODO Auto-generated method stub 25 | return mb; 26 | } 27 | 28 | @Override 29 | public void execute() { 30 | System.out.println("Msg_ping.execute()"); 31 | // TODO Auto-generated method stub 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /TorChat/src/prof7bit/torchat/core/Msg_pong.java: -------------------------------------------------------------------------------- 1 | package prof7bit.torchat.core; 2 | 3 | /** 4 | * This class handles the protocol message "pong". 5 | * 6 | * @author Bernd Kreuss 7 | * 8 | */ 9 | public class Msg_pong extends Msg { 10 | 11 | public Msg_pong(Connection connection) { 12 | super(connection); 13 | } 14 | 15 | @Override 16 | public void parse(MessageBuffer buf) throws XMessageParseException { 17 | // TODO Auto-generated method stub 18 | } 19 | 20 | @Override 21 | public MessageBuffer serialize() { 22 | MessageBuffer mb = new MessageBuffer(); 23 | // TODO Auto-generated method stub 24 | return mb; 25 | } 26 | 27 | @Override 28 | public void execute() { 29 | System.out.println("Msg_pong.execute()"); 30 | // TODO Auto-generated method stub 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /TorChat/src/prof7bit/torchat/core/XMessageParseException.java: -------------------------------------------------------------------------------- 1 | package prof7bit.torchat.core; 2 | 3 | public class XMessageParseException extends XTorChatException { 4 | 5 | private static final long serialVersionUID = 1L; 6 | 7 | public XMessageParseException(String detailMessage) { 8 | super(detailMessage); 9 | // TODO Auto-generated constructor stub 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /TorChat/src/prof7bit/torchat/core/XTorChatException.java: -------------------------------------------------------------------------------- 1 | package prof7bit.torchat.core; 2 | 3 | public class XTorChatException extends Exception { 4 | 5 | private static final long serialVersionUID = 1L; 6 | 7 | public XTorChatException(String detailMessage) { 8 | super(detailMessage); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /TorChatAndroid/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /TorChatAndroid/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | TorChatAndroid 4 | 5 | 6 | ActionBarSherlock 7 | 8 | 9 | 10 | com.android.ide.eclipse.adt.ResourceManagerBuilder 11 | 12 | 13 | 14 | 15 | com.android.ide.eclipse.adt.PreCompilerBuilder 16 | 17 | 18 | 19 | 20 | org.eclipse.jdt.core.javabuilder 21 | 22 | 23 | 24 | 25 | com.android.ide.eclipse.adt.ApkBuilder 26 | 27 | 28 | 29 | 30 | 31 | com.android.ide.eclipse.adt.AndroidNature 32 | org.eclipse.jdt.core.javanature 33 | 34 | 35 | -------------------------------------------------------------------------------- /TorChatAndroid/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 10 | 11 | 12 | 13 | 18 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /TorChatAndroid/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prof7bit/TorChat-Android/1d183f4366bbc28b08d91ddfea66c7c4160ab8e4/TorChatAndroid/ic_launcher-web.png -------------------------------------------------------------------------------- /TorChatAndroid/lint.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /TorChatAndroid/project.properties: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by Android Tools. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file must be checked in Version Control Systems. 5 | # 6 | # To customize properties used by the Ant build system edit 7 | # "ant.properties", and override values to adapt the script to your 8 | # project structure. 9 | # 10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): 11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt 12 | 13 | # Project target. 14 | target=android-17 15 | android.library.reference.1=../../ActionBarSherlock-4.2.0/library 16 | -------------------------------------------------------------------------------- /TorChatAndroid/res/drawable-hdpi-v11/ic_stat_service_running.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prof7bit/TorChat-Android/1d183f4366bbc28b08d91ddfea66c7c4160ab8e4/TorChatAndroid/res/drawable-hdpi-v11/ic_stat_service_running.png -------------------------------------------------------------------------------- /TorChatAndroid/res/drawable-hdpi-v9/ic_stat_service_running.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prof7bit/TorChat-Android/1d183f4366bbc28b08d91ddfea66c7c4160ab8e4/TorChatAndroid/res/drawable-hdpi-v9/ic_stat_service_running.png -------------------------------------------------------------------------------- /TorChatAndroid/res/drawable-hdpi/ic_action_quit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prof7bit/TorChat-Android/1d183f4366bbc28b08d91ddfea66c7c4160ab8e4/TorChatAndroid/res/drawable-hdpi/ic_action_quit.png -------------------------------------------------------------------------------- /TorChatAndroid/res/drawable-hdpi/ic_action_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prof7bit/TorChat-Android/1d183f4366bbc28b08d91ddfea66c7c4160ab8e4/TorChatAndroid/res/drawable-hdpi/ic_action_settings.png -------------------------------------------------------------------------------- /TorChatAndroid/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prof7bit/TorChat-Android/1d183f4366bbc28b08d91ddfea66c7c4160ab8e4/TorChatAndroid/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /TorChatAndroid/res/drawable-hdpi/ic_stat_service_running.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prof7bit/TorChat-Android/1d183f4366bbc28b08d91ddfea66c7c4160ab8e4/TorChatAndroid/res/drawable-hdpi/ic_stat_service_running.png -------------------------------------------------------------------------------- /TorChatAndroid/res/drawable-ldpi-v11/ic_stat_service_running.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prof7bit/TorChat-Android/1d183f4366bbc28b08d91ddfea66c7c4160ab8e4/TorChatAndroid/res/drawable-ldpi-v11/ic_stat_service_running.png -------------------------------------------------------------------------------- /TorChatAndroid/res/drawable-ldpi-v9/ic_stat_service_running.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prof7bit/TorChat-Android/1d183f4366bbc28b08d91ddfea66c7c4160ab8e4/TorChatAndroid/res/drawable-ldpi-v9/ic_stat_service_running.png -------------------------------------------------------------------------------- /TorChatAndroid/res/drawable-ldpi/ic_action_quit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prof7bit/TorChat-Android/1d183f4366bbc28b08d91ddfea66c7c4160ab8e4/TorChatAndroid/res/drawable-ldpi/ic_action_quit.png -------------------------------------------------------------------------------- /TorChatAndroid/res/drawable-ldpi/ic_action_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prof7bit/TorChat-Android/1d183f4366bbc28b08d91ddfea66c7c4160ab8e4/TorChatAndroid/res/drawable-ldpi/ic_action_settings.png -------------------------------------------------------------------------------- /TorChatAndroid/res/drawable-ldpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prof7bit/TorChat-Android/1d183f4366bbc28b08d91ddfea66c7c4160ab8e4/TorChatAndroid/res/drawable-ldpi/ic_launcher.png -------------------------------------------------------------------------------- /TorChatAndroid/res/drawable-ldpi/ic_stat_service_running.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prof7bit/TorChat-Android/1d183f4366bbc28b08d91ddfea66c7c4160ab8e4/TorChatAndroid/res/drawable-ldpi/ic_stat_service_running.png -------------------------------------------------------------------------------- /TorChatAndroid/res/drawable-mdpi-v11/ic_stat_service_running.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prof7bit/TorChat-Android/1d183f4366bbc28b08d91ddfea66c7c4160ab8e4/TorChatAndroid/res/drawable-mdpi-v11/ic_stat_service_running.png -------------------------------------------------------------------------------- /TorChatAndroid/res/drawable-mdpi-v9/ic_stat_service_running.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prof7bit/TorChat-Android/1d183f4366bbc28b08d91ddfea66c7c4160ab8e4/TorChatAndroid/res/drawable-mdpi-v9/ic_stat_service_running.png -------------------------------------------------------------------------------- /TorChatAndroid/res/drawable-mdpi/ic_action_quit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prof7bit/TorChat-Android/1d183f4366bbc28b08d91ddfea66c7c4160ab8e4/TorChatAndroid/res/drawable-mdpi/ic_action_quit.png -------------------------------------------------------------------------------- /TorChatAndroid/res/drawable-mdpi/ic_action_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prof7bit/TorChat-Android/1d183f4366bbc28b08d91ddfea66c7c4160ab8e4/TorChatAndroid/res/drawable-mdpi/ic_action_settings.png -------------------------------------------------------------------------------- /TorChatAndroid/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prof7bit/TorChat-Android/1d183f4366bbc28b08d91ddfea66c7c4160ab8e4/TorChatAndroid/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /TorChatAndroid/res/drawable-mdpi/ic_stat_service_running.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prof7bit/TorChat-Android/1d183f4366bbc28b08d91ddfea66c7c4160ab8e4/TorChatAndroid/res/drawable-mdpi/ic_stat_service_running.png -------------------------------------------------------------------------------- /TorChatAndroid/res/drawable-xhdpi-v11/ic_stat_service_running.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prof7bit/TorChat-Android/1d183f4366bbc28b08d91ddfea66c7c4160ab8e4/TorChatAndroid/res/drawable-xhdpi-v11/ic_stat_service_running.png -------------------------------------------------------------------------------- /TorChatAndroid/res/drawable-xhdpi-v9/ic_stat_service_running.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prof7bit/TorChat-Android/1d183f4366bbc28b08d91ddfea66c7c4160ab8e4/TorChatAndroid/res/drawable-xhdpi-v9/ic_stat_service_running.png -------------------------------------------------------------------------------- /TorChatAndroid/res/drawable-xhdpi/ic_action_quit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prof7bit/TorChat-Android/1d183f4366bbc28b08d91ddfea66c7c4160ab8e4/TorChatAndroid/res/drawable-xhdpi/ic_action_quit.png -------------------------------------------------------------------------------- /TorChatAndroid/res/drawable-xhdpi/ic_action_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prof7bit/TorChat-Android/1d183f4366bbc28b08d91ddfea66c7c4160ab8e4/TorChatAndroid/res/drawable-xhdpi/ic_action_settings.png -------------------------------------------------------------------------------- /TorChatAndroid/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prof7bit/TorChat-Android/1d183f4366bbc28b08d91ddfea66c7c4160ab8e4/TorChatAndroid/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /TorChatAndroid/res/drawable-xhdpi/ic_stat_service_running.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prof7bit/TorChat-Android/1d183f4366bbc28b08d91ddfea66c7c4160ab8e4/TorChatAndroid/res/drawable-xhdpi/ic_stat_service_running.png -------------------------------------------------------------------------------- /TorChatAndroid/res/layout/l_roster.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | -------------------------------------------------------------------------------- /TorChatAndroid/res/menu/m_roster.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 13 | -------------------------------------------------------------------------------- /TorChatAndroid/res/values-v11/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /TorChatAndroid/res/values-v14/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /TorChatAndroid/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | TorChat 5 | Settings 6 | TorChat 7 | Background service is active 8 | Quit 9 | TorChat service stopped 10 | 11 | -------------------------------------------------------------------------------- /TorChatAndroid/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 14 | 15 | 16 | 19 | 20 | -------------------------------------------------------------------------------- /TorChatAndroid/src/prof7bit/torchat/android/gui/TorChat.java: -------------------------------------------------------------------------------- 1 | package prof7bit.torchat.android.gui; 2 | 3 | import prof7bit.torchat.android.R; 4 | import prof7bit.torchat.android.service.Backend; 5 | import prof7bit.torchat.android.service.PrintlnRedirect; 6 | import android.content.Intent; 7 | import android.os.Bundle; 8 | 9 | import com.actionbarsherlock.app.SherlockActivity; 10 | import com.actionbarsherlock.view.Menu; 11 | import com.actionbarsherlock.view.MenuInflater; 12 | import com.actionbarsherlock.view.MenuItem; 13 | 14 | /** 15 | * Main activity that is visible on start of application. 16 | * It mainly shows the roster (buddy list) and has an options 17 | * menu where all global settings can be set. It will start the 18 | * service if it is not already running and it is also the only 19 | * place from where the service can be stopped. 20 | * 21 | * @author Bernd Kreuss 22 | */ 23 | public class TorChat extends SherlockActivity { 24 | 25 | @Override 26 | protected void onCreate(Bundle savedInstanceState) { 27 | super.onCreate(savedInstanceState); 28 | PrintlnRedirect.Install("TorChat"); 29 | System.out.println("onCreate"); 30 | setContentView(R.layout.l_roster); 31 | doStartService(); 32 | doRegisterEventListeners(); 33 | } 34 | 35 | @Override 36 | public void onSaveInstanceState(Bundle savedInstanceState) { 37 | super.onSaveInstanceState(savedInstanceState); 38 | System.out.println("onSaveInstanceState"); 39 | } 40 | 41 | @Override 42 | public boolean onCreateOptionsMenu(Menu menu) { 43 | MenuInflater inflater = getSupportMenuInflater(); 44 | inflater.inflate(R.menu.m_roster, menu); 45 | return true; 46 | } 47 | 48 | private void doRegisterEventListeners(){ 49 | // findViewById(R.id.menu_quit).setOnClickListener(new OnClickListener() { 50 | // public void onClick(View v) { 51 | // doQuit(); 52 | // } 53 | // }); 54 | } 55 | 56 | @Override 57 | public boolean onOptionsItemSelected(MenuItem item) { 58 | switch (item.getItemId()) { 59 | case R.id.menu_quit: 60 | doQuit(); 61 | return true; 62 | case R.id.menu_settings: 63 | doShowSettings(); 64 | return true; 65 | default: 66 | return super.onOptionsItemSelected(item); 67 | } 68 | } 69 | 70 | /** 71 | * Start the Background service. 72 | */ 73 | private void doStartService(){ 74 | System.out.println("doStartService"); 75 | startService(new Intent(this, Backend.class)); 76 | } 77 | 78 | /** 79 | * Stop the Background service. 80 | */ 81 | private void doStopService(){ 82 | System.out.println("doStopService"); 83 | stopService(new Intent(this, Backend.class)); 84 | } 85 | 86 | /** 87 | * Quit entirely (stop service and close the main activity). 88 | */ 89 | private void doQuit(){ 90 | System.out.println("doQuit"); 91 | doStopService(); 92 | finish(); 93 | } 94 | 95 | /** 96 | * Show the setting dialog 97 | */ 98 | private void doShowSettings(){ 99 | System.out.println("doShowSettings"); 100 | // nothing yet 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /TorChatAndroid/src/prof7bit/torchat/android/service/Backend.java: -------------------------------------------------------------------------------- 1 | package prof7bit.torchat.android.service; 2 | import java.io.IOException; 3 | 4 | import prof7bit.torchat.android.R; 5 | import prof7bit.torchat.android.gui.TorChat; 6 | import prof7bit.torchat.core.Client; 7 | import prof7bit.torchat.core.ClientHandler; 8 | import android.app.Notification; 9 | import android.app.NotificationManager; 10 | import android.app.PendingIntent; 11 | import android.app.Service; 12 | import android.content.Intent; 13 | import android.os.IBinder; 14 | import android.widget.Toast; 15 | 16 | 17 | public class Backend extends Service implements ClientHandler { 18 | 19 | private NotificationManager nMgr; 20 | private int NOTIFICATION = 10429; //Any unique number for this notification 21 | 22 | private Client client; 23 | 24 | @SuppressWarnings("deprecation") 25 | private void showNotification() { 26 | CharSequence title = getText(R.string.service_running); 27 | CharSequence title2 = getText(R.string.service_running_detail); 28 | 29 | // Set the icon, scrolling text and timestamp 30 | Notification notification = new Notification(R.drawable.ic_stat_service_running, title, System.currentTimeMillis()); 31 | notification.flags = Notification.FLAG_ONGOING_EVENT; 32 | 33 | // The PendingIntent to launch our activity if the user selects this notification 34 | Intent toLaunch = new Intent(this, TorChat.class); 35 | toLaunch.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 36 | toLaunch.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); 37 | PendingIntent contentIntent = PendingIntent.getActivity(this, 0, toLaunch , 0); 38 | 39 | // Set the info for the views that show in the notification panel. 40 | notification.setLatestEventInfo(this, title, title2, contentIntent); 41 | 42 | // Send the notification. 43 | nMgr.notify(NOTIFICATION, notification); 44 | } 45 | 46 | private void removeNotification(){ 47 | nMgr.cancel(NOTIFICATION); 48 | } 49 | 50 | @Override 51 | public IBinder onBind(Intent arg0) { 52 | return null; 53 | } 54 | 55 | @Override 56 | public void onCreate() { 57 | nMgr = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); 58 | 59 | PrintlnRedirect.Install("TorChat"); 60 | 61 | try { 62 | client = new Client(this, 11009); 63 | showNotification(); 64 | } catch (IOException e) { 65 | // TODO what to do now? 66 | e.printStackTrace(); 67 | } 68 | } 69 | 70 | @Override 71 | public void onDestroy() { 72 | try { 73 | client.close(); 74 | } catch (InterruptedException e) { 75 | e.printStackTrace(); 76 | } 77 | removeNotification(); 78 | Toast.makeText(this, R.string.toast_service_stopped, Toast.LENGTH_LONG).show(); 79 | } 80 | 81 | @Override 82 | public void onStart(Intent intent, int startid) { 83 | // nothing 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /TorChatAndroid/src/prof7bit/torchat/android/service/PrintlnRedirect.java: -------------------------------------------------------------------------------- 1 | package prof7bit.torchat.android.service; 2 | 3 | import java.io.IOException; 4 | import java.io.OutputStream; 5 | import java.io.PrintStream; 6 | 7 | import android.util.Log; 8 | 9 | public class PrintlnRedirect extends OutputStream { 10 | 11 | public enum LogLevel{ 12 | DEBUG, 13 | ERROR 14 | } 15 | 16 | private String buffer = ""; 17 | private String TAG; 18 | private LogLevel level = LogLevel.DEBUG; 19 | 20 | public PrintlnRedirect(String tag, LogLevel lvl){ 21 | TAG = tag; 22 | level = lvl; 23 | } 24 | 25 | /** 26 | * Install redirection of stdout and stderr to android log 27 | * @param tag the text of the tag column in the android log 28 | */ 29 | public static void Install(String tag){ 30 | System.setOut(new PrintStream(new PrintlnRedirect(tag, LogLevel.DEBUG))); 31 | System.setErr(new PrintStream(new PrintlnRedirect(tag, LogLevel.ERROR))); 32 | } 33 | 34 | @Override 35 | public void write(int oneByte) throws IOException { 36 | 37 | if (oneByte == 0x0a){ 38 | switch(level){ 39 | case DEBUG: 40 | Log.d(TAG, buffer); 41 | break; 42 | case ERROR: 43 | Log.e(TAG, buffer); 44 | break; 45 | } 46 | buffer = ""; 47 | }else{ 48 | buffer += (char)oneByte; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /TorChatTests/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /TorChatTests/.gitignore: -------------------------------------------------------------------------------- 1 | /bin 2 | -------------------------------------------------------------------------------- /TorChatTests/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | TorChatTests 4 | 5 | 6 | TorChat 7 | 8 | 9 | 10 | org.eclipse.jdt.core.javabuilder 11 | 12 | 13 | 14 | 15 | 16 | org.eclipse.jdt.core.javanature 17 | 18 | 19 | -------------------------------------------------------------------------------- /TorChatTests/src/prof7bit/reactor/TestTCP.java: -------------------------------------------------------------------------------- 1 | package prof7bit.reactor; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertTrue; 5 | import static org.junit.Assert.fail; 6 | 7 | import java.io.IOException; 8 | import java.nio.ByteBuffer; 9 | import java.util.concurrent.CountDownLatch; 10 | import java.util.concurrent.TimeUnit; 11 | 12 | import org.junit.After; 13 | import org.junit.Before; 14 | import org.junit.Test; 15 | 16 | import prof7bit.reactor.ListenPort; 17 | import prof7bit.reactor.ListenPortHandler; 18 | import prof7bit.reactor.Reactor; 19 | import prof7bit.reactor.TCP; 20 | import prof7bit.reactor.TCPHandler; 21 | import prof7bit.reactor.XConnectionClosedHere; 22 | import prof7bit.reactor.XConnectionClosedRemote; 23 | 24 | public class TestTCP implements ListenPortHandler, TCPHandler { 25 | 26 | private static int TEST_PORT = 3456; 27 | private Reactor reactor; 28 | private ListenPort listener; 29 | private TCP tcpOut; 30 | 31 | private Latch latchAcc; 32 | private Latch latchRcvA; 33 | private Latch latchRcvB; 34 | private Latch latchConA; 35 | private Latch latchConB; 36 | private Latch latchDisA; 37 | private Latch latchDisB; 38 | 39 | private ByteBuffer bufRcvA; 40 | private ByteBuffer bufRcvB; 41 | private Exception exDisA; 42 | private Exception exDisB; 43 | 44 | @Before 45 | public void setUp() throws Exception { 46 | latchAcc = new Latch(1); 47 | latchRcvA = new Latch(1); 48 | latchRcvB = new Latch(1); 49 | latchConA = new Latch(1); 50 | latchConB = new Latch(1); 51 | latchDisA = new Latch(1); 52 | latchDisB = new Latch(1); 53 | 54 | reactor = new Reactor(); 55 | listener = new ListenPort(reactor, this); 56 | listener.listen(TEST_PORT); 57 | } 58 | 59 | @After 60 | public void tearDown() throws Exception { 61 | reactor.close(); 62 | } 63 | 64 | 65 | @Test 66 | public void testConnectEchoDisconnect() { 67 | ByteBuffer bufSnd = ByteBuffer.wrap("test transmission".getBytes()); 68 | try { 69 | tcpOut = new TCP(reactor, "127.0.0.1", TEST_PORT, this); 70 | } catch (IOException e) { 71 | fail("TCP constructor exception"); 72 | } 73 | tcpOut.send(bufSnd); // will be queued until connect happens 74 | 75 | latchRcvA.await(1000); 76 | assertEquals(0, latchAcc.getCount()); 77 | assertEquals(0, latchConA.getCount()); 78 | assertEquals(1, latchConB.getCount()); 79 | assertEquals(0, latchRcvA.getCount()); 80 | assertEquals(0, latchRcvB.getCount()); 81 | assertEquals(1, latchDisA.getCount()); 82 | assertEquals(1, latchDisB.getCount()); 83 | 84 | bufSnd.position(0); 85 | bufRcvA.position(0); 86 | bufRcvB.position(0); 87 | assertTrue(bufSnd.equals(bufRcvB)); 88 | assertTrue(bufSnd.equals(bufRcvA)); 89 | 90 | // now disconnect 91 | tcpOut.close("test reason"); 92 | latchDisA.await(1000); 93 | latchDisB.await(1000); 94 | assertTrue(exDisA instanceof XConnectionClosedHere); 95 | assertTrue(exDisB instanceof XConnectionClosedRemote); 96 | assertEquals("test reason", exDisA.getMessage()); 97 | } 98 | 99 | 100 | 101 | // handler for ListenPort 102 | 103 | @Override 104 | public TCPHandler onAccept(TCP tcp) { 105 | latchAcc.countDown(); 106 | return new IncomingHandler(tcp); 107 | } 108 | 109 | 110 | 111 | // handler for outgoing TCP 112 | 113 | @Override 114 | public void onConnect() { 115 | latchConA.countDown(); 116 | } 117 | 118 | @Override 119 | public void onDisconnect(Exception e) { 120 | exDisA = e; 121 | latchDisA.countDown(); 122 | } 123 | 124 | @Override 125 | public void onReceive(ByteBuffer buf) { 126 | bufRcvA = buf; 127 | latchRcvA.countDown(); 128 | } 129 | 130 | 131 | 132 | // handler for incoming TCP 133 | 134 | private class IncomingHandler implements TCPHandler{ 135 | 136 | private TCP tcp; 137 | 138 | public IncomingHandler(TCP tcp){ 139 | this.tcp = tcp; 140 | } 141 | 142 | @Override 143 | public void onConnect() { 144 | latchConB.countDown(); 145 | } 146 | 147 | @Override 148 | public void onDisconnect(Exception e) { 149 | exDisB = e; 150 | latchDisB.countDown(); 151 | } 152 | 153 | @Override 154 | public void onReceive(ByteBuffer buf) { 155 | bufRcvB = buf; 156 | tcp.send(buf); 157 | latchRcvB.countDown(); 158 | } 159 | } 160 | 161 | 162 | // used for waiting in main thread 163 | 164 | private class Latch extends CountDownLatch{ 165 | 166 | public Latch(int count) { 167 | super(count); 168 | } 169 | 170 | public void await(int timeout){ 171 | try { 172 | if(!await(timeout, TimeUnit.MILLISECONDS)){ 173 | fail("timeout"); 174 | } 175 | } catch (InterruptedException e) { 176 | fail("interrupted while waiting"); 177 | } 178 | } 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /TorChatTests/src/prof7bit/torchat/core/TestMessageBuffer.java: -------------------------------------------------------------------------------- 1 | package prof7bit.torchat.core; 2 | 3 | import java.io.EOFException; 4 | 5 | import junit.framework.TestCase; 6 | 7 | import org.junit.Test; 8 | 9 | import prof7bit.torchat.core.MessageBuffer; 10 | 11 | public class TestMessageBuffer extends TestCase { 12 | 13 | private final byte[] binWithSpace = "foo \\ \n \r\n \r \u0000 bar".getBytes(); 14 | private final byte[] encWithSpace = "foo \\/ \\n \r\\n \r \u0000 bar".getBytes(); 15 | private final byte[] encWithSpaceLF = "foo \\/ \\n \r\\n \r \u0000 bar\n".getBytes(); 16 | 17 | private final String strUnclean = "\r\nfoo\r\nbar\rbaz\nblub\n "; 18 | private final String strTrimmed = "foo\nbar\nbaz\nblub"; 19 | 20 | private MessageBuffer b; 21 | 22 | protected void setUp() throws Exception { 23 | super.setUp(); 24 | b = new MessageBuffer(); 25 | } 26 | 27 | protected void tearDown() throws Exception { 28 | super.tearDown(); 29 | b.close(); 30 | } 31 | 32 | /** 33 | * compare two byte arrays 34 | */ 35 | private boolean eq(byte[] x, byte[] y){ 36 | if (x.length != y.length){ 37 | return false; 38 | } 39 | for (int i=0; i