/*
 * Copyright 2015 the original author or authors.
 *
 * 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.springframework.social.facebook.web;

import static org.springframework.web.bind.annotation.RequestMethod.*;

import java.util.List;
import java.util.Map;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.crypto.codec.Hex;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * A Spring MVC controller that handles callbacks from Facebook's Real-Time Update API.
 * Handles both the initial subscription verification request as well as individual updates:
 * The following requests are handled:
 * <ul>
 * <li>GET /realtime/facebook/{subscription}  - Verifies subscription.</li>
 * <li>POST /realtime/facebook/{subscription} - Receives an update.</li>
 * </ul>
 * 
 * Note that these requests are performed by Facebook and are not typically linked to or otherwise called in a web application.
 * @author Craig Walls
 */
@Controller
@RequestMapping("/realtime/facebook")
public class RealTimeUpdateController {

	private Map<String, String> tokens;
	
	private List<UpdateHandler> updateHandlers;

	private String applicationSecret;

	/**
	 * Constructs a RealTimeUpdateController.
	 * @param tokens A map of subscription names to verification tokens.
	 * @param updateHandlers A list of {@link UpdateHandler} implementations to handle incoming updates.
	 * @param applicationSecret the application's Facebook App Secret
	 */
	public RealTimeUpdateController(Map<String, String> tokens, List<UpdateHandler> updateHandlers, String applicationSecret) {
		this.tokens = tokens;
		this.updateHandlers = updateHandlers;
		this.applicationSecret = applicationSecret;
	}

	/**
	 * Handles subscription verification callback from Facebook.
	 * @param subscription The subscription name.
	 * @param challenge A challenge that Facebook expects to be returned.
	 * @param verifyToken A verification token that must match with the subscription's token given when the controller was created.
	 * @return The challenge if the verification token matches; blank string otherwise. 
	 */
	@RequestMapping(value="/{subscription}", method=GET, params="hub.mode=subscribe")
	public @ResponseBody String verifySubscription(
			@PathVariable("subscription") String subscription,
			@RequestParam("hub.challenge") String challenge,
			@RequestParam("hub.verify_token") String verifyToken) {
		logger.debug("Received subscription verification request for '" + subscription + "'.");
		return tokens.containsKey(subscription) && tokens.get(subscription).equals(verifyToken) ? challenge : "";
	}
	
	/**
	 * Receives an update from Facebook's real-time API.
	 * @param subscription The subscription name.
	 * @param payload The request body payload.
	 * @param signature The SHA1 signature of the request.
	 * @return a String with the response back to Facebook
	 * @throws Exception an Exception if anything goes wrong while processing the update
	 */
	@RequestMapping(value="/{subscription}", method=POST)
	public @ResponseBody String receiveUpdate(
			@PathVariable("subscription") String subscription,
			@RequestBody String payload,
			@RequestHeader(X_HUB_SIGNATURE) String signature) throws Exception {

		// Can only read body once and we need it as a raw String to calculate the signature.
		// Therefore, use Jackson ObjectMapper to give us a RealTimeUpdate object from that raw String.
		RealTimeUpdate update = new ObjectMapper().readValue(payload, RealTimeUpdate.class);		
		if (verifySignature(payload, signature)) {
			logger.debug("Received " + update.getObject() + " update for '" + subscription + "'.");
			for (UpdateHandler handler : updateHandlers) {
				handler.handleUpdate(subscription, update);
			}
		} else {
			logger.warn("Received an update, but signature was invalid. Not delegating to handlers.");
		}
		return "";
	}

	private boolean verifySignature(String payload, String signature) throws Exception {
		if (!signature.startsWith("sha1=")) {
			return false;
		}
		String expected = signature.substring(5);		
		Mac mac = Mac.getInstance(HMAC_SHA1_ALGORITHM);
		SecretKeySpec signingKey = new SecretKeySpec(applicationSecret.getBytes(), HMAC_SHA1_ALGORITHM);
		mac.init(signingKey);
		byte[] rawHmac = mac.doFinal(payload.getBytes());
		String actual = new String(Hex.encode(rawHmac));
		return expected.equals(actual);
	}

	private static final String HMAC_SHA1_ALGORITHM = "HmacSHA1";

	private static final String X_HUB_SIGNATURE = "X-Hub-Signature";

	private final static Log logger = LogFactory.getLog(RealTimeUpdateController.class);

}

@@ -841,16 +841,44 @@
 KeySpec;
+%0Aimport javax.inject.Inject;
 %0A%0Aimport
@@ -2535,16 +2535,25 @@
 et%0A%09 */%0A
+%09@Inject%0A
 %09public 

Add @Inject to RealTimeUpdateController