/*
 * Copyright (c) 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 it.backbox.client.oauth;

import org.mortbay.jetty.Connector;
import org.mortbay.jetty.Request;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.handler.AbstractHandler;

import java.io.IOException;
import java.io.PrintWriter;

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

/**
 * Runs a Jetty server on a free port, waiting for OAuth to redirect to it with
 * the verification code.
 * <p>
 * Mostly copied from oacurl by phopkins@google.com.
 * </p>
 *
 * @author Yaniv Inbar
 */
public final class LocalServerReceiver implements VerificationCodeReceiver {

	private static final String CALLBACK_PATH = "/Callback";
	private static final String LOCALHOST = "127.0.0.1";
	private static final int PORT = 8482;

	/** Server or {@code null} before {@link #getRedirectUri()}. */
	private Server server;

	/** Verification code or {@code null} before received. */
	volatile String code;

	@Override
	public String getRedirectUri() throws Exception {
		server = new Server(PORT);
		for (Connector c : server.getConnectors()) {
			c.setHost(LOCALHOST);
		}
		server.addHandler(new CallbackHandler());
		server.start();
		return "http://" + LOCALHOST + ":" + PORT + CALLBACK_PATH;
	}

	@Override
	public synchronized String waitForCode() {
		while (code == null)
			try {
				this.wait();
			} catch (InterruptedException exception) {
				// should not happen
			}
		return code;
	}

	@Override
	public void stop() throws Exception {
		if (server != null) {
			server.stop();
			server = null;
		}
	}

	/**
	 * Jetty handler that takes the verifier token passed over from the OAuth
	 * provider and stashes it where {@link #waitForCode} will find it.
	 */
	class CallbackHandler extends AbstractHandler {

		@Override
		public void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch)
				throws IOException {
			if (!CALLBACK_PATH.equals(target)) {
				return;
			}
			writeLandingHtml(response);
			response.flushBuffer();
			((Request) request).setHandled(true);
			String error = request.getParameter("error");
			if (error != null) {
				System.out.println("Authorization failed. Error: " + error);
				System.out.println("Quitting.");
				System.exit(1);
			}
			code = request.getParameter("code");
			synchronized (LocalServerReceiver.this) {
				LocalServerReceiver.this.notify();
			}
		}

		private void writeLandingHtml(HttpServletResponse response) throws IOException {
			response.setStatus(HttpServletResponse.SC_OK);
			response.setContentType("text/html");

			PrintWriter doc = response.getWriter();
			doc.println("<html>");
			doc.println("<head><title>OAuth 2.0 Authentication Token Recieved</title></head>");
			doc.println("<body>");
			doc.println("Received verification code. Closing...");
			doc.println("<script type='text/javascript'>");
			// We open "" in the same window to trigger JS ownership of it,
			// which lets us then close it via JS, at least in Chrome.
			doc.println("window.setTimeout(function() {");
			doc.println("    window.open('', '_self', ''); window.close(); }, 1000);");
			doc.println("if (window.opener) { window.opener.checkToken(); }");
			doc.println("</script>");
			doc.println("</body>");
			doc.println("</HTML>");
			doc.flush();
		}
	}
}

@@ -628,103 +628,151 @@
 ort 
-org.mortbay.jetty.Connecto
+java.io.IOException;%0Aimport java.io.PrintWrite
 r;%0A
+%0A
 import 
-org.mortbay.jetty.Request;%0Aimport org.mortbay.jetty.Server;
+javax.servlet.http.HttpServletRequest;%0Aimport javax.servlet.http.HttpServletResponse;%0A
 %0Aimp
@@ -797,182 +797,134 @@
 tty.
-handler.AbstractHandle
+Connecto
 r;%0A
-%0A
 import 
-java.io.IOException;%0Aimport java.io.PrintWriter;%0A%0Aimport javax.servlet.http.HttpServletRequest;%0Aimport javax.servlet.http.HttpServletResponse
+org.mortbay.jetty.Request;%0Aimport org.mortbay.jetty.Server;%0Aimport org.mortbay.jetty.handler.AbstractHandler
 ;%0A%0A/
@@ -1532,16 +1532,212 @@
 g code;%0A
+%09%0A%09/** Verification error or %7B@code null%7D before received. */%0A%09volatile String error;%0A%09%0A%09/** Verification error description or %7B@code null%7D before received. */%0A%09volatile String errorDescription;%0A%09
 %0A%09@Overr
@@ -2086,16 +2086,17 @@
 %09%09while 
+(
 (code ==
@@ -2101,16 +2101,36 @@
 == null)
+ && (error == null))
 %0A%09%09%09try 
@@ -2230,19 +2230,170 @@
 %7D%0A%09%09
-return code
+if (code != null)%0A%09%09%09return code;%0A%09%09else %0A%09%09%09throw new RuntimeException(%22Error retrieving OAuth code: %22 + ((errorDescription != null) ? errorDescription : error))
 ;%0A%09%7D
@@ -3020,23 +3020,16 @@
 ue);%0A%09%09%09
-String 
 error = 
@@ -3066,155 +3066,69 @@
 %0A%09%09%09
-if (
 error
- != null) %7B%0A%09%09%09%09System.out.println(%22Authorization failed. Error: %22 + error);%0A%09%09%09%09System.out.println(%22Quitting.%22);%0A%09%09%09%09System.exit(1);%0A%09%09%09%7D
+Description = request.getParameter(%22error_description%22);
 %0A%09%09%09

manage oauth error