All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.facebook.api.Facebook Maven / Gradle / Ivy

The newest version!
package com.facebook.api;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.Map.Entry;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Utility class to handle authorization and authentication of requests. Objects of this class are meant to be created for every request. They are stateless and are not
 * supposed to be kept in the session.
 */
public class Facebook {

	private HttpServletRequest request;

	private HttpServletResponse response;

	protected FacebookRestClient apiClient;

	protected String apiKey;

	protected String secret;

	protected Map fbParams;

	protected Long user;

	private static String FACEBOOK_URL_PATTERN = "^https?://([^/]*\\.)?facebook\\.com(:\\d+)?/.*";

  private String sessionKey;
  private boolean inCanvas;
  private boolean added;
  private boolean uninstall;
  private boolean installed;
  private Long canvasUser;
  private Long profileUpdateTime;
  private boolean inProfileTab;
  private Long profileUser;
  private String profileSessionKey;
  private Long pageId;
  private boolean pageAdded;

  public Facebook( HttpServletRequest request, HttpServletResponse response, String apiKey, String secret ) {
		this.request = request;
		this.response = response;
		this.apiKey = apiKey;
		this.secret = secret;
		this.apiClient = new FacebookRestClient( this.apiKey, this.secret );
		validateFbParams();
		// caching of friends
		String friends = fbParams.get( "friends" );
		if ( friends != null && !friends.equals( "" ) ) {
			List friendsList = new ArrayList();
			for ( String friend : friends.split( "," ) ) {
				friendsList.add( Long.parseLong( friend ) );
			}
			apiClient.friendsList = friendsList;
		}
		// caching of the "added" value
    added = toBoolean(fbParams.get("added"));
		apiClient.added = added;

    inCanvas = toBoolean(fbParams.get("in_canvas"));
    inProfileTab = toBoolean(fbParams.get("in_profile_tab"));
    uninstall = toBoolean(fbParams.get("uninstall"));
    installed = toBoolean(request.getParameter("installed"));
    canvasUser = toLong(fbParams.get("canvas_user"));
    profileUser = toLong(fbParams.get("profile_user"));
    profileSessionKey = fbParams.get("profile_session_key");
    profileUpdateTime = toLong(fbParams.get("profile_update_time"));
    pageId = toLong(fbParams.get("page_id"));
    pageAdded = toBoolean(fbParams.get("page_added"));
  }

  private Long toLong(String s) {
    if(s == null) return null;
    return new Long(s);
  }

  private boolean toBoolean(String s) {
    return s != null && s.equals("1");
  }

  /**
	 * Returns the internal FacebookRestClient object.
	 * 
	 * @return
	 */
	public FacebookRestClient getFacebookRestClient() {
		return apiClient;
	}

	/**
	 * Synonym for {@link #getFacebookRestClient()}
	 * 
	 * @return
	 */
	public FacebookRestClient get_api_client() {
		return getFacebookRestClient();
	}

	/**
	 * Returns the secret key used to initialize this object.
	 * 
	 * @return
	 */
	public String getSecret() {
		return secret;
	}

	/**
	 * Returns the api key used to initialize this object.
	 * 
	 * @return
	 */
	public String getApiKey() {
		return apiKey;
	}

	private void validateFbParams() {
		// first we analyze the request parameters
		fbParams = getValidFbParams( _getRequestParams(), 48 * 3600, FacebookParam.SIGNATURE.toString() );
		if ( fbParams != null && !fbParams.isEmpty() ) {
			// this comment block is copied from the official php client,
			// it explains a lot :)
			//
			// If we got any fb_params passed in at all, then either:
			// - they included an fb_user / fb_session_key, which we should
			// assume
			// to be correct
			// - they didn't include an fb_user / fb_session_key, which means
			// the
			// user doesn't have a valid session and if we want to get one we'll
			// need to use require_login(). (Calling set_user with null values
			// for user/session_key will work properly.)
			// - Note that we should *not* use our cookies in this scenario,
			// since
			// they may be referring to the wrong user.
			//

			// parsing the user, session, and expiry info
			String tmpSt = fbParams.get( FacebookParam.USER.getSignatureName() );
			Long user_id = tmpSt != null ? Long.valueOf( tmpSt ) : null;
			String session_key = fbParams.get( FacebookParam.SESSION_KEY.getSignatureName() );
			tmpSt = fbParams.get( FacebookParam.EXPIRES.getSignatureName() );
			Long expires = tmpSt != null ? Long.valueOf( tmpSt ) : null;
			setUser( user_id, session_key, expires );
		} else {
			// fallback to checking cookies
			Map cookieParams = _getCookiesParams();
			fbParams = getValidFbParams( cookieParams, null, this.apiKey );
			if ( fbParams != null && !fbParams.isEmpty() ) {
				// parsing the user and session
				String tmpSt = fbParams.get( FacebookParam.USER.getSignatureName() );
				Long user_id = tmpSt != null ? Long.valueOf( tmpSt ) : null;
				String session_key = fbParams.get( FacebookParam.SESSION_KEY.getSignatureName() );
				setUser( user_id, session_key, null );
			}
			// finally we check the auth_token for a round-trip from the
			// facebook login page
			else if ( request.getParameter( "auth_token" ) != null ) {
				try {
					doGetSession( request.getParameter( "auth_token" ) );
					setUser( apiClient._userId, apiClient._sessionKey, apiClient._expires );
				}
				catch ( Exception e ) {
					// if auth_token is stale (browser url doesn't change,
					// server is restarted), then auth_getSession throws
					// an exception. This happens a lot during development. To
					// recover, we do nothing. Then when requireLogin or
					// requireAdd
					// kick in, a new auth_token is created by redirecting the
					// user.
					// e.printStackTrace(System.err);
				}
			}
		}
	}

	public String doGetSession( String authToken ) {
		try {
			return apiClient.auth_getSession( authToken );
		}
		catch ( Exception e ) {
			throw new RuntimeException( e );
		}
	}

	/**
	 * Sets the user. This method also saves the user and session information in the HttpSession
	 * 
	 * @param user_id
	 * @param session_key
	 * @param expires
	 */
	private void setUser( Long user_id, String session_key, Long expires ) {
		// place the data in the session for future requests that may not have
		// the
		// facebook parameters
		if ( !inFbCanvas() ) {
			Map cookiesInfo = _getCookiesParams();
			String cookieUser = cookiesInfo.get( this.apiKey + "_user" );
			if ( cookieUser == null || !cookieUser.equals( user_id + "" ) ) {
				// map of parameters, but without the api_key prefix
				Map cookies = new HashMap();
				cookies.put( "user", user_id + "" );
				cookies.put( "session_key", session_key );
				String sig = generateSig( cookies, this.secret );
				int age = 0;
				if ( expires != null ) {
					age = (int) ( expires.longValue() - ( System.currentTimeMillis() / 1000 ) );
				}
				for ( Map.Entry entry : cookies.entrySet() ) {
					addCookie( this.apiKey + "_" + entry.getKey(), entry.getValue(), age );
				}
				addCookie( this.apiKey, sig, age );
			}
		}
		this.user = user_id;
    this.sessionKey = session_key;
    this.apiClient._sessionKey = session_key;
	}

	private void addCookie( String key, String value, int age ) {
		Cookie cookie = new Cookie( key, value );
		if ( age > 0 ) {
			cookie.setMaxAge( age );
		}
		cookie.setPath( request.getContextPath() );
		response.addCookie( cookie );
	}

	private Map getValidFbParams( Map params, Integer timeout, String namespace ) {
		if ( namespace == null )
			namespace = "fb_sig";
		String prefix = namespace + "_";
		int prefix_len = prefix.length();
		Map fb_params = new HashMap();
		for ( Entry requestParam : params.entrySet() ) {
			if ( requestParam.getKey().indexOf( prefix ) == 0 ) {
				fb_params.put( requestParam.getKey().substring( prefix_len ), requestParam.getValue() );
			}
		}
		if ( timeout != null ) {
			if ( !fb_params.containsKey( FacebookParam.TIME.getSignatureName() ) ) {
				return new HashMap();
			}
			String tmpTime = fb_params.get( FacebookParam.TIME.getSignatureName() );
			if ( tmpTime.indexOf( '.' ) > 0 )
				tmpTime = tmpTime.substring( 0, tmpTime.indexOf( '.' ) );
			long time = Long.parseLong( tmpTime );
			if ( System.currentTimeMillis() / 1000 - time > timeout ) {
				return new HashMap();
			}
		}
		if ( !params.containsKey( namespace ) || !verifySignature( fb_params, params.get( namespace ) ) ) {
			return new HashMap();
		}
		return fb_params;
	}

	private void redirect( String url ) {
		try {
			// fbml redirect
			if ( inFbCanvas() ) {
				String out = "";
				response.getWriter().print( out );
				response.flushBuffer();
			}
			// javascript "frame-bypassing" redirect
			else if ( url.matches( FACEBOOK_URL_PATTERN ) ) {
				String out = "";
				response.getWriter().print( out );
				response.flushBuffer();
			} else {
				// last fallback
				response.sendRedirect( url );
			}
		}
		catch ( IOException e ) {
			throw new RuntimeException( e );
		}
	}

	/**
	 * Returns true if the application is in a frame or a canvas.
	 * 
	 * @return
	 */
	public boolean inFrame() {
		return fbParams.containsKey( FacebookParam.IN_CANVAS.getSignatureName() ) || fbParams.containsKey( FacebookParam.IN_IFRAME.getSignatureName() );
	}

	/**
	 * Returns true if the application is in a canvas.
	 * 
	 * @return
	 */
	public boolean inFbCanvas() {
		return fbParams.containsKey( FacebookParam.IN_CANVAS.getSignatureName() );
	}

	public boolean isAdded() {
		return "1".equals( fbParams.get( FacebookParam.ADDED.getSignatureName() ) );
	}

	public boolean isLogin() {
		return getUser() != null;
	}

	/**
	 * Synonym for {@link #getUser()}
	 * 
	 * @return
	 */
	public Long get_loggedin_user() {
		return getUser();
	}

	/**
	 * Returns the user id of the logged in user associated with this object
	 * 
	 * @return
	 */
	public Long getUser() {
		return this.user;
	}

  /**
   * Returns the session key of the logged in user.
   * @return
   */
  public String getSessionKey() {
    return this.sessionKey;
  }

  /**
	 * Returns the url of the currently requested page
	 * 
	 * @return
	 */
	private String currentUrl() {
		String url = request.getScheme() + "://" + request.getServerName();
		int port = request.getServerPort();
		if ( port != 80 ) {
			url += ":" + port;
		}
		url += request.getRequestURI();
		return url;
	}

	/**
	 * Forces the user to log in to this application. If the user hasn't logged in yet, this method issues a url redirect.
	 *
     * NOTE: You should return immediately if this returns true.
     *
     * Example usage:
     * if(facebook.requireLogin("")) return;
     *
	 * @param next
	 *            the value for the 'next' request paramater that is appended to facebook's login screen.
     *            This should be an emptry string if you want it to go to your base canvas url.
	 * @return true if the user hasn't logged in yet and a redirect was issued.
	 */
	public boolean requireLogin( String next ) {
		if ( getUser() != null )
			return false;
		redirect( getLoginUrl( next, inFrame() ) );
		return true;
	}

	/**
	 * Forces the user to add this application. If the user hasn't added it yet, this method issues a url redirect.
	 * 
	 * @param next
	 *            the value for the 'next' request paramater that is appended to facebook's add screen.
	 * @return true if the user hasn't added the application yet and a redirect was issued.
   * @deprecated Use requireLogin now instead of requireAdd. 
   * @see http://forum.developers.facebook.com/viewtopic.php?id=15734
	 */
	public boolean requireAdd( String next ) {
		if ( getUser() != null && isAdded() )
			return false;
		redirect( getAddUrl( next ) );
		return true;
	}

	/**
	 * Forces the application to be in a frame. If it is not in a frame, this method issues a url redirect.
	 * 
	 * @param next
	 *            the value for the 'next' request paramater that is appended to facebook's login screen.
	 * @return true if a redirect was issued, false otherwise.
	 */
	public boolean requireFrame( String next ) {
		if ( !inFrame() ) {
			redirect( getLoginUrl( next, true ) );
			return true;
		}
		return false;
	}

	/**
	 * Returns the url that facebook uses to prompt the user to login to this application.
	 * 
	 * @param next
	 *            indicates the page to which facebook should redirect the user has logged in.
	 * @return
	 */
	public String getLoginUrl( String next, boolean canvas ) {
		String url = getFacebookUrl( null ) + "/login.php?v=1.0&api_key=" + apiKey;
		try {
			url += next != null ? "&next=" + URLEncoder.encode( next, "UTF-8" ) : "";
		}
		catch ( UnsupportedEncodingException e ) {
			throw new RuntimeException( e );
		}
		url += canvas ? "&canvas" : "";
		return url;
	}

	/**
	 * Returns the url that facebook uses to prompt the user to add this application.
	 * 
	 * @param next
	 *            indicates the page to which facebook should redirect the user after the application is added.
	 * @return
	 */
	public String getAddUrl( String next ) {
		String url = getFacebookUrl( null ) + "/add.php?api_key=" + apiKey;
		try {
			url += next != null ? "&next=" + URLEncoder.encode( next, "UTF-8" ) : "";
		}
		catch ( UnsupportedEncodingException e ) {
			throw new RuntimeException( e );
		}
		return url;
	}

	/**
	 * Returns a url to a facebook sub-domain
	 * 
	 * @param subDomain
	 * @return
	 */
	public static String getFacebookUrl( String subDomain ) {
		if ( subDomain == null || subDomain.equals( "" ) )
			subDomain = "www";
		return "http://" + subDomain + ".facebook.com";
	}

	public static String generateSig( Map params, String secret ) {
		SortedSet keys = new TreeSet( params.keySet() );
		// make sure that the signature paramater is not included
		keys.remove( FacebookParam.SIGNATURE.toString() );
		String str = "";
		for ( String key : keys ) {
			str += key + "=" + params.get( key );
		}
		str += secret;
		try {
			MessageDigest md = MessageDigest.getInstance( "MD5" );
			md.update( str.getBytes( "UTF-8" ) );
			StringBuilder result = new StringBuilder();
			for ( byte b : md.digest() ) {
				result.append( Integer.toHexString( ( b & 0xf0 ) >>> 4 ) );
				result.append( Integer.toHexString( b & 0x0f ) );
			}
			return result.toString();
		}
		catch ( Exception e ) {
			throw new RuntimeException( e );
		}
	}

	/**
	 * Verifies that the signature of the parameters is valid
	 * 
	 * @param params
	 *            a map of the parameters. Typically these are the request parameters that start with "fb_sig"
	 * @param expected_sig
	 *            the expected signature
	 * @return
	 */
	public boolean verifySignature( Map params, String expected_sig ) {
		return generateSig( params, secret ).equals( expected_sig );
	}

	/**
	 * returns a String->String map of the request parameters. It doesn't matter if the request method is GET or POST.
	 * 
	 * @return
	 */
	private Map _getRequestParams() {
		Map results = new HashMap();
		Map map = request.getParameterMap();
		for ( Entry entry : map.entrySet() ) {
			results.put( entry.getKey(), entry.getValue()[0] );
		}
		return results;
	}

	private Map _getCookiesParams() {
		Map results = new HashMap();
		Cookie[] cookies = request.getCookies();
		if ( cookies != null ) {
			for ( Cookie cookie : cookies ) {
				results.put( cookie.getName(), cookie.getValue() );
			}
		}
		return results;
	}

  public List getFriends(){
    return apiClient.friendsList;
  }

  public Long getProfileUser() {
    return profileUser;
  }

  public boolean isUninstall() {
    return uninstall;
  }

  public Long getProfileUpdateTime() {
    return profileUpdateTime;
  }

  public String getProfileSessionKey() {
    return profileSessionKey;
  }

  public Long getPageId() {
    return pageId;
  }

  public boolean isPageAdded() {
    return pageAdded;
  }

  public boolean isInstalled() {
    return installed;
  }

  public boolean isInProfileTab() {
    return inProfileTab;
  }

  public boolean isInCanvas() {
    return inCanvas;
  }

  public Long getCanvasUser() {
    return canvasUser;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy