private Map<String, String> loadAllToks(final String code, int port, final HttpClient httpClient) throws IOException { final HttpPost post = new HttpPost("https://accounts.google.com/o/oauth2/token"); try { final List<? extends NameValuePair> nvps = Arrays.asList( new BasicNameValuePair("code", code), new BasicNameValuePair("client_id", model.getSettings().getClientID()), new BasicNameValuePair("client_secret", model.getSettings().getClientSecret()), new BasicNameValuePair("redirect_uri", OauthUtils.getRedirectUrl(port)), new BasicNameValuePair("grant_type", "authorization_code")); final HttpEntity entity = new UrlEncodedFormEntity(nvps, LanternConstants.UTF8); post.setEntity(entity); log.debug("About to execute post!"); final HttpResponse response = httpClient.execute(post); log.debug("Got response status: {}", response.getStatusLine()); final HttpEntity responseEntity = response.getEntity(); final String body = IOUtils.toString(responseEntity.getContent()); EntityUtils.consume(responseEntity); final Map<String, String> oauthToks = JsonUtils.OBJECT_MAPPER.readValue(body, Map.class); log.debug("Got oath data: {}", oauthToks); return oauthToks; } finally { post.reset(); } }
@Subscribe public void onConnectivityChanged(final ConnectivityChangedEvent e) { Connectivity connectivity = model.getConnectivity(); if (!e.isConnected()) { connectivity.setInternet(false); Events.sync(SyncPath.CONNECTIVITY_INTERNET, false); return; } InetAddress ip = e.getNewIp(); connectivity.setIp(ip.getHostAddress()); connectivity.setInternet(true); Events.sync(SyncPath.CONNECTIVITY, model.getConnectivity()); Settings set = model.getSettings(); if (set.getMode() == null || set.getMode() == Mode.unknown) { if (censored.isCensored()) { set.setMode(Mode.get); } else { set.setMode(Mode.give); } } else if (set.getMode() == Mode.give && censored.isCensored()) { // want to set the mode to get now so that we don't mistakenly // proxy any more than necessary set.setMode(Mode.get); log.info("Disconnected; setting giveModeForbidden"); Events.syncModal(model, Modal.giveModeForbidden); } }
@Inject public LogglyHelper(Model model) { this.model = model; this.loggly = new Loggly( model.isDev(), model.getSettings().getMode() == Mode.get ? LanternConstants.LANTERN_LOCALHOST_ADDR : null); }
public void submit(String json) { String reporterId = "(" + model.getInstanceId() + ")"; String email = model.getProfile().getEmail(); if (!StringUtils.isBlank(email)) { reporterId = email + " " + reporterId; } LogglyMessage msg = new LogglyMessage(reporterId, "Lantern Feedback", new Date()) .setExtraFromJson(json) .sanitized(false); loggly.log(msg); LOG.info("submitted to Loggly: %s", json); }
/** * Make sure we're getting messages back from the controller. * * @throws Exception If anything goes wrong. */ @Test public void testControllerMessages() throws Exception { this.closedBetaEvent = null; final Censored censored = new DefaultCensored(); final CountryService countryService = new CountryService(censored); final Model model = new Model(countryService); // .getModel(); final org.lantern.state.Settings settings = model.getSettings(); settings.setMode(Mode.get); settings.setAccessToken(TestingUtils.accessToken()); settings.setRefreshToken(TestingUtils.getRefreshToken()); settings.setUseGoogleOAuth2(true); TestingUtils.doWithGetModeProxy( new Callable<Void>() { @Override public Void call() throws Exception { final XmppHandler handler = TestingUtils.newXmppHandler(censored, model); // handler.start(); // The handler could have already been created and connected, so // make sure we disconnect. handler.disconnect(); handler.connect(); assertTrue(handler.isLoggedIn()); LOG.debug("Checking for proxies in settings: {}", settings); int count = 0; while (closedBetaEvent == null && count < 200) { Thread.sleep(120); count++; } handler.stop(); return null; } }); assertTrue("Should have received event from the controller", this.closedBetaEvent != null); }
@Test public void testMessages() throws Exception { final Model model = new Model(); final Messages msgs = new Messages(model); assertEquals(0, model.getNotifications().size()); final String email = "*****@*****.**"; msgs.info(MessageKey.ALREADY_ADDED, email); int tries = 0; while (tries < 30) { if (model.getNotifications().size() > 0) { break; } Thread.sleep(50); tries++; } assertEquals(1, model.getNotifications().size()); final Notification note = model.getNotifications().get(new Integer(0)); assertEquals("You have already added [email protected].", note.getMessage()); }
private boolean handleClose(String json) { if (StringUtils.isBlank(json)) { return false; } final ObjectMapper om = new ObjectMapper(); Map<String, Object> map; try { map = om.readValue(json, Map.class); final String notification = (String) map.get("notification"); model.closeNotification(Integer.parseInt(notification)); Events.sync(SyncPath.NOTIFICATIONS, model.getNotifications()); return true; } catch (JsonParseException e) { log.warn("Exception closing notifications {}", e); } catch (JsonMappingException e) { log.warn("Exception closing notifications {}", e); } catch (IOException e) { log.warn("Exception closing notifications {}", e); } return false; }
@Subscribe public void onLocationChanged(final LocationChangedEvent e) { Events.sync(SyncPath.LOCATION, e.getNewLocation()); if (censored.isCountryCodeCensored(e.getNewCountry())) { if (!censored.isCountryCodeCensored(e.getOldCountry())) { // moving from uncensored to censored if (model.getSettings().getMode() == Mode.give) { Events.syncModal(model, Modal.giveModeForbidden); } } } }
public static void load(final boolean start) { if (loaded) { LOG.warn("ALREADY LOADED. HOW THE HECK DOES SUREFIRE CLASSLOADING WORK?"); if (!started) { start(start); } return; } loaded = true; final Module lm = newTestLanternModule(); injector = Guice.createInjector(lm); xmppHandler = instance(DefaultXmppHandler.class); socketsUtil = instance(LanternSocketsUtil.class); ksm = instance(LanternKeyStoreManager.class); lanternXmppUtil = instance(LanternXmppUtil.class); localCipherProvider = instance(LocalCipherProvider.class); encryptedFileService = instance(EncryptedFileService.class); model = instance(Model.class); jettyLauncher = instance(JettyLauncher.class); messageService = instance(MessageService.class); statsTracker = instance(ClientStats.class); roster = instance(Roster.class); modelService = instance(ModelService.class); proxifier = instance(Proxifier.class); modelUtils = instance(ModelUtils.class); modelIo = instance(ModelIo.class); proxyTracker = instance(DefaultProxyTracker.class); trustStore = instance(LanternTrustStore.class); httpClientFactory = instance(HttpClientFactory.class); geoIpLookupService = instance(GeoIpLookupService.class); countryService = instance(CountryService.class); final Settings set = model.getSettings(); LOG.debug("setting oauth token values..."); LOG.debug("secure env vars available? {}", System.getenv("TRAVIS_SECURE_ENV_VARS")); set.setAccessToken(getAccessToken()); set.setRefreshToken(getRefreshToken()); set.setUseGoogleOAuth2(true); start(start); }
/** * Convenience method for syncing the current modal with the frontend. * * @param model The state model. */ public static void syncModal(final Model model) { Events.asyncEventBus().post(new SyncEvent(SyncPath.MODAL, model.getModal())); }
/** * Convenience method for syncing a new modal both with the state model and with the frontend. * * @param model The state model. * @param modal The modal to set. */ public static void syncModal(final Model model, final Modal modal) { model.setModal(modal); syncModal(model); }
private void handleReset() { // This posts the reset event to any classes that need to take action, // avoiding coupling this class to those classes. Events.eventBus().post(new ResetEvent()); if (LanternClientConstants.DEFAULT_MODEL_FILE.isFile()) { try { FileUtils.forceDelete(LanternClientConstants.DEFAULT_MODEL_FILE); } catch (final IOException e) { log.warn("Could not delete model file?"); } } final Model base = new Model(model.getCountryService()); model.setEverGetMode(false); model.setLaunchd(base.isLaunchd()); model.setModal(base.getModal()); model.setNodeId(base.getNodeId()); model.setProfile(base.getProfile()); model.setNproxiedSitesMax(base.getNproxiedSitesMax()); // we need to keep clientID and clientSecret, because they are application-level settings String clientID = model.getSettings().getClientID(); String clientSecret = model.getSettings().getClientSecret(); model.setSettings(base.getSettings()); model.getSettings().setClientID(clientID); model.getSettings().setClientSecret(clientSecret); model.setSetupComplete(base.isSetupComplete()); model.setShowVis(base.isShowVis()); // model.setFriends(base.getFriends()); model.clearNotifications(); modelIo.write(); }
protected void processRequest(final HttpServletRequest req, final HttpServletResponse resp) { LanternUtils.addCSPHeader(resp); final String uri = req.getRequestURI(); log.debug("Received URI: {}", uri); final String interactionStr = StringUtils.substringAfterLast(uri, "/"); if (StringUtils.isBlank(interactionStr)) { log.debug("blank interaction"); HttpUtils.sendClientError(resp, "blank interaction"); return; } log.debug("Headers: " + HttpUtils.getRequestHeaders(req)); if (!"XMLHttpRequest".equals(req.getHeader("X-Requested-With"))) { log.debug("invalid X-Requested-With"); HttpUtils.sendClientError(resp, "invalid X-Requested-With"); return; } if (!SecurityUtils.constantTimeEquals(model.getXsrfToken(), req.getHeader("X-XSRF-TOKEN"))) { log.debug( "X-XSRF-TOKEN wrong: got {} expected {}", req.getHeader("X-XSRF-TOKEN"), model.getXsrfToken()); HttpUtils.sendClientError(resp, "invalid X-XSRF-TOKEN"); return; } final int cl = req.getContentLength(); String json = ""; if (cl > 0) { try { json = IOUtils.toString(req.getInputStream()); } catch (final IOException e) { log.error("Could not parse json?"); } } log.debug("Body: '" + json + "'"); final Interaction inter = Interaction.valueOf(interactionStr.toUpperCase()); if (inter == Interaction.CLOSE) { if (handleClose(json)) { return; } } if (inter == Interaction.URL) { final String url = JsonUtils.getValueFromJson("url", json); if (!StringUtils.startsWith(url, "http://") && !StringUtils.startsWith(url, "https://")) { log.error("http(s) url expected, got {}", url); HttpUtils.sendClientError(resp, "http(s) urls only"); return; } try { new URL(url); } catch (MalformedURLException e) { log.error("invalid url: {}", url); HttpUtils.sendClientError(resp, "invalid url"); return; } final String cmd; if (SystemUtils.IS_OS_MAC_OSX) { cmd = "open"; } else if (SystemUtils.IS_OS_LINUX) { cmd = "gnome-open"; } else if (SystemUtils.IS_OS_WINDOWS) { cmd = "start"; } else { log.error("unsupported OS"); HttpUtils.sendClientError(resp, "unsupported OS"); return; } try { if (SystemUtils.IS_OS_WINDOWS) { // On Windows, we have to quote the url to allow for // e.g. ? and & characters in query string params. // To quote the url, we supply a dummy first argument, // since otherwise start treats the first argument as a // title for the new console window when it's quoted. LanternUtils.runCommand(cmd, "\"\"", "\"" + url + "\""); } else { // on OS X and Linux, special characters in the url make // it through this call without our having to quote them. LanternUtils.runCommand(cmd, url); } } catch (IOException e) { log.error("open url failed"); HttpUtils.sendClientError(resp, "open url failed"); return; } return; } final Modal modal = this.model.getModal(); log.debug( "processRequest: modal = {}, inter = {}, mode = {}", modal, inter, this.model.getSettings().getMode()); if (handleExceptionalInteractions(modal, inter, json)) { return; } Modal switchTo = null; try { // XXX a map would make this more robust switchTo = Modal.valueOf(interactionStr); } catch (IllegalArgumentException e) { } if (switchTo != null && switchModals.contains(switchTo)) { if (!switchTo.equals(modal)) { if (!switchModals.contains(modal)) { this.internalState.setLastModal(modal); } Events.syncModal(model, switchTo); } return; } switch (modal) { case welcome: this.model.getSettings().setMode(Mode.unknown); switch (inter) { case GET: log.debug("Setting get mode"); handleSetModeWelcome(Mode.get); break; case GIVE: log.debug("Setting give mode"); handleSetModeWelcome(Mode.give); break; } break; case authorize: log.debug("Processing authorize modal..."); this.internalState.setModalCompleted(Modal.authorize); this.internalState.advanceModal(null); break; case finished: this.internalState.setCompletedTo(Modal.finished); switch (inter) { case CONTINUE: log.debug("Processing continue"); this.model.setShowVis(true); Events.sync(SyncPath.SHOWVIS, true); this.internalState.setModalCompleted(Modal.finished); this.internalState.advanceModal(null); break; case SET: log.debug("Processing set in finished modal...applying JSON\n{}", json); applyJson(json); break; default: log.error("Did not handle interaction {} for modal {}", inter, modal); HttpUtils.sendClientError( resp, "Interaction not handled for modal: " + modal + " and interaction: " + inter); break; } break; case firstInviteReceived: log.error("Processing invite received..."); break; case lanternFriends: this.internalState.setCompletedTo(Modal.lanternFriends); switch (inter) { case FRIEND: this.friender.addFriend(email(json)); break; case REJECT: this.friender.removeFriend(email(json)); break; case CONTINUE: // This dialog always passes continue as of this writing and // not close. case CLOSE: log.debug("Processing continue/close for friends dialog"); if (this.model.isSetupComplete()) { Events.syncModal(model, Modal.none); } else { this.internalState.setModalCompleted(Modal.lanternFriends); this.internalState.advanceModal(null); } break; default: log.error("Did not handle interaction {} for modal {}", inter, modal); HttpUtils.sendClientError( resp, "Interaction not handled for modal: " + modal + " and interaction: " + inter); break; } break; case none: break; case notInvited: switch (inter) { case RETRY: Events.syncModal(model, Modal.authorize); break; // not currently implemented: // case REQUESTINVITE: // Events.syncModal(model, Modal.requestInvite); // break; default: log.error("Unexpected interaction: " + inter); break; } break; case proxiedSites: this.internalState.setCompletedTo(Modal.proxiedSites); switch (inter) { case CONTINUE: if (this.model.isSetupComplete()) { Events.syncModal(model, Modal.none); } else { this.internalState.setModalCompleted(Modal.proxiedSites); this.internalState.advanceModal(null); } break; case LANTERNFRIENDS: log.debug("Processing lanternFriends from proxiedSites"); Events.syncModal(model, Modal.lanternFriends); break; case SET: if (!model.getSettings().isSystemProxy()) { String msg = "Because you are using manual proxy " + "configuration, you may have to restart your " + "browser for your updated proxied sites list " + "to take effect."; model.addNotification(msg, MessageType.info, 30); Events.sync(SyncPath.NOTIFICATIONS, model.getNotifications()); } applyJson(json); break; case SETTINGS: log.debug("Processing settings from proxiedSites"); Events.syncModal(model, Modal.settings); break; default: log.error("Did not handle interaction {} for modal {}", inter, modal); HttpUtils.sendClientError(resp, "unexpected interaction for proxied sites"); break; } break; case requestInvite: log.info("Processing request invite"); switch (inter) { case CANCEL: this.internalState.setModalCompleted(Modal.requestInvite); this.internalState.advanceModal(Modal.notInvited); break; case CONTINUE: applyJson(json); this.internalState.setModalCompleted(Modal.proxiedSites); // TODO: need to do something here this.internalState.advanceModal(null); break; default: log.error("Did not handle interaction {} for modal {}", inter, modal); HttpUtils.sendClientError(resp, "unexpected interaction for request invite"); break; } break; case requestSent: log.debug("Process request sent"); break; case settings: switch (inter) { case GET: log.debug("Setting get mode"); // Only deal with a mode change if the mode has changed! if (modelService.getMode() == Mode.give) { // Break this out because it's set in the subsequent // setMode call final boolean everGet = model.isEverGetMode(); this.modelService.setMode(Mode.get); if (!everGet) { // need to do more setup to switch to get mode from // give mode model.setSetupComplete(false); model.setModal(Modal.proxiedSites); Events.syncModel(model); } else { // This primarily just triggers a setup complete event, // which triggers connecting to proxies, setting up // the local system proxy, etc. model.setSetupComplete(true); } } break; case GIVE: log.debug("Setting give mode"); this.modelService.setMode(Mode.give); break; case CLOSE: log.debug("Processing settings close"); Events.syncModal(model, Modal.none); break; case SET: log.debug("Processing set in setting...applying JSON\n{}", json); applyJson(json); break; case RESET: log.debug("Processing reset"); Events.syncModal(model, Modal.confirmReset); break; case PROXIEDSITES: log.debug("Processing proxied sites in settings"); Events.syncModal(model, Modal.proxiedSites); break; case LANTERNFRIENDS: log.debug("Processing friends in settings"); Events.syncModal(model, Modal.lanternFriends); break; default: log.error("Did not handle interaction {} for modal {}", inter, modal); HttpUtils.sendClientError( resp, "Interaction not handled for modal: " + modal + " and interaction: " + inter); break; } break; case settingsLoadFailure: switch (inter) { case RETRY: modelIo.reload(); Events.sync(SyncPath.NOTIFICATIONS, model.getNotifications()); Events.syncModal(model, model.getModal()); break; case RESET: backupSettings(); Events.syncModal(model, Modal.welcome); break; default: log.error("Did not handle interaction {} for modal {}", inter, modal); break; } break; case systemProxy: this.internalState.setCompletedTo(Modal.systemProxy); switch (inter) { case CONTINUE: log.debug("Processing continue in systemProxy", json); applyJson(json); Events.sync(SyncPath.SYSTEMPROXY, model.getSettings().isSystemProxy()); this.internalState.setModalCompleted(Modal.systemProxy); this.internalState.advanceModal(null); break; default: log.error("Did not handle interaction {} for modal {}", inter, modal); HttpUtils.sendClientError(resp, "error setting system proxy pref"); break; } break; case updateAvailable: switch (inter) { case CLOSE: this.internalState.setModalCompleted(Modal.updateAvailable); this.internalState.advanceModal(null); break; default: log.error("Did not handle interaction {} for modal {}", inter, modal); break; } break; case authorizeLater: log.error("Did not handle interaction {} for modal {}", inter, modal); break; case confirmReset: log.debug("Handling confirm reset interaction"); switch (inter) { case CANCEL: log.debug("Processing cancel"); Events.syncModal(model, Modal.settings); break; case RESET: handleReset(); Events.syncModel(this.model); break; default: log.error("Did not handle interaction {} for modal {}", inter, modal); HttpUtils.sendClientError( resp, "Interaction not handled for modal: " + modal + " and interaction: " + inter); } break; case about: // fall through on purpose case sponsor: switch (inter) { case CLOSE: Events.syncModal(model, this.internalState.getLastModal()); break; default: HttpUtils.sendClientError(resp, "invalid interaction " + inter); } break; case contact: switch (inter) { case CONTINUE: String msg; MessageType messageType; try { lanternFeedback.submit(json, this.model.getProfile().getEmail()); msg = "Thank you for contacting Lantern."; messageType = MessageType.info; } catch (Exception e) { log.error("Error submitting contact form: {}", e); msg = "Error sending message. Please check your " + "connection and try again."; messageType = MessageType.error; } model.addNotification(msg, messageType, 30); Events.sync(SyncPath.NOTIFICATIONS, model.getNotifications()); // fall through because this should be done in both cases: case CANCEL: Events.syncModal(model, this.internalState.getLastModal()); break; default: HttpUtils.sendClientError(resp, "invalid interaction " + inter); } break; case giveModeForbidden: if (inter == Interaction.CONTINUE) { // need to do more setup to switch to get mode from give mode model.getSettings().setMode(Mode.get); model.setSetupComplete(false); this.internalState.advanceModal(null); Events.syncModal(model, Modal.proxiedSites); Events.sync(SyncPath.SETUPCOMPLETE, false); } break; default: log.error("No matching modal for {}", modal); } this.modelIo.write(); }
protected synchronized void addMapping( final PortMappingProtocol prot, final int externalPortRequested, int localPort, final PortMapListener portMapListener) { ByteBuffer lanaddr = ByteBuffer.allocate(16); ByteBuffer intClient = ByteBuffer.allocate(16); ByteBuffer intPort = ByteBuffer.allocate(6); ByteBuffer desc = ByteBuffer.allocate(80); ByteBuffer enabled = ByteBuffer.allocate(4); ByteBuffer leaseDuration = ByteBuffer.allocate(16); int ret; final UPNPUrls urls = new UPNPUrls(); final IGDdatas data = new IGDdatas(); UPNPDev devlist = MiniupnpcLibrary.INSTANCE.upnpDiscover( UPNP_DELAY, (String) null, (String) null, 0, 0, IntBuffer.allocate(1)); if (devlist == null) { MiniupnpcLibrary.INSTANCE.FreeUPNPUrls(urls); portMapListener.onPortMapError(); return; } ret = MiniupnpcLibrary.INSTANCE.UPNP_GetValidIGD(devlist, urls, data, lanaddr, 16); if (ret == 0) { log.debug("No valid UPNP Internet Gateway Device found."); portMapListener.onPortMapError(); MiniupnpcLibrary.INSTANCE.FreeUPNPUrls(urls); devlist.setAutoRead(false); MiniupnpcLibrary.INSTANCE.freeUPNPDevlist(devlist); return; } try { logIGDResponse(ret, urls); log.debug("Local LAN ip address : " + zeroTerminatedString(lanaddr.array())); ByteBuffer externalAddress = ByteBuffer.allocate(16); MiniupnpcLibrary.INSTANCE.UPNP_GetExternalIPAddress( urls.controlURL.getString(0), zeroTerminatedString(data.first.servicetype), externalAddress); publicIp = zeroTerminatedString(externalAddress.array()); log.debug("ExternalIPAddress = " + publicIp); ret = MiniupnpcLibrary.INSTANCE.UPNP_AddPortMapping( urls.controlURL.getString(0), // controlURL zeroTerminatedString(data.first.servicetype), // servicetype "" + externalPortRequested, // external Port "" + localPort, // internal Port zeroTerminatedString(lanaddr.array()), // internal client "added via MiniupnpcLibrary.INSTANCE/JAVA !", // description prot.toString(), // protocol UDP or TCP null, // remote host (useless) "0"); // leaseDuration if (ret != MiniupnpcLibrary.UPNPCOMMAND_SUCCESS) { portMapListener.onPortMapError(); return; } // get the local port (but didn't we request one?) ret = MiniupnpcLibrary.INSTANCE.UPNP_GetSpecificPortMappingEntry( urls.controlURL.getString(0), zeroTerminatedString(data.first.servicetype), "" + externalPortRequested, prot.toString(), intClient, intPort, desc, enabled, leaseDuration); log.debug( "InternalIP:Port = " + zeroTerminatedString(intClient.array()) + ":" + zeroTerminatedString(intPort.array()) + " (" + zeroTerminatedString(desc.array()) + ")"); model.getInstanceStats().setUsingUPnP(true); final UpnpMapping mapping = new UpnpMapping(); mapping.prot = prot; mapping.internalPort = localPort; mapping.externalPort = externalPortRequested; mappings.add(mapping); log.debug("Added mapping. Mappings now: {}", mappings); } finally { MiniupnpcLibrary.INSTANCE.FreeUPNPUrls(urls); devlist.setAutoRead(false); MiniupnpcLibrary.INSTANCE.freeUPNPDevlist(devlist); } portMapListener.onPortMap(externalPortRequested); }