]> Pileus Git - ~andy/spades/blob - src/org/pileus/spades/Client.java
Improve client modeing
[~andy/spades] / src / org / pileus / spades / Client.java
1 package org.pileus.spades;
2
3 import java.io.*;
4 import java.net.*;
5
6 public class Client
7 {
8         /* Constnats */
9         enum State {
10                 INIT,
11                 SETUP,
12                 READY,
13                 ABORT,
14         };
15
16         /* Preference data */
17         public  String         server   = "irc.freenode.net";
18         public  int            port     = 6667;
19         public  String         nickname = "SpadeGuest";
20         public  String         channel  = "#rhnoise";
21         public  boolean        usesasl  = false;
22         public  String         authname = "";
23         public  String         password = "";
24         public  String         username = "user";
25         public  String         hostname = "localhost";
26
27         /* Public data */
28         public  State          state    = State.INIT;
29         public  String         name     = "";
30
31         /* Connection data */
32         private Socket         socket;
33         private BufferedReader input;
34         private PrintWriter    output;
35
36         /* Private data */
37         private int            mangle;
38
39         /* Public Methods */
40         public Client()
41         {
42                 Os.debug("Client: create");
43         }
44
45         public void setServer(String server, int port)
46         {
47                 this.server = server;
48                 this.port   = port;
49         }
50
51         public void setAuth(boolean usesasl, String authname, String password)
52         {
53                 this.usesasl  = usesasl;
54                 this.authname = authname;
55                 this.password = password;
56         }
57
58         public void setUser(String nickname, String channel)
59         {
60                 this.nickname = nickname;
61                 this.channel  = channel;
62                 this.name     = nickname;
63         }
64
65         public boolean connect()
66         {
67                 Os.debug("Client: connect");
68
69                 try {
70                         this.state  = State.SETUP;
71                         this.socket = new Socket();
72                         this.socket.connect(new InetSocketAddress(this.server, this.port));
73                         this.input  = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
74                         this.output = new PrintWriter(this.socket.getOutputStream());
75                 } catch (Exception e) {
76                         Os.debug("Client: failed to create connection: " + e);
77                         return false;
78                 }
79
80                 Os.debug("Client: connected");
81                 if (this.usesasl)
82                         this.raw("CAP REQ :sasl");
83                 this.raw("USER "+this.username+" "+this.hostname+" "+this.server+" :"+this.name);
84                 this.raw("NICK "+this.name);
85
86                 return true;
87         }
88
89         public void abort()
90         {
91                 Os.debug("Client: abort");
92                 this.state = State.ABORT;
93                 this.validate();
94         }
95
96         public void reset()
97         {
98                 Os.debug("Client: reset");
99                 this.state = State.INIT;
100         }
101
102         public void raw(String line)
103         {
104                 try {
105                         if (this.validate() != State.SETUP &&
106                             this.validate() != State.READY)
107                                 return;
108                         Os.debug("< " + line);
109                         this.output.println(line);
110                         this.output.flush();
111                 } catch (Exception e) {
112                         Os.debug("Client: error writing line", e);
113                 }
114         }
115
116         public Message send(String txt)
117         {
118                 if (this.validate() != State.READY)
119                         return null;
120                 Message msg = new Message(this.channel, this.name, txt);
121                 if (msg.type == Message.Type.JOIN)
122                         this.channel = msg.msg;
123                 this.raw(msg.line);
124                 return msg;
125         }
126
127         public Message recv()
128         {
129                 try {
130                         if (this.validate() != State.SETUP &&
131                             this.validate() != State.READY)
132                                 return null;
133                         String line = this.input.readLine();
134                         if (line == null)
135                                 return null;
136                         Os.debug("> " + line);
137                         Message msg = new Message(line, this.name);
138                         this.process(msg);
139                         if (this.usesasl)
140                                 this.dosasl(msg);
141                         return msg;
142                 } catch (SocketException e) {
143                         this.state = State.INIT;
144                         return null;
145                 } catch (Exception e) {
146                         this.state = State.INIT;
147                         Os.debug("Client: error in recv", e);
148                         return null;
149                 }
150         }
151
152         /* Private methods */
153         private State validate()
154         {
155                 try {
156                         if (this.state == State.ABORT) {
157                                 if (this.socket != null) {
158                                         this.socket.close();
159                                         this.state = State.INIT;
160                                 }
161                         }
162                 } catch (Exception e) {
163                         Os.debug("Client: error closing socket", e);
164                 }
165                 return this.state;
166         }
167
168         private void process(Message msg)
169         {
170                 if (msg.cmd.equals("001") && msg.msg.matches("Welcome.*")) {
171                         this.raw("JOIN "  + this.channel);
172                         this.raw("TOPIC " + this.channel);
173                         this.state = State.READY;
174                 }
175                 if (msg.cmd.equals("433")) {
176                         this.name   = this.nickname + this.mangle;
177                         this.mangle = this.mangle + 11;
178                         this.raw("NICK "  + this.name);
179                 }
180                 if (msg.cmd.equals("PING")) {
181                         this.raw("PING " + msg.msg);
182                 }
183         }
184
185         private void dosasl(Message msg)
186         {
187                 switch (msg.type) {
188                         case CAP:
189                                 if (msg.msg.equals("sasl") && msg.arg.equals("ACK")) {
190                                         Os.debug("Client: sasl - starting auth");
191                                         this.raw("AUTHENTICATE PLAIN");
192                                 } else {
193                                         Os.debug("Client: sasl - Server does not support sasl");
194                                 }
195                                 break;
196                         case AUTH:
197                                 if (msg.arg.equals("+")) {
198                                         Os.debug("Client: sasl - performin authentication");
199                                         this.raw("AUTHENTICATE " + Os.base64(
200                                                                 this.authname + "\0" +
201                                                                 this.authname + "\0" +
202                                                                 this.password));
203                                 } else {
204                                         Os.debug("Client: sasl - unexpected authenticate response");
205                                 }
206                                 break;
207                         case AUTHOK:
208                                 Os.debug("Client: SASL Auth Successful");
209                                 this.raw("CAP END");
210                                 break;
211                         case AUTHFAIL:
212                                 Os.debug("Client: SASL Auth Failed");
213                                 this.raw("CAP END");
214                                 break;
215                 }
216         }
217 }