aboutsummaryrefslogtreecommitdiff
path: root/src/org/jivesoftware/smack/sasl/SASLFacebookConnect.java
blob: 3126d83dba85c6cfcc18c569f68084e299a53dc1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
package org.jivesoftware.smack.sasl;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Map;

import org.apache.harmony.javax.security.auth.callback.CallbackHandler;
import de.measite.smack.Sasl;

import org.jivesoftware.smack.SASLAuthentication;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.sasl.SASLMechanism;
import org.jivesoftware.smack.util.Base64;

/**
 * This class is originally from http://code.google.com/p/fbgc/source/browse/trunk/daemon/src/main/java/org/albino/mechanisms/FacebookConnectSASLMechanism.java
 * I just adapted to match the SMACK package scheme and 
 */
public class SASLFacebookConnect extends SASLMechanism {

        private String sessionKey = "";
        private String sessionSecret = "";
        private String apiKey = "";
        
        static{
        	SASLAuthentication.registerSASLMechanism("X-FACEBOOK-PLATFORM",
                    SASLFacebookConnect.class);
        	SASLAuthentication.supportSASLMechanism("X-FACEBOOK-PLATFORM", 0);
        }
        
        public SASLFacebookConnect(SASLAuthentication saslAuthentication) {
                super(saslAuthentication);
        }

        // protected void authenticate() throws IOException, XMPPException {
        // String[] mechanisms = { getName() };
        // Map<String, String> props = new HashMap<String, String>();
        // sc = Sasl.createSaslClient(mechanisms, null, "xmpp", hostname, props,
        // this);
        //
        // super.authenticate();
        // }

        protected void authenticate() throws IOException, XMPPException {
                final StringBuilder stanza = new StringBuilder();
                stanza.append("<auth mechanism=\"").append(getName());
                stanza.append("\" xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">");
                stanza.append("</auth>");

                // Send the authentication to the server
                getSASLAuthentication().send(new Packet(){

					@Override
					public String toXML() {
						return stanza.toString();
					}    	
                	
                });
        }

        public void authenticate(String apiKeyAndSessionKey, String host, String sessionSecret)
                        throws IOException, XMPPException {

                if(apiKeyAndSessionKey==null || sessionSecret==null)
                        throw new IllegalStateException("Invalid parameters!");
                
                String[] keyArray = apiKeyAndSessionKey.split("\\|");
                
                if(keyArray==null || keyArray.length != 2)
                        throw new IllegalStateException("Api key or session key is not present!");
                
                this.apiKey = keyArray[0];
                this.sessionKey = keyArray[1];
                this.sessionSecret = sessionSecret;
                
                this.authenticationId = sessionKey;
                this.password = sessionSecret;
                this.hostname = host;

                String[] mechanisms = { "DIGEST-MD5" };
                Map<String, String> props = new HashMap<String, String>();
                sc = Sasl.createSaslClient(mechanisms, null, "xmpp", host, props, this);
                authenticate();
        }

        public void authenticate(String username, String host, CallbackHandler cbh)
                        throws IOException, XMPPException {
                String[] mechanisms = { "DIGEST-MD5" };
                Map<String, String> props = new HashMap<String, String>();
                sc = Sasl.createSaslClient(mechanisms, null, "xmpp", host, props, cbh);
                authenticate();
        }

        protected String getName() {
                return "X-FACEBOOK-PLATFORM";
        }

        public void challengeReceived(String challenge) throws IOException {
                // Build the challenge response stanza encoding the response text
                final StringBuilder stanza = new StringBuilder();

                byte response[] = null;
                if (challenge != null) {
                        String decodedResponse = new String(Base64.decode(challenge));
                        Map<String, String> parameters = getQueryMap(decodedResponse);

                        String version = "1.0";
                        String nonce = parameters.get("nonce");
                        String method = parameters.get("method");
                        
                        Long callId = new GregorianCalendar().getTimeInMillis()/1000;
                        
                        String sig = "api_key="+apiKey
                                                        +"call_id="+callId
                                                        +"method="+method
                                                        +"nonce="+nonce
                                                        +"session_key="+sessionKey
                                                        +"v="+version
                                                        +sessionSecret;
                        
                        try {
                                sig = MD5(sig);
                        } catch (NoSuchAlgorithmException e) {
                                throw new IllegalStateException(e);
                        }
                        
                        String composedResponse = "api_key="+apiKey+"&"
                                                                                +"call_id="+callId+"&"
                                                                                +"method="+method+"&"
                                                                                +"nonce="+nonce+"&"
                                                                                +"session_key="+sessionKey+"&"
                                                                                +"v="+version+"&"
                                                                                +"sig="+sig;
                        
                        response = composedResponse.getBytes();
                }

                String authenticationText="";

                if (response != null) {
                        authenticationText = Base64.encodeBytes(response, Base64.DONT_BREAK_LINES);
                }

                stanza.append("<response xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">");
                stanza.append(authenticationText);
                stanza.append("</response>");

                // Send the authentication to the server
                getSASLAuthentication().send(new Packet(){

					@Override
					public String toXML() {
						return stanza.toString();
					}
                	
                });
        }

        private Map<String, String> getQueryMap(String query) {
                String[] params = query.split("&");
                Map<String, String> map = new HashMap<String, String>();
                for (String param : params) {
                        String name = param.split("=")[0];
                        String value = param.split("=")[1];
                        map.put(name, value);
                }
                return map;
        }
        
    private String convertToHex(byte[] data) {
        StringBuffer buf = new StringBuffer();
        for (int i = 0; i < data.length; i++) {
            int halfbyte = (data[i] >>> 4) & 0x0F;
            int two_halfs = 0;
            do {
                if ((0 <= halfbyte) && (halfbyte <= 9))
                    buf.append((char) ('0' + halfbyte));
                else
                    buf.append((char) ('a' + (halfbyte - 10)));
                halfbyte = data[i] & 0x0F;
            } while(two_halfs++ < 1);
        }
        return buf.toString();
    }
 
    public String MD5(String text) throws NoSuchAlgorithmException, UnsupportedEncodingException  {
        MessageDigest md;
        md = MessageDigest.getInstance("MD5");
        byte[] md5hash = new byte[32];
        md.update(text.getBytes("iso-8859-1"), 0, text.length());
        md5hash = md.digest();
        return convertToHex(md5hash);
    }
}