Java tutorial
package io.cslinmiso.line.api.impl; /** * * @Package: io.cslinmiso.line.api.impl * @FileName: LineApiImpl.java * @author: treylin * @date: 2016/03/25, ? 10:46:25 * * <pre> * The MIT License (MIT) * * Copyright (c) 2015 Trey Lin * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * </pre> */ import io.cslinmiso.line.api.LineApi; import io.cslinmiso.line.utils.Utility; import java.io.InputStream; import java.math.BigInteger; import java.security.KeyFactory; import java.security.interfaces.RSAPublicKey; import java.security.spec.RSAPublicKeySpec; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import javax.crypto.Cipher; import line.thrift.AuthQrcode; import line.thrift.Contact; import line.thrift.Group; import line.thrift.IdentityProvider; import line.thrift.LoginResult; import line.thrift.LoginResultType; import line.thrift.Message; import line.thrift.Operation; import line.thrift.Profile; import line.thrift.Room; import line.thrift.TMessageBoxWrapUp; import line.thrift.TMessageBoxWrapUpResponse; import line.thrift.TalkException; import line.thrift.TalkService; import line.thrift.TalkService.Client; import org.apache.commons.codec.binary.Hex; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.thrift.TException; import org.apache.thrift.protocol.TCompactProtocol; import org.apache.thrift.protocol.TProtocol; import org.apache.thrift.transport.THttpClient; import org.apache.thrift.transport.TTransportException; import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.JsonNode; import com.mashape.unirest.http.Unirest; public class LineApiImpl implements LineApi { private static final String EMAIL_REGEX = "[^@]+@[^@]+\\.[^@]+"; private static final String CERT_FILE = "./line.crt"; /** The ip. */ private String ip = "127.0.0.1"; /** The version. */ private String version = "4.0.1"; /** The com_name. */ private String systemName = "Line4J"; private String id; private String password; private String authToken; private String verifier; private String certificate; /** The revision. */ private long revision; /** The _headers. */ private Map<String, String> headers = new HashMap<String, String>(); /** The _client. */ public TalkService.Client client; public LineApiImpl() { initHeaders(""); } public LineApiImpl(String systemName) { initHeaders(systemName); } public static void main(String[] args) { // LineApi api = new LineApiImpl(); // try { // api.login("xxxx", "xxxx"); // } catch (java.net.SocketTimeoutException e) { // // setAwaitforVerify false // } catch (Exception e) { // // TODO Auto-generated catch block // e.printStackTrace(); // } } private void initHeaders(String systemName) { String osVersion = null; String userAgent = null; String app = null; boolean isMac = true; if (isMac) { osVersion = "10.10.4-YOSEMITE-x64"; userAgent = "DESKTOP:MAC:" + osVersion + "(" + version + ")"; app = "DESKTOPMAC\t" + osVersion + "\tMAC\t" + version; } else { osVersion = "6.1.7600-7-x64"; userAgent = "DESKTOP:WIN:" + osVersion + "(" + version + ")"; app = "DESKTOPWIN\t" + osVersion + "\tWINDOWS\t" + version; } if (StringUtils.isNotEmpty(systemName)) { this.systemName = systemName; } headers.put("User-Agent", userAgent); headers.put("X-Line-Application", app); } /** * Ready. * * @return the talk service. client * @throws TTransportException */ public Client ready() throws TTransportException { THttpClient transport = new THttpClient(LINE_HTTP_IN_URL); transport.setCustomHeaders(headers); transport.open(); TProtocol protocol = new TCompactProtocol(transport); return new TalkService.Client(protocol); } public LoginResult login(String id, String password) throws Exception { return login(id, password, null); } public LoginResult login(String id, String password, String certificate) throws Exception { IdentityProvider provider = null; Map<String, String> json = null; String sessionKey = null; boolean keepLoggedIn = true; String accessLocation = this.ip; // Login to LINE server. if (id.matches(EMAIL_REGEX)) { provider = IdentityProvider.LINE; // LINE json = getCertResult(LINE_SESSION_LINE_URL); } else { provider = IdentityProvider.NAVER_KR; // NAVER json = getCertResult(LINE_SESSION_NAVER_URL); } if (id != null) { this.id = id; } if (password != null) { this.password = password; } if (StringUtils.isNotEmpty(certificate)) { setCertificate(certificate); } else { // read the certificate file if it exists try { List<String> readFile = Utility.readFile(LineApiImpl.CERT_FILE); String tmpCert = readFile != null ? readFile.get(0) : ""; if (tmpCert != null) { setCertificate(tmpCert); } } catch (Exception ex) { setCertificate(""); } } sessionKey = json.get("session_key"); String tmpMsg = (char) (sessionKey.length()) + sessionKey + (char) (id.length()) + id + (char) (password.length()) + password; String message = new String(tmpMsg.getBytes(), java.nio.charset.StandardCharsets.UTF_8); String[] keyArr = json.get("rsa_key").split(","); String keyName = keyArr[0]; String n = keyArr[1]; String e = keyArr[2]; BigInteger modulus = new BigInteger(n, 16); BigInteger pubExp = new BigInteger(e, 16); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(modulus, pubExp); RSAPublicKey publicKey = (RSAPublicKey) keyFactory.generatePublic(pubKeySpec); Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); cipher.init(Cipher.ENCRYPT_MODE, publicKey); byte[] enBytes = cipher.doFinal(message.getBytes()); String encryptString = Hex.encodeHexString(enBytes); THttpClient transport = new THttpClient(LINE_HTTP_URL); transport.setCustomHeaders(headers); transport.open(); TProtocol protocol = new TCompactProtocol(transport); this.client = new TalkService.Client(protocol); LoginResult result = this.client.loginWithIdentityCredentialForCertificate(provider, keyName, encryptString, keepLoggedIn, accessLocation, this.systemName, this.certificate); if (result.getType() == LoginResultType.REQUIRE_DEVICE_CONFIRM) { headers.put("X-Line-Access", result.getVerifier()); String pinCode = result.getPinCode(); System.out.printf("Enter PinCode '%s' to your mobile phone in 2 minutes.\n", pinCode); // await for pinCode to be certified, it will return a verifier afterward. loginWithVerifierForCertificate(); } else if (result.getType() == LoginResultType.SUCCESS) { // if param certificate has passed certification setAuthToken(result.getAuthToken()); } // Once the client passed the verification, switch connection to HTTP_IN_URL this.client = ready(); return result; } /* * (non-Javadoc) * * @see api.line.LineApi#loginWithAuthToken(java.lang.String) */ public void loginWithAuthToken(String authToken) throws Exception { if (StringUtils.isNotEmpty(authToken)) { setAuthToken(authToken); } THttpClient transport = new THttpClient(LINE_HTTP_URL); transport.setCustomHeaders(headers); transport.open(); TProtocol protocol = new TCompactProtocol(transport); setClient(new TalkService.Client(protocol)); } /* * (non-Javadoc) * * @see api.line.LineApi#loginWithQrCode() */ public AuthQrcode loginWithQrCode() throws Exception { // Request QrCode from LINE server. // Map<String, String> json = null; boolean keepLoggedIn = false; THttpClient transport = new THttpClient(LINE_HTTP_URL); transport.setCustomHeaders(headers); transport.open(); TProtocol protocol = new TCompactProtocol(transport); this.client = new TalkService.Client(protocol); AuthQrcode result = this.client.getAuthQrcode(keepLoggedIn, systemName); headers.put("X-Line-Access", result.getVerifier()); System.out.println("Retrieved QR Code."); return result; // await for QR code to be certified, it will return a verifier afterward. // loginWithVerifier(); } /* * (non-Javadoc) * * @see api.line.LineApi#loginWithVerifier() */ public String loginWithVerifierForCertificate() throws Exception { Map json = null; json = getCertResult(LINE_CERTIFICATE_URL); if (json == null) { throw new Exception("fail to pass certificate check."); } // login with verifier json = (Map) json.get("result"); String verifier = (String) json.get("verifier"); this.verifier = verifier; LoginResult result = this.client.loginWithVerifierForCertificate(verifier); if (result.getType() == LoginResultType.SUCCESS) { setAuthToken(result.getAuthToken()); String tmpCert = result.getCertificate(); if (tmpCert != null) { Utility.writeFile(IOUtils.toInputStream(tmpCert), this.CERT_FILE); setCertificate(tmpCert); } return tmpCert; } else if (result.getType() == LoginResultType.REQUIRE_QRCODE) { throw new Exception("require QR code"); } else { throw new Exception("require device confirm"); } } public Map getCertResult(String url) throws Exception { Unirest unirest = new Unirest(); // set timed out in 2 mins. Unirest.setTimeouts(120000, 120000); HttpResponse<JsonNode> jsonResponse = unirest.get(url).headers(this.headers).asJson(); return Utility.toMap(jsonResponse.getBody().getObject()); } public boolean postContent(String url, Map<String, Object> data, InputStream is) throws Exception { Unirest unirest = new Unirest(); byte[] byteArray = IOUtils.toByteArray(is); HttpResponse<JsonNode> jsonResponse = unirest.post(url).headers(this.headers).fields(data) .field("file", byteArray, "").asJson(); return jsonResponse.getStatus() == 201; } /** * * After login, update authToken to avoid expiration of authToken. This method skip the PinCode * validation step. * **/ public boolean updateAuthToken() throws Exception { if (this.certificate != null) { this.login(this.id, this.password); this.loginWithAuthToken(this.authToken); return true; } else { throw new Exception("You need to login first. There is no valid certificate"); } } /** * find and add Contact by user id * * @return * @throws TException * @throws TalkException **/ public Contact findContactByUserid(String userid) throws TalkException, TException { return this.client.findContactByUserid(userid); } /** * find and add Contact by user id * * @return * @throws TException * @throws TalkException **/ public Map<String, Contact> findAndAddContactsByUserid(int reqSeq, String userid) throws TalkException, TException { return this.client.findAndAddContactsByUserid(0, userid); } /** * find contacts by email (not tested) * * @return * @throws TException * @throws TalkException **/ public Map<String, Contact> findContactsByEmail(Set<String> emails) throws TalkException, TException { return this.client.findContactsByEmail(emails); } /** * find and add contact by email (not tested) * * @return * @throws TException * @throws TalkException **/ public Map<String, Contact> findAndAddContactsByEmail(int reqSeq, Set<String> emails) throws TalkException, TException { return this.client.findAndAddContactsByEmail(0, emails); } /** * find and add contact by phone number (not tested) * * @return * @return * @throws TException * @throws TalkException **/ public Map<String, Contact> findContactsByPhone(Set<String> phones) throws TalkException, TException { return this.client.findContactsByPhone(phones); } /** * find and add contact by phone number (not tested) * * @return * @return * @throws TException * @throws TalkException **/ public Map<String, Contact> findAndAddContactsByPhone(int reqSeq, Set<String> phones) throws TalkException, TException { return this.client.findAndAddContactsByPhone(0, phones); } /** * Get profile information * * returns Profile object; - picturePath - displayName - phone (base64 encoded?) - * allowSearchByUserid - pictureStatus - userid - mid # used for unique id for account - * phoneticName - regionCode - allowSearchByEmail - email - statusMessage **/ public Profile getProfile() throws TalkException, TException { return this.client.getProfile(); } public List<String> getAllContactIds() throws TalkException, TException { /** Get all contacts of your LINE account **/ return this.client.getAllContactIds(); } public List<String> getBlockedContactIds() throws TalkException, TException { /** Get all blocked contacts of your LINE account **/ return this.client.getBlockedContactIds(); } public List<String> getHiddenContactIds() throws TalkException, TException { /** Get all hidden contacts of your LINE account **/ return this.client.getHiddenContactMids(); } public List<Contact> getContacts(List<String> ids) throws TalkException, TException { /** * Get contact information list from ids * * {returns{ List of Contact list;} - status - capableVideoCall - dispalyName - settings - * pictureStatus - capableVoiceCall - capableBuddy - mid - displayNameOverridden - relation - * thumbnailUrl_ - createdTime - facoriteTime - capableMyhome - attributes - type - phoneticName * - statusMessage **/ // if type(ids) != list{ // msg = "argument should be list of contact ids" return this.client.getContacts(ids); } public Room createRoom(int reqSeq, List<String> ids) throws TalkException, TException { /** Create a chat room **/ // reqSeq = 0; return this.client.createRoom(reqSeq, ids); } public Room getRoom(String roomId) throws TalkException, TException { /** Get a chat room **/ return this.client.getRoom(roomId); } public void inviteIntoRoom(String roomId, List<String> contactIds) throws TalkException, TException { /** Invite contacts into room **/ this.client.inviteIntoRoom(0, roomId, contactIds); } public void leaveRoom(String id) throws TalkException, TException { /** Leave a chat room **/ this.client.leaveRoom(0, id); } public Group createGroup(int seq, String name, List<String> ids) throws TalkException, TException { /** Create a group **/ // seq = 0; return this.client.createGroup(seq, name, ids); } public List<Group> getGroups(List<String> groupIds) throws TalkException, TException { /** Get a list of group with ids **/ // if type(ids) != list{ // msg = "argument should be list of group ids" // this.raise_error(msg) return this.client.getGroups(groupIds); } public List<String> getGroupIdsJoined() throws TalkException, TException { /** Get group id that you joined **/ return this.client.getGroupIdsJoined(); } public List<String> getGroupIdsInvited() throws TalkException, TException { /** Get group id that you invited **/ return this.client.getGroupIdsInvited(); } public void acceptGroupInvitation(int seq, String groupId) throws TalkException, TException { /** Accept a group invitation **/ // seq = 0; this.client.acceptGroupInvitation(seq, groupId); } public void cancelGroupInvitation(int seq, String groupId, List<String> contactIds) throws TalkException, TException { /** Cancel a group invitation **/ // seq = 0; this.client.cancelGroupInvitation(seq, groupId, contactIds); } public void inviteIntoGroup(int seq, String groupId, List<String> contactIds) throws TalkException, TException { /** Invite contacts into group **/ // seq = 0; this.client.inviteIntoGroup(seq, groupId, contactIds); } public void kickoutFromGroup(int seq, String groupId, List<String> contactIds) throws TalkException, TException { /** Kick a group members **/ // seq = 0; this.client.kickoutFromGroup(seq, groupId, contactIds); } public void leaveGroup(String id) throws TalkException, TException { /** Leave a group **/ this.client.leaveGroup(0, id); } public List<Message> getRecentMessages(String id, int count) throws TalkException, TException { /** Get recent messages from `id` **/ return this.client.getRecentMessages(id, count); } public Message sendMessage(int seq, Message message) throws TalkException, TException { /** * Send a message to `id`. `id` could be contact id or group id * * param message: `message` instance **/ return this.client.sendMessage(seq, message); } public long getLastOpRevision() throws TalkException, TException { return this.client.getLastOpRevision(); } public List<Operation> fetchOperations(long revision, int count) throws TalkException, TException { return this.client.fetchOperations(revision, count); } public TMessageBoxWrapUp getMessageBoxCompactWrapUp(String id) { try { return this.client.getMessageBoxCompactWrapUp(id); } catch (Exception e) { return null; } } public TMessageBoxWrapUpResponse getMessageBoxCompactWrapUpList(int start, int count) throws Exception { try { return this.client.getMessageBoxCompactWrapUpList(start, count); } catch (Exception e) { throw new Exception(e.getMessage()); } } private void setAuthToken(String token) { headers.put("X-Line-Access", token); this.authToken = token; } public TalkService.Client getClient() { return client; } public void setClient(TalkService.Client client) { this.client = client; } public String getLineAccessToken() { return headers.get("X-Line-Access"); } public String getCertificate() { return certificate; } public void setCertificate(String certificate) { this.certificate = certificate; } }