package org.fluxtream.connectors.lastfm; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang.StringUtils; import org.fluxtream.core.Configuration; import org.fluxtream.core.auth.AuthHelper; import org.fluxtream.core.connectors.Connector; import org.fluxtream.connectors.controllers.ControllerSupport; import org.fluxtream.core.domain.ApiKey; import org.fluxtream.core.domain.Guest; import org.fluxtream.core.domain.Notification; import org.fluxtream.core.services.GuestService; import org.fluxtream.core.services.NotificationsService; import org.fluxtream.core.utils.HttpUtils; import org.fluxtream.core.utils.UnexpectedHttpResponseCodeException; import org.apache.commons.io.IOUtils; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.client.HttpClient; import org.apache.http.client.ResponseHandler; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; import org.joda.time.DateTimeConstants; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import static org.fluxtream.core.utils.Utils.hash; @Controller @RequestMapping(value = "/lastfm") public class LastFmController { private static final String LASTFM_RENEWTOKEN_APIKEYID = "lastfm.renewtoken.apiKeyId"; @Autowired GuestService guestService; @Autowired Configuration env; @Autowired NotificationsService notificationsService; @Autowired ServletContext context; @RequestMapping(value = "/token") public String getToken(HttpServletRequest request) throws IOException, ServletException { String oauthCallback = ControllerSupport.getLocationBase(request, env) + "lastfm/upgradeToken"; if (request.getParameter("guestId") != null) oauthCallback += "?guestId=" + request.getParameter("guestId"); String approvalPageUrl = "http://www.last.fm/api/auth/?api_key=" + env.get("lastfmConsumerKey") + "&cb=" + oauthCallback; if (request.getParameter("apiKeyId")!=null) request.getSession().setAttribute(LASTFM_RENEWTOKEN_APIKEYID, request.getParameter("apiKeyId")); return "redirect:" + approvalPageUrl; } @RequestMapping(value = "/upgradeToken") public String upgradeToken(HttpServletRequest request) throws NoSuchAlgorithmException, IOException { String token = request.getParameter("token"); String api_key = env.get("lastfmConsumerKey"); String api_sig = getApiSig(toMap("token", token, "api_key", api_key, "method", "auth.getsession")); Map<String, String> params = toMap("method", "auth.getsession", "format", "json", "token", token, "api_key", api_key, "api_sig", api_sig); String jsonResponse; try { jsonResponse = HttpUtils.fetch("https://ws.audioscrobbler.com/2.0/", params); } catch (UnexpectedHttpResponseCodeException e) { e.printStackTrace(); notificationsService.addNamedNotification(AuthHelper.getGuestId(), Notification.Type.ERROR, Connector.getConnector("lastfm").statusNotificationName(), String.format("Oops, we couldn't link your LastFM account (reason: '%s', http code: %s)" + "<br>Please contact your administrator.", e.getHttpResponseMessage(), e.getHttpResponseCode())); return "redirect:/app"; } String sessionKey = LastfmHelper.getSessionKey(jsonResponse); String username = LastfmHelper.getUsername(jsonResponse); Guest guest = AuthHelper.getGuest(); final Connector connector = Connector.getConnector("lastfm"); ApiKey apiKey; if (request.getSession().getAttribute(LASTFM_RENEWTOKEN_APIKEYID)!=null) { final String apiKeyIdString = (String) request.getSession().getAttribute(LASTFM_RENEWTOKEN_APIKEYID); long apiKeyId = Long.valueOf(apiKeyIdString); apiKey = guestService.getApiKey(apiKeyId); } else apiKey = guestService.createApiKey(guest.getId(), connector); guestService.populateApiKey(apiKey.getId()); guestService.setApiKeyAttribute(apiKey, "sessionKey", sessionKey); guestService.setApiKeyAttribute(apiKey, "username", username); if (request.getSession().getAttribute(LASTFM_RENEWTOKEN_APIKEYID)!=null) { request.getSession().removeAttribute(LASTFM_RENEWTOKEN_APIKEYID); return "redirect:/app/tokenRenewed/"+connector.getName(); } return "redirect:/app/from/"+connector.getName(); } Map<String, String> toMap(String... params) { HashMap<String, String> map = new HashMap<String, String>(); for (int i = 0; i < params.length;) map.put(params[i++], params[i++]); return map; } String getApiSig(Map<String, String> params) throws NoSuchAlgorithmException, UnsupportedEncodingException { Object[] key = params.keySet().toArray(); Arrays.sort(key); String toHash = ""; for (int i = 0; i < key.length; i++) toHash += key[i] + new String(params.get(key[i]).getBytes(), "UTF-8"); toHash += env.get("lastfmConsumerSecret"); String hashed = hash(toHash); return hashed; } @RequestMapping(value="/img") public void getLastfmImageResource(@RequestParam("url") String url, final HttpServletResponse response) throws IOException { HttpClient client = new DefaultHttpClient(); response.setDateHeader("Expires", System.currentTimeMillis() + DateTimeConstants.MILLIS_PER_WEEK*30); try { if (StringUtils.isEmpty(url)) { String realPath = context.getRealPath("images/lastfm-404.png"); FileInputStream fis = new FileInputStream(realPath); IOUtils.copy(fis, response.getOutputStream()); return; } HttpGet get = new HttpGet(url); HttpResponse lastfmResponse = client.execute(get); if (lastfmResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { new ResponseHandler<byte[]>() { /** Returns the contents of the response as a <code>byte[]</code>. */ @Override public byte[] handleResponse(final HttpResponse lastfmResponse) throws IOException { final HttpEntity entity = lastfmResponse.getEntity(); if (entity != null) { final InputStream in = entity.getContent(); IOUtils.copy(in, response.getOutputStream()); } return new byte[0]; } }.handleResponse(lastfmResponse); } } finally { client.getConnectionManager().shutdown(); } } }