func PostUserSettings(w rest.ResponseWriter, r *rest.Request) { form := UserSettings{} err := r.DecodeJsonPayload(&form) if err != nil { apiutils.WriteRestError(w, apierrors.NewBadRequest(err.Error())) return } changed := false // true if config really was updated err = clientconfig.Update(func(conf *clientconfig.Config) error { s := &conf.Settings.Local prevLang := s.Language if ValidLanguage(form.Language) { s.Language = form.Language } else { s.Language = "en" } if prevLang != s.Language { ui.Language(s.Language) changed = true } if s.CountryCode != form.CountryCode { s.CountryCode = form.CountryCode changed = true } if s.ClientAutoUpdate != form.ClientAutoUpdate { s.ClientAutoUpdate = form.ClientAutoUpdate changed = true } if s.BlocklistAutoUpdate != form.BlocklistAutoUpdate { s.BlocklistAutoUpdate = form.BlocklistAutoUpdate changed = true } return nil }) if changed { err := clientconfig.Write() if err != nil { lg.Errorln(err) } } if err != nil { lg.Errorln(err) } }
func DeleteConnection(w rest.ResponseWriter, r *rest.Request) { ID := r.PathParam("id") err := clientconfig.Update(func(conf *clientconfig.Config) error { all := conf.Settings.Connections var result []shared.Connection // validate that the id exists found := false nEnabled := 0 for _, v := range all { if v.ID == ID { found = true if v.Protected { apiutils.WriteRestError(w, apierrors.NewForbidden("connection", ID, nil)) return nil } } else { if !v.Disabled { nEnabled += 1 } result = append(result, v) } } if !found { apiutils.WriteRestError(w, apierrors.NewNotFound("connection", ID)) return nil } if nEnabled < 1 { apiutils.WriteRestError(w, apierrors.NewForbidden("connection", ID, errors.New("one connection must be enabled"))) return nil } service.UpdateConnections(result) conf.Settings.Connections = result return nil }) if err != nil { lg.Errorln(err) } clientconfig.Write() w.WriteJson(true) }
func ToggleConnection(w rest.ResponseWriter, r *rest.Request) { ID := r.PathParam("id") err := clientconfig.Update(func(conf *clientconfig.Config) error { // validate that the id exists, if supplied all := conf.Settings.Connections foundIdx := 0 found := false nEnabled := 0 for k, v := range all { if !v.Disabled { nEnabled += 1 } if v.ID == ID { found = true foundIdx = k } } if !found { apiutils.WriteRestError(w, apierrors.NewNotFound("connection", ID)) return nil } if nEnabled < 2 && !conf.Settings.Connections[foundIdx].Disabled { apiutils.WriteRestError(w, apierrors.NewForbidden("connection", ID, errors.New("one connection must be enabled"))) return nil } conf.Settings.Connections[foundIdx].Disabled = !conf.Settings.Connections[foundIdx].Disabled service.UpdateConnections(conf.Settings.Connections) return nil }) if err != nil { lg.Errorln(err) } clientconfig.Write() w.WriteJson(true) }
// Init does precondition check if the application can/should be started. // Init will return an error message with reason for exit printed. func Run() { if debugEnabled { log.Println("ALKASIR_DEBUG ENABLED!") err := os.Setenv("ALKASIR_DEBUG", "1") if err != nil { log.Fatal(err) } } if hotEnabled { log.Println("ALKASIR_HOT ENABLED!") err := os.Setenv("ALKASIR_HOT", "1") if err != nil { log.Fatal(err) } } // the darwin systray does not exit the main loop if runtime.GOOS != "darwin" { uiRunning.Add(1) } err := ui.Run(func() { Atexit(ui.Done) // start the getpublic ip updater. go func() { _ = shared.GetPublicIPAddr() }() if debugEnabled { go func() { err := http.ListenAndServe( fmt.Sprintf("localhost:%d", debugPort), nil) if err != nil { panic(err) } }() } // wipe user data if wipeData { settingsdir := clientconfig.ConfigPath() if settingsdir == "" { log.Println("[wipe] Configdir not set") os.Exit(1) } settingsfile := clientconfig.ConfigPath("settings.json") if _, err := os.Stat(settingsfile); os.IsNotExist(err) { log.Println("[wipe] No settings.json in configdir, will NOT wipe data") os.Exit(1) } log.Println("Wiping all user data") if err := os.RemoveAll(settingsdir); err != nil { log.Println(err) } } // Prepare logging logdir := clientconfig.ConfigPath("log") err := os.MkdirAll(logdir, 0775) if err != nil { log.Println("Could not create logging directory") os.Exit(1) } err = flag.Set("log_dir", logdir) if err != nil { panic(err) } lg.SetSrcHighlight("alkasir/cmd", "alkasir/pkg") lg.CopyStandardLogTo("INFO") // Start init if VERSION != "" { lg.Infoln("Alkasir v" + VERSION) } else { lg.Warningln("Alkasir dev version (VERSION not set)") } lg.V(1).Info("Log v-level:", lg.Verbosity()) _, err = clientconfig.Read() if err != nil { lg.Infoln("Could not read config") exit() } lg.V(30).Infoln("settings", clientconfig.Get().Settings) if saveChromeExt { err := saveChromeExtension() if err != nil { lg.Fatal(err) } } { configChanged, err := clientconfig.UpgradeConfig() if err != nil { lg.Fatalln("Could not upgrade config", err) } clientconfig.Update(func(conf *clientconfig.Config) error { if clientAuthKeyFlag != "" { lg.Warningln("Overriding generated authKey with", clientAuthKeyFlag) conf.Settings.Local.ClientAuthKey = clientAuthKeyFlag configChanged = true } if bindAddrFlag != "" { lg.Warningln("Overriding configured bindAddr with", bindAddrFlag) conf.Settings.Local.ClientBindAddr = bindAddrFlag configChanged = true } if centralAddrFlag != "" { lg.Warningln("Overriding central server addr with", centralAddrFlag) conf.Settings.Local.CentralAddr = centralAddrFlag configChanged = true } return nil }) if configChanged { if err := clientconfig.Write(); err != nil { lg.Warning(err) } } } conf := clientconfig.Get() loadTranslations(LanguageOptions...) if err := ui.Language(conf.Settings.Local.Language); err != nil { lg.Warningln(err) } go func() { select { case <-sigIntC: exit() case <-ui.Actions.Quit: exit() } }() for _, e := range []error{ mime.AddExtensionType(".json", "application/json"), mime.AddExtensionType(".js", "application/javascript"), mime.AddExtensionType(".css", "text/css"), mime.AddExtensionType(".md", "text/plain"), } { if e != nil { lg.Warning(e) } } err = startInternalHTTPServer(conf.Settings.Local.ClientAuthKey) if err != nil { lg.Fatal("could not start internal http services") } // Connect the default transport service.UpdateConnections(conf.Settings.Connections) service.UpdateTransports(conf.Settings.Transports) go service.StartConnectionManager(conf.Settings.Local.ClientAuthKey) // TODO: async pac.UpdateDirectList(conf.DirectHosts.Hosts) pac.UpdateBlockedList(conf.BlockedHostsCentral.Hosts, conf.BlockedHosts.Hosts) lastBlocklistChange = time.Now() go StartBlocklistUpgrader() if upgradeDiffsBaseURL != "" { lg.V(19).Infoln("upgradeDiffsBaseURL is ", upgradeDiffsBaseURL) go StartBinaryUpgradeChecker(upgradeDiffsBaseURL) } else { lg.Warningln("empty upgradeDiffsBaseURL, disabling upgrade checks") } lg.V(5).Info("Alkasir has started") }) // the darwin systray does not exit the main loop if runtime.GOOS != "darwin" { uiRunning.Done() } lg.Infoln("ui.Run ended") if err != nil { log.Println("client.Run error:", err) } }
// SubmitSuggestion initiates the comminication with Central for a Submission // session. func SubmitSuggestion(w rest.ResponseWriter, r *rest.Request) { // // TODO This is the response that must be forwarded from central/api and parsed by client and passed on to the browser. // apiutils.WriteRestError(w, // apierrors.NewInvalid("object", "suggestion", // fielderrors.ValidationErrorList{ // fielderrors.NewFieldValueNotSupported("URL", "...", []string{})})) // return ID := r.PathParam("id") suggestion, ok := client.GetSuggestion(ID) if !ok { apiutils.WriteRestError(w, apierrors.NewNotFound("suggestion", ID)) return } wanip := shared.GetPublicIPAddr() if wanip == nil { lg.Warning("could not resolve public ip addr") } conf := clientconfig.Get() restclient, err := NewRestClient() if err != nil { apiutils.WriteRestError(w, apierrors.NewInternalError(err)) return } tokenResp, err := suggestion.RequestToken( restclient, wanip, conf.Settings.Local.CountryCode) if err != nil { if apiutils.IsNetError(err) { apiutils.WriteRestError(w, apierrors.NewServerTimeout("alkasir-central", "request-submission-token", 0)) } else { apiutils.WriteRestError(w, apierrors.NewInternalError(err)) } return } n, err := suggestion.SendSamples(restclient) if err != nil { lg.Warningln("error sending samples", err.Error()) } lg.V(5).Infof("sent %d samples", n) // continue sending samples if future measuremetns are expected to come prepared, err := suggestion.Prepared() if err != nil { lg.Errorln(err) } else if !prepared { lg.V(5).Infof("all samples not collected, will try to send the rest when they are done") go func(s client.Suggestion) { start := time.Now() t := time.NewTicker(30 * time.Second) defer t.Stop() submitSamples: for range t.C { if time.Now().After(start.Add(15 * time.Minute)) { lg.Errorln("Stopping trying to send additional samples") return } prepared, err := suggestion.Prepared() if err != nil { lg.Errorln(err) return } if prepared { restclient, err := NewRestClient() if err != nil { continue submitSamples } n, err := suggestion.SendSamples(restclient) lg.V(5).Infof("sent %d samples", n) if err != nil { lg.Warningln("error sending samples", err.Error()) continue submitSamples } return } } }(suggestion) } u, err := url.Parse(suggestion.URL) if err != nil { lg.Errorln(err) } else { err := clientconfig.Update(func(conf *clientconfig.Config) error { conf.BlockedHosts.Add(u.Host) lastBlocklistChange = time.Now() pac.UpdateBlockedList(conf.BlockedHostsCentral.Hosts, conf.BlockedHosts.Hosts) return nil }) if err != nil { lg.Errorln(err) } } w.WriteJson(tokenResp) }
func PostConnection(w rest.ResponseWriter, r *rest.Request) { form := ConnectionSetting{} err := r.DecodeJsonPayload(&form) if err != nil { apiutils.WriteRestError(w, apierrors.NewBadRequest(err.Error())) return } err = clientconfig.Update(func(conf *clientconfig.Config) error { all := conf.Settings.Connections var connection shared.Connection // decoded connection var invalids fielderrors.ValidationErrorList // Verify that the encoded field is set { if form.Encoded == "" { invalids = append(invalids, fielderrors.NewFieldRequired("encoded")) } } // verify that the connection is decodeable { var err error connection, err = shared.DecodeConnection(form.Encoded) if err != nil { invalids = append(invalids, fielderrors.NewFieldInvalid("encoded", form.Encoded, "invalid formatting")) } } // validate that the id exists, if supplied foundIdx := 0 { if form.ID != "" { found := false for k, v := range all { if v.ID == form.ID { found = true foundIdx = k } } if !found { invalids = append(invalids, fielderrors.NewFieldNotFound("id", form.ID)) } } } // validate that the connection doesnt alreay exist { encoded, err := connection.Encode() if err != nil { apiutils.WriteRestError(w, err) return nil } found := false for _, v := range all { enc2, err := v.Encode() if err != nil { lg.Errorln(err) continue } if enc2 == encoded { found = true } } if found { invalids = append(invalids, fielderrors.NewFieldDuplicate("encoded", form.Encoded)) } } // end of field validations if len(invalids) > 0 { apiutils.WriteRestError(w, apierrors.NewInvalid("post-connection", "", invalids)) return nil } // add connection to settings and save if form.ID == "" { connection.EnsureID() conf.Settings.Connections = append(conf.Settings.Connections, connection) } else { conf.Settings.Connections[foundIdx] = connection } service.UpdateConnections(conf.Settings.Connections) return nil }) if err != nil { lg.Errorln(err) } clientconfig.Write() w.WriteJson(true) }