/* * Jitsi, the OpenSource Java VoIP and Instant Messaging client. * * Copyright @ 2015 Atlassian Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.java.sip.communicator.impl.protocol.icq; import java.net.*; import java.util.*; import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.service.protocol.ServerStoredDetails.GenericDetail; import net.java.sip.communicator.util.*; import net.kano.joscar.flapcmd.*; import net.kano.joscar.snac.*; import net.kano.joscar.snaccmd.icq.*; /** * @author Damian Minkov */ public class InfoRetreiver { private static final Logger logger = Logger.getLogger(InfoRetreiver.class); /** * A callback to the ICQ provider that created us. */ private ProtocolProviderServiceIcqImpl icqProvider = null; // the uin of the account using us, // used when sending commands for user info to the server private String ownerUin = null; // here is kept all the details retrieved so far private final Map<String, List<GenericDetail>> retreivedDetails = new Hashtable<String, List<GenericDetail>>(); // used to generate request id when sending commands for retrieving user info private static int requestID = 0; /** * As all the Full User Info comes in * sequences of 8 packets according to the * requestID we keep the stored Info so far. */ private static final Map<Integer, List<GenericDetail>> retreivedInfo = new Hashtable<Integer, List<GenericDetail>>(); protected InfoRetreiver (ProtocolProviderServiceIcqImpl icqProvider, String ownerUin) { this.icqProvider = icqProvider; this.ownerUin = ownerUin; } /** * returns the user details from the specified class or its descendants * the class is one from the * net.java.sip.communicator.service.protocol.ServerStoredDetails * or implemented one in the operation set for the user info * * @param uin String * @param detailClass Class * @return Iterator */ public <T extends GenericDetail> Iterator<T> getDetailsAndDescendants( String uin, Class<T> detailClass) { List<GenericDetail> details = getContactDetails(uin); List<T> result = new LinkedList<T>(); for (GenericDetail item : details) if (detailClass.isInstance(item)) { @SuppressWarnings("unchecked") T t = (T) item; result.add(t); } return result.iterator(); } /** * returns the user details from the specified class * exactly that class not its descendants * * @param uin String * @param detailClass Class * @return Iterator */ public Iterator<GenericDetail> getDetails( String uin, Class<? extends GenericDetail> detailClass) { List<GenericDetail> details = getContactDetails(uin); List<GenericDetail> result = new LinkedList<GenericDetail>(); for (GenericDetail item : details) { if(detailClass.equals(item.getClass())) result.add(item); } return result.iterator(); } /** * request the full info for the given uin * waits and return this details * * @param uin String * @return Vector */ protected List<GenericDetail> getContactDetails(String uin) { List<GenericDetail> result = getCachedContactDetails(uin); if(result == null) { return retrieveDetails(uin); } return result; } /** * Retrieve details and return them or if missing return an empty list. * @param uin the uin to search for. * @return the details or empty list. */ protected List<GenericDetail> retrieveDetails(String uin) { List<GenericDetail> result = null; // Fixes a NumberFormatException, occurring if the uin is not a long // number. try { //retrieve the details long toICQUin = Long.parseLong(uin); int reqID = requestID++; MetaFullInfoRequest infoRequest = new MetaFullInfoRequest( Long.parseLong(ownerUin), reqID, toICQUin); UserInfoResponseRetriever responseRetriever = new UserInfoResponseRetriever(reqID); icqProvider.getAimConnection().getInfoService().getOscarConnection() .sendSnacRequest(infoRequest, responseRetriever); responseRetriever.waitForLastInfo(60000); result = responseRetriever.result; } catch(Exception e) {} if (result == null) result = new LinkedList<GenericDetail>(); // put even empty result to bypass further retrieve retreivedDetails.put(uin, result); return result; } /** * Request the full info for the given uin if available in cache, * if missing return null. * * @param uin to search for in cache. * @return list of details. */ protected List<GenericDetail> getCachedContactDetails(String uin) { return retreivedDetails.get(uin); } /** * waits for the last snac from the full info response sequence */ private class UserInfoResponseRetriever extends SnacRequestAdapter { private final int requestID; List<GenericDetail> result = null; UserInfoResponseRetriever(int requestID) { this.requestID = requestID; } @Override public void handleResponse(SnacResponseEvent e) { SnacCommand snac = e.getSnacCommand(); if (snac instanceof MetaBasicInfoCmd) { if (logger.isTraceEnabled()) logger.trace("received basic info"); readBasicUserInfo((MetaBasicInfoCmd)snac); } else if (snac instanceof MetaMoreInfoCmd) { if (logger.isTraceEnabled()) logger.trace("received meta more info"); readMoreUserInfo((MetaMoreInfoCmd)snac); } else if (snac instanceof MetaEmailInfoCmd) { if (logger.isTraceEnabled()) logger.trace("received email info"); readEmailUserInfo((MetaEmailInfoCmd)snac); } else if (snac instanceof MetaHomepageCategoryInfoCmd) { if (logger.isTraceEnabled()) logger.trace("received home page info"); readHomePageUserInfo((MetaHomepageCategoryInfoCmd)snac); } else if (snac instanceof MetaWorkInfoCmd) { if (logger.isTraceEnabled()) logger.trace("received work info"); readWorkUserInfo((MetaWorkInfoCmd)snac); } else if (snac instanceof MetaNotesInfoCmd) { if (logger.isTraceEnabled()) logger.trace("received notes info"); readUserAboutInfo((MetaNotesInfoCmd)snac); } else if (snac instanceof MetaInterestsInfoCmd) { if (logger.isTraceEnabled()) logger.trace("received interest info"); readInterestsUserInfo((MetaInterestsInfoCmd)snac); } else if (snac instanceof MetaAffiliationsInfoCmd) { if (logger.isTraceEnabled()) logger.trace("received affiliations info"); readAffilationsUserInfo((MetaAffiliationsInfoCmd)snac); result = getInfoForRequest(((MetaAffiliationsInfoCmd)snac).getId()); // this is the last packet synchronized(this){this.notifyAll();} } } public void waitForLastInfo(long waitFor) { synchronized(this) { try { wait(waitFor); } catch (InterruptedException ex) { if (logger.isDebugEnabled()) logger.debug( "Interrupted while waiting for a subscription evt", ex); } } } } /** * wait for response of our ShorInfo Requests */ private static class ShortInfoResponseRetriever extends SnacRequestAdapter { String nickname = null; @Override public void handleResponse(SnacResponseEvent e) { SnacCommand snac = e.getSnacCommand(); if (snac instanceof MetaShortInfoCmd) { MetaShortInfoCmd infoSnac = (MetaShortInfoCmd)snac; nickname = infoSnac.getNickname(); synchronized(this){this.notifyAll();} } } } /** * when detail is changed we remove it from the cache, * from retreivedDetails so the next time we want the details * we are shure they are get from the server and are actual * * @param uin String */ protected void detailsChanged(String uin) { retreivedDetails.remove(uin); } /** * Get the nickname of the specified uin * @param uin String the uin * @return String the nickname of the uin */ public String getNickName(String uin) { ShortInfoResponseRetriever responseRetriever = new ShortInfoResponseRetriever(); long longUin = Long.parseLong(uin); MetaShortInfoRequest req = new MetaShortInfoRequest(Long.parseLong(ownerUin), 2, longUin); icqProvider.getAimConnection().getInfoService().getOscarConnection() .sendSnacRequest(req, responseRetriever); synchronized(responseRetriever) { try{ responseRetriever.wait(30000); } catch (InterruptedException ex) { //we don't care } } return responseRetriever.nickname; } /** * Metods retrieving data and storing it */ /** * Returns the stored info so far on the specified request * * @param requestID int * @return List */ private List<GenericDetail> getInfoForRequest(int requestID) { List<GenericDetail> res = retreivedInfo.get(requestID); if (res == null) { // this indicates that the info data // doesn't exists, so this is the first packet // from the sequence (basic info) res = new LinkedList<GenericDetail>(); retreivedInfo.put(requestID, res); } return res; } /** * Method for parsing incoming data * Read data in MetaBasicInfoCmd command * @param cmd MetaBasicInfoCmd */ private void readBasicUserInfo(MetaBasicInfoCmd cmd) { List<GenericDetail> infoData = getInfoForRequest(cmd.getId()); Locale countryCodeLocale = OperationSetServerStoredAccountInfoIcqImpl.getCountry(cmd.getCountryCode()); if (countryCodeLocale != null) infoData.add(new ServerStoredDetails.CountryDetail( countryCodeLocale)); // the following are not used // cmd.getGmtOffset() // cmd.isAuthorization() // cmd.isWebAware() // cmd.isPublishPrimaryEmail() // everything is read lets store it String tmp = null; if ((tmp = cmd.getNickname()) != null) infoData.add(new ServerStoredDetails.NicknameDetail(tmp)); if ((tmp = cmd.getFirstName()) != null) infoData.add(new ServerStoredDetails.FirstNameDetail(tmp)); if ((tmp = cmd.getLastName()) != null) infoData.add(new ServerStoredDetails.LastNameDetail(tmp)); if ((tmp = cmd.getEmail()) != null) infoData.add(new ServerStoredDetails.EmailAddressDetail(tmp)); if ((tmp = cmd.getHomeCity()) != null) infoData.add(new ServerStoredDetails.CityDetail(tmp)); if ((tmp = cmd.getHomeState()) != null) infoData.add(new ServerStoredDetails.ProvinceDetail(tmp)); if ((tmp = cmd.getHomePhone()) != null) infoData.add(new ServerStoredDetails.PhoneNumberDetail(tmp)); if ((tmp = cmd.getHomeFax()) != null) infoData.add(new ServerStoredDetails.FaxDetail(tmp)); if ((tmp = cmd.getHomeAddress()) != null) infoData.add(new ServerStoredDetails.AddressDetail(tmp)); if ((tmp = cmd.getCellPhone()) != null) infoData.add(new ServerStoredDetails.MobilePhoneDetail(tmp)); if ((tmp = cmd.getHomeZip()) != null) infoData.add(new ServerStoredDetails.PostalCodeDetail(tmp)); } /** * Method for parsing incoming data * Read data in MoreUserInfo command * @param cmd MetaMoreInfoCmd */ private void readMoreUserInfo(MetaMoreInfoCmd cmd) { List<GenericDetail> infoData = getInfoForRequest(cmd.getId()); ServerStoredDetails.GenderDetail gender = OperationSetServerStoredAccountInfoIcqImpl.genders[cmd.getGender()]; if(gender != null) infoData.add(gender); String tmp = null; try { if((tmp = cmd.getHomepage()) != null) infoData.add(new ServerStoredDetails.WebPageDetail(new URL(tmp))); } catch (MalformedURLException ex) {} Calendar birthDate = Calendar.getInstance(); birthDate.setTime(cmd.getBirthDate()); infoData.add(new ServerStoredDetails.BirthDateDetail(birthDate)); Locale spokenLanguage1 = OperationSetServerStoredAccountInfoIcqImpl. getSpokenLanguage(cmd.getSpeakingLanguages()[0]); if(spokenLanguage1 != null) infoData.add( new ServerStoredDetails.SpokenLanguageDetail(spokenLanguage1)); Locale spokenLanguage2 = OperationSetServerStoredAccountInfoIcqImpl. getSpokenLanguage(cmd.getSpeakingLanguages()[1]); if(spokenLanguage2 != null) infoData.add( new ServerStoredDetails.SpokenLanguageDetail(spokenLanguage2)); Locale spokenLanguage3 = OperationSetServerStoredAccountInfoIcqImpl. getSpokenLanguage(cmd.getSpeakingLanguages()[2]); if(spokenLanguage3 != null) infoData.add( new ServerStoredDetails.SpokenLanguageDetail(spokenLanguage3)); if((tmp = cmd.getOriginalCity()) != null) infoData.add( new OperationSetServerStoredAccountInfoIcqImpl.OriginCityDetail(tmp)); if((tmp = cmd.getOriginalState()) != null) infoData.add( new OperationSetServerStoredAccountInfoIcqImpl.OriginProvinceDetail(tmp)); Locale originCountryLocale = OperationSetServerStoredAccountInfoIcqImpl.getCountry(cmd.getOriginalCountryCode()); if(originCountryLocale != null) infoData.add( new OperationSetServerStoredAccountInfoIcqImpl. OriginCountryDetail(originCountryLocale)); int userGMTOffset = cmd.getTimeZone(); TimeZone userTimeZone = null; if(userGMTOffset >= 0) userTimeZone = TimeZone.getTimeZone("GMT+" + userGMTOffset); else userTimeZone = TimeZone.getTimeZone("GMT" + userGMTOffset); infoData.add(new ServerStoredDetails.TimeZoneDetail("GMT Offest", userTimeZone)); } /** * Method for parsing incoming data * Read data in EmailUserInfo command * @param cmd MetaEmailInfoCmd */ private void readEmailUserInfo(MetaEmailInfoCmd cmd) { List<GenericDetail> infoData = getInfoForRequest(cmd.getId()); String emails[] = cmd.getEmails(); if (emails == null) return; for (String email : emails) { infoData.add(new ServerStoredDetails.EmailAddressDetail(email)); } } /** * Method for parsing incoming data * Read data in HomePageUserInfo command * @param cmd MetaHomepageCategoryInfoCmd */ private void readHomePageUserInfo(MetaHomepageCategoryInfoCmd cmd) { List<GenericDetail> infoData = getInfoForRequest(cmd.getId()); String tmp = null; try { if ((tmp = cmd.getKeywords()) != null) infoData.add(new ServerStoredDetails.WebPageDetail(new URL(tmp))); } catch (MalformedURLException ex) {} } /** * Method for parsing incoming data * Read data in WorkUserInfo command * @param cmd MetaWorkInfoCmd */ private void readWorkUserInfo(MetaWorkInfoCmd cmd) { List<GenericDetail> infoData = getInfoForRequest(cmd.getId()); String tmp = null; if ((tmp = cmd.getWorkCity()) != null) infoData.add(new ServerStoredDetails.WorkCityDetail(tmp)); if ((tmp = cmd.getWorkState()) != null) infoData.add(new ServerStoredDetails.WorkProvinceDetail(tmp)); if ((tmp = cmd.getWorkPhone()) != null) infoData.add(new ServerStoredDetails.WorkPhoneDetail(tmp)); if ((tmp = cmd.getWorkFax()) != null) infoData.add( new OperationSetServerStoredAccountInfoIcqImpl.WorkFaxDetail(tmp)); if ((tmp = cmd.getWorkAddress()) != null) infoData.add(new ServerStoredDetails.WorkAddressDetail(tmp)); if ((tmp = cmd.getWorkZipCode()) != null) infoData.add(new ServerStoredDetails.WorkPostalCodeDetail(tmp)); Locale workCountry = OperationSetServerStoredAccountInfoIcqImpl.getCountry(cmd.getWorkCountryCode()); if (workCountry != null) infoData.add(new ServerStoredDetails.WorkCountryDetail(workCountry)); if ((tmp = cmd.getWorkCompany()) != null) infoData.add(new ServerStoredDetails.WorkOrganizationNameDetail(tmp)); if ((tmp = cmd.getWorkDepartment()) != null) infoData.add( new OperationSetServerStoredAccountInfoIcqImpl.WorkDepartmentNameDetail(tmp)); if ((tmp = cmd.getWorkPosition()) != null) infoData.add( new OperationSetServerStoredAccountInfoIcqImpl.WorkPositionNameDetail(tmp)); int workOccupationCode = cmd.getWorkOccupationCode(); if (workOccupationCode == 99) infoData.add( new OperationSetServerStoredAccountInfoIcqImpl. WorkOcupationDetail(OperationSetServerStoredAccountInfoIcqImpl. occupations[OperationSetServerStoredAccountInfoIcqImpl.occupations.length - 1])); else infoData.add( new OperationSetServerStoredAccountInfoIcqImpl. WorkOcupationDetail( OperationSetServerStoredAccountInfoIcqImpl.occupations[workOccupationCode])); try { if ((tmp = cmd.getWorkWebPage()) != null) infoData.add(new ServerStoredDetails.WorkPageDetail(new URL(tmp))); } catch (MalformedURLException ex) {} } /** * Method for parsing incoming data * Read data in UserAboutInfo command * @param cmd MetaNotesInfoCmd */ private void readUserAboutInfo(MetaNotesInfoCmd cmd) { List<GenericDetail> infoData = getInfoForRequest(cmd.getId()); if (cmd.getNotes() != null) infoData.add( new OperationSetServerStoredAccountInfoIcqImpl.NotesDetail(cmd.getNotes())); } /** * Method for parsing incoming data * Read data in InterestsUserInfo command * @param cmd MetaInterestsInfoCmd */ private void readInterestsUserInfo(MetaInterestsInfoCmd cmd) { List<GenericDetail> infoData = getInfoForRequest(cmd.getId()); int[] categories = cmd.getCategories(); String[] interests = cmd.getInterests(); for (int i = 0; i < interests.length; i++) { int category = categories[i]; if (category != 0) { // as the categories are between 100 and 150 we shift them // because their string representations are stored in array category = category - 99; } if(category <= interests.length) { infoData.add( new OperationSetServerStoredAccountInfoIcqImpl.InterestDetail( interests[i], OperationSetServerStoredAccountInfoIcqImpl.interestsCategories[category])); } } } /** * Not used for now * @param cmd MetaAffiliationsInfoCmd */ private void readAffilationsUserInfo(MetaAffiliationsInfoCmd cmd) { // Vector<GenericDetail> infoData = getInfoForRequest(cmd.getId()); } }