├── .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 |
--------------------------------------------------------------------------------
/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