/** * Copyright 2011 Google Inc. * * 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 org.waveprotocol.box.server.robots.operations; import com.google.common.base.Preconditions; import com.google.common.collect.MapMaker; import com.google.gxp.com.google.common.collect.Maps; import com.google.wave.api.ParticipantProfile; import org.apache.commons.codec.digest.DigestUtils; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.waveprotocol.box.server.robots.operations.FetchProfilesService.ProfilesFetcher; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.ProtocolException; import java.net.URL; import java.util.Map; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; /** * A {@link ProfilesFetcher} implementation that fetches profile data from * Gravatar using the wave address as email. * * @author yurize@apache.org (Yuri Zelikov) */ public class GravatarProfileFetcher implements ProfilesFetcher { private static final Logger LOG = Logger.getLogger(GravatarProfileFetcher.class.getName()); private static final String EXCEPTION_FETCHING_PROFILE = "Exception while fetching profile for: "; private static final String GRAVATAR_BASIC_URL = "http://www.gravatar.com/"; private static final String GRAVATAR_URL = "http://www.gravatar.com/avatar/"; private static final long EXPIRE_AFTER = 1; private static String ROBOT_APP_ID = "wavyemail"; private static final ConcurrentMap<String, ParticipantProfile> PROFILES_CACHE = new MapMaker() .expireAfterWrite(EXPIRE_AFTER, TimeUnit.HOURS).makeMap(); private static final Map<String, ParticipantProfile> KNOWN_PROFILES_CACHE = Maps.newHashMap(); private static final String DEFAULT_WAVE_DOMAIN = "vegalabz.com"; static { KNOWN_PROFILES_CACHE.put("@waveinabox.net", new ParticipantProfile("@waveinabox.net", "Shared Wave", "https://wave.google.com/wave/static/images/profiles/public.png", "")); KNOWN_PROFILES_CACHE.put("@vegalabz.com", new ParticipantProfile("@vegalabz.com", "Shared Wave", "https://wave.google.com/wave/static/images/profiles/public.png", "")); KNOWN_PROFILES_CACHE .put( "wavyemail@waveinabox.net", new ParticipantProfile( "wavyemail@waveinabox.net", "Wavy Email bot", "https://lh6.googleusercontent.com/_tsWs83xehHE/TLBQ-7Nu-VI/AAAAAAAAFiA/ePzhrvMXkfI/wavyemailbeta.png", "")); } public static GravatarProfileFetcher create() { return new GravatarProfileFetcher(); } private GravatarProfileFetcher() { } public ParticipantProfile fetchFullProfile(String email, String waveAddress) throws ProfileFetchException { String imageUrl = null; String name = null; String profileUrl = null; JSONObject json = null; JSONArray entryArr = null; JSONObject entry = null; String emailHash = DigestUtils.md5Hex(email.toLowerCase().trim()); HttpURLConnection connection = null; URL url; try { url = new URL(GRAVATAR_BASIC_URL + emailHash + ".json"); connection = (HttpURLConnection) url.openConnection(); connection.setDoOutput(true); connection.setRequestMethod("GET"); connection.connect(); } catch (MalformedURLException e) { throw new ProfileFetchException(EXCEPTION_FETCHING_PROFILE + email, e); } catch (ProtocolException e) { throw new ProfileFetchException(EXCEPTION_FETCHING_PROFILE + email, e); } catch (IOException e) { throw new ProfileFetchException(EXCEPTION_FETCHING_PROFILE + email, e); } StringBuilder sb = new StringBuilder(); try { if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) { BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); String line; while ((line = reader.readLine()) != null) { sb.append(line); } } else { throw new ProfileFetchException("Response code: " + connection.getResponseCode() + "while fetching profile for: " + email + ", fetching 'identicon' image instead."); } } catch (java.io.IOException e) { throw new ProfileFetchException(EXCEPTION_FETCHING_PROFILE + email, e); } finally { connection.disconnect(); } LOG.fine("Json profile for " + email + " : " + sb.toString()); try { json = new JSONObject(sb.toString()); entryArr = new JSONArray(json.getString("entry")); entry = entryArr.getJSONObject(0); imageUrl = entry.getString("thumbnailUrl"); name = entry.getString("displayName"); profileUrl = entry.getString("profileUrl"); } catch (JSONException e) { throw new ProfileFetchException(EXCEPTION_FETCHING_PROFILE + email, e); } ParticipantProfile profile = new ParticipantProfile(waveAddress, name, imageUrl, profileUrl); PROFILES_CACHE.put(email, profile); return profile; } /** * Returns the Gravatar URL for the given email address. */ public String getImageUrl(String email) { // Hexadecimal MD5 hash of the requested user's lowercased email address // with all whitespace trimmed. String emailHash = DigestUtils.md5Hex(email.toLowerCase().trim()); return GRAVATAR_URL + emailHash + ".jpg" + "?s=100&d=identicon"; } private ParticipantProfile fetchOnlyGravatarImage(String waveAddress, String email) throws ProfileFetchException { ParticipantProfile pTemp = null; pTemp = FetchProfilesService.ProfilesFetcher.SIMPLE_PROFILE_FETCHER.fetchProfile(email); ParticipantProfile profile = new ParticipantProfile(waveAddress, pTemp.getName(), getImageUrl(email), pTemp.getProfileUrl()); return profile; } @Override public ParticipantProfile fetchProfile(String waveAddress) { Preconditions.checkNotNull(waveAddress); if (PROFILES_CACHE.containsKey(waveAddress)) { return PROFILES_CACHE.get(waveAddress); } else if (KNOWN_PROFILES_CACHE.containsKey(waveAddress)) { return KNOWN_PROFILES_CACHE.get(waveAddress); } ParticipantProfile participantProfile = null; String[] split = waveAddress.split("@"); String wavyEmailAddress; if (split[1].equals(DEFAULT_WAVE_DOMAIN)) { wavyEmailAddress = split[0] + "@" + ROBOT_APP_ID + ".appspotmail.com"; try { participantProfile = fetchFullProfile(wavyEmailAddress, waveAddress); } catch (ProfileFetchException e) { LOG.fine("Can't load full profile for: " + wavyEmailAddress); } } if (!split[1].equals(DEFAULT_WAVE_DOMAIN) || participantProfile == null) { wavyEmailAddress = split[0] + "_" + split[1] + "@" + ROBOT_APP_ID + ".appspotmail.com"; try { participantProfile = fetchFullProfile(wavyEmailAddress, waveAddress); } catch (ProfileFetchException e) { LOG.fine("Can't load full profile for: " + wavyEmailAddress); try { wavyEmailAddress = split[0] + "@" + ROBOT_APP_ID + ".appspotmail.com"; LOG.fine("Trying to fetch only Gravatar image for: " + wavyEmailAddress); participantProfile = fetchOnlyGravatarImage(waveAddress, wavyEmailAddress); } catch (ProfileFetchException e1) { LOG.log(Level.SEVERE, EXCEPTION_FETCHING_PROFILE + waveAddress + "wavyEmail: " + wavyEmailAddress, e1); participantProfile = new ParticipantProfile(); } } } PROFILES_CACHE.put(waveAddress, participantProfile); return participantProfile; } }