XmppConnection.java

  1package de.gultsch.chat.xmpp;
  2
  3import java.io.IOException;
  4import java.io.InputStream;
  5import java.io.OutputStream;
  6import java.math.BigInteger;
  7import java.net.Socket;
  8import java.net.UnknownHostException;
  9import java.security.SecureRandom;
 10
 11import javax.net.ssl.SSLSocket;
 12import javax.net.ssl.SSLSocketFactory;
 13
 14import org.xmlpull.v1.XmlPullParserException;
 15
 16import android.os.PowerManager;
 17import android.util.Log;
 18import de.gultsch.chat.entities.Account;
 19import de.gultsch.chat.utils.SASL;
 20import de.gultsch.chat.xml.Element;
 21import de.gultsch.chat.xml.Tag;
 22import de.gultsch.chat.xml.XmlReader;
 23import de.gultsch.chat.xml.TagWriter;
 24
 25public class XmppConnection implements Runnable {
 26
 27	protected Account account;
 28	private static final String LOGTAG = "xmppService";
 29
 30	private PowerManager.WakeLock wakeLock;
 31
 32	private SecureRandom random = new SecureRandom();
 33	
 34	private Socket socket;
 35	private XmlReader tagReader;
 36	private TagWriter tagWriter;
 37
 38	private boolean isTlsEncrypted = false;
 39	private boolean isAuthenticated = false;
 40
 41	public XmppConnection(Account account, PowerManager pm) {
 42		this.account = account;
 43		wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
 44				"XmppConnection");
 45		tagReader = new XmlReader(wakeLock);
 46		tagWriter = new TagWriter();
 47	}
 48
 49	protected void connect() {
 50		try {
 51			socket = new Socket(account.getServer(), 5222);
 52			Log.d(LOGTAG, "starting new socket");
 53			OutputStream out = socket.getOutputStream();
 54			tagWriter.setOutputStream(out);
 55			InputStream in = socket.getInputStream();
 56			tagReader.setInputStream(in);
 57		} catch (UnknownHostException e) {
 58			Log.d(LOGTAG, "error during connect. unknown host");
 59		} catch (IOException e) {
 60			Log.d(LOGTAG, "error during connect. io exception. falscher port?");
 61		}
 62	}
 63
 64	@Override
 65	public void run() {
 66		connect();
 67		try {
 68			tagWriter.beginDocument();
 69			sendStartStream();
 70			Tag nextTag;
 71			while ((nextTag = tagReader.readTag()) != null) {
 72				if (nextTag.isStart("stream")) {
 73					processStream(nextTag);
 74				} else {
 75					Log.d(LOGTAG, "found unexpected tag: " + nextTag.getName());
 76				}
 77			}
 78		} catch (XmlPullParserException e) {
 79			Log.d(LOGTAG,
 80					"xml error during normal read. maybe missformed xml? "
 81							+ e.getMessage());
 82		} catch (IOException e) {
 83			Log.d(LOGTAG, "io exception during read. connection lost?");
 84		}
 85	}
 86
 87	private void processStream(Tag currentTag) throws XmlPullParserException,
 88			IOException {
 89		Log.d(LOGTAG, "process Stream");
 90		Tag nextTag;
 91		while ((nextTag = tagReader.readTag()) != null) {
 92			if (nextTag.isStart("error")) {
 93				processStreamError(nextTag);
 94			} else if (nextTag.isStart("features")) {
 95				processStreamFeatures(nextTag);
 96				if (!isTlsEncrypted) {
 97					sendStartTLS();
 98				}
 99				if ((!isAuthenticated) && (isTlsEncrypted)) {
100					sendSaslAuth();
101				}
102				if ((isAuthenticated)&&(isTlsEncrypted)) {
103					sendBindRequest();
104				}
105			} else if (nextTag.isStart("proceed")) {
106				switchOverToTls(nextTag);
107			} else if (nextTag.isStart("success")) {
108				isAuthenticated = true;
109				Log.d(LOGTAG,"read success tag in stream. reset again");
110				tagReader.readTag();
111				tagReader.reset();
112				sendStartStream();
113				processStream(tagReader.readTag());
114			} else if (nextTag.isStart("iq")) {
115				processIq(nextTag);
116			} else if (nextTag.isEnd("stream")) {
117				break;
118			} else {
119				Log.d(LOGTAG, "found unexpected tag: " + nextTag.getName()
120						+ " as child of " + currentTag.getName());
121			}
122		}
123	}
124
125	private void processIq(Tag currentTag) throws XmlPullParserException, IOException {
126		int typ = -1;
127		if (currentTag.getAttribute("type").equals("result")) {
128			typ = IqPacket.TYPE_RESULT;
129		}
130		IqPacket iq = new IqPacket(currentTag.getAttribute("id"),typ);
131		Tag nextTag = tagReader.readTag();
132		while(!nextTag.isEnd("iq")) {
133			Element element = tagReader.readElement(nextTag);
134			iq.addChild(element);
135			nextTag = tagReader.readTag();
136		}
137		Log.d(LOGTAG,"this is what i understood: "+iq.toString());
138	}
139
140	private void sendStartTLS() throws XmlPullParserException, IOException {
141		Tag startTLS = Tag.empty("starttls");
142		startTLS.setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-tls");
143		Log.d(LOGTAG, "sending starttls");
144		tagWriter.writeTag(startTLS).flush();
145	}
146
147	private void switchOverToTls(Tag currentTag) throws XmlPullParserException,
148			IOException {
149		Tag nextTag = tagReader.readTag(); // should be proceed end tag
150		Log.d(LOGTAG, "now switch to ssl");
151		SSLSocket sslSocket;
152		try {
153			sslSocket = (SSLSocket) ((SSLSocketFactory) SSLSocketFactory
154					.getDefault()).createSocket(socket, socket.getInetAddress()
155					.getHostAddress(), socket.getPort(), true);
156			tagReader.setInputStream(sslSocket.getInputStream());
157			Log.d(LOGTAG, "reset inputstream");
158			tagWriter.setOutputStream(sslSocket.getOutputStream());
159			Log.d(LOGTAG, "switch over seemed to work");
160			isTlsEncrypted = true;
161			sendStartStream();
162			processStream(tagReader.readTag());
163		} catch (IOException e) {
164			Log.d(LOGTAG, "error on ssl" + e.getMessage());
165		}
166	}
167
168	private void sendSaslAuth() throws IOException, XmlPullParserException {
169		String saslString = SASL.plain(account.getUsername(),
170				account.getPassword());
171		Element auth = new Element("auth");
172		auth.setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-sasl");
173		auth.setAttribute("mechanism", "PLAIN");
174		auth.setContent(saslString);
175		Log.d(LOGTAG,"sending sasl "+auth.toString());
176		tagWriter.writeElement(auth);
177		tagWriter.flush();
178	}
179
180	private void processStreamFeatures(Tag currentTag)
181			throws XmlPullParserException, IOException {
182		Log.d(LOGTAG, "processStreamFeatures");
183		
184		Element streamFeatures = new Element("features");
185		
186		Tag nextTag = tagReader.readTag();
187		while(!nextTag.isEnd("features")) {
188			Element element = tagReader.readElement(nextTag);
189			streamFeatures.addChild(element);
190			nextTag = tagReader.readTag();
191		}	
192	}
193
194	private void sendBindRequest() throws IOException {
195		IqPacket iq = new IqPacket(nextRandomId(),IqPacket.TYPE_SET);
196		Element bind = new Element("bind");
197		bind.setAttribute("xmlns","urn:ietf:params:xml:ns:xmpp-bind");
198		iq.addChild(bind);
199		Log.d(LOGTAG,"sending bind request: "+iq.toString());
200		tagWriter.writeElement(iq);
201		tagWriter.flush();
202	}
203
204	private void processStreamError(Tag currentTag) {
205		Log.d(LOGTAG, "processStreamError");
206	}
207
208	private void sendStartStream() throws IOException {
209		Tag stream = Tag.start("stream");
210		stream.setAttribute("from", account.getJid());
211		stream.setAttribute("to", account.getServer());
212		stream.setAttribute("version", "1.0");
213		stream.setAttribute("xml:lang", "en");
214		stream.setAttribute("xmlns", "jabber:client");
215		stream.setAttribute("xmlns:stream", "http://etherx.jabber.org/streams");
216		tagWriter.writeTag(stream).flush();
217	}
218
219	private String nextRandomId() {
220		return new BigInteger(50, random).toString(32);
221	}
222}