// backfillConfigFile opens the config file, adds all missing keys
// and default values, then writes the config file back.
func backfillConfigFile(context *cli.Context) {
	config, cfBefore, err := lib.GetConfig(context)
	if err != nil {
		log.Fatalln(err)
	}
	if cfBefore == "" {
		fmt.Println("Could not find a config file to backfill")
		return
	}

	// Same config in []byte format.
	configRaw, err := ioutil.ReadFile(cfBefore)
	if err != nil {
		log.Fatalln(err)
	}

	// Same config in map format so that we can detect missing keys.
	var configMap map[string]interface{}
	if err = json.Unmarshal(configRaw, &configMap); err != nil {
		log.Fatalln(err)
	}

	if cfWritten, err := config.Backfill(configMap).ToFile(context); err != nil {
		fmt.Printf("Failed to write config file: %s\n", err)
	} else {
		fmt.Printf("Wrote %s\n", cfWritten)
	}
}
// getConfig returns a config object
func getConfig(context *cli.Context) *lib.Config {
	config, _, err := lib.GetConfig(context)
	if err != nil {
		log.Fatalln(err)
	}
	return config
}
// sparseConfigFile opens the config file, removes most keys
// that have default values, then writes the config file back.
func sparseConfigFile(context *cli.Context) {
	config, cfBefore, err := lib.GetConfig(context)
	if err != nil {
		log.Fatalln(err)
	}
	if cfBefore == "" {
		fmt.Println("Could not find a config file to sparse")
		return
	}

	if cfWritten, err := config.Sparse(context).ToFile(context); err != nil {
		fmt.Printf("Failed to write config file: %s\n", err)
	} else {
		fmt.Printf("Wrote %s\n", cfWritten)
	}
}
func monitorConnector(context *cli.Context) {
	config, filename, err := lib.GetConfig(context)
	if err != nil {
		log.Fatalf("Failed to read config file: %s\n", err)
	}
	if filename == "" {
		fmt.Println("No config file was found, so using defaults")
	}

	if _, err := os.Stat(config.MonitorSocketFilename); err != nil {
		if !os.IsNotExist(err) {
			log.Fatalln(err)
		}
		log.Fatalf(
			"No connector is running, or the monitoring socket %s is mis-configured\n",
			config.MonitorSocketFilename)
	}

	timer := time.AfterFunc(context.Duration("monitor-timeout"), func() {
		log.Fatalf("Timeout after %s\n", context.Duration("monitor-timeout").String())
	})

	conn, err := net.DialTimeout("unix", config.MonitorSocketFilename, time.Second)
	if err != nil {
		log.Fatalf(
			"No connector is running, or it is not listening to socket %s\n",
			config.MonitorSocketFilename)
	}
	defer conn.Close()

	buf, err := ioutil.ReadAll(conn)
	if err != nil {
		log.Fatalln(err)
	}

	timer.Stop()

	fmt.Printf(string(buf))
}
func (service *service) Execute(args []string, r <-chan svc.ChangeRequest, s chan<- svc.Status) (bool, uint32) {
	if service.interactive {
		if err := log.Start(true); err != nil {
			fmt.Fprintf(os.Stderr, "Failed to start event log: %s\n", err)
			return false, 1
		}
	} else {
		if err := log.Start(false); err != nil {
			fmt.Fprintf(os.Stderr, "Failed to start event log: %s\n", err)
			return false, 1
		}
	}
	defer log.Stop()

	config, configFilename, err := lib.GetConfig(service.context)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to read config file: %s\n", err)
		return false, 1
	}

	logLevel, ok := log.LevelFromString(config.LogLevel)
	if !ok {
		fmt.Fprintf(os.Stderr, "Log level %s is not recognized\n", config.LogLevel)
		return false, 1
	}
	log.SetLevel(logLevel)

	if configFilename == "" {
		log.Info("No config file was found, so using defaults")
	} else {
		log.Infof("Using config file %s", configFilename)
	}
	completeConfig, _ := json.MarshalIndent(config, "", " ")
	log.Debugf("Config: %s", string(completeConfig))

	log.Info(lib.FullName)

	if !config.CloudPrintingEnable && !config.LocalPrintingEnable {
		log.Fatal("Cannot run connector with both local_printing_enable and cloud_printing_enable set to false")
		return false, 1
	} else if config.LocalPrintingEnable {
		log.Fatal("Local printing has not been implemented in this version of the Windows connector.")
		return false, 1
	}

	jobs := make(chan *lib.Job, 10)
	xmppNotifications := make(chan xmpp.PrinterNotification, 5)

	var g *gcp.GoogleCloudPrint
	var x *xmpp.XMPP
	if config.CloudPrintingEnable {
		xmppPingTimeout, err := time.ParseDuration(config.XMPPPingTimeout)
		if err != nil {
			log.Fatalf("Failed to parse xmpp ping timeout: %s", err)
			return false, 1
		}
		xmppPingInterval, err := time.ParseDuration(config.XMPPPingInterval)
		if err != nil {
			log.Fatalf("Failed to parse xmpp ping interval default: %s", err)
			return false, 1
		}

		g, err = gcp.NewGoogleCloudPrint(config.GCPBaseURL, config.RobotRefreshToken,
			config.UserRefreshToken, config.ProxyName, config.GCPOAuthClientID,
			config.GCPOAuthClientSecret, config.GCPOAuthAuthURL, config.GCPOAuthTokenURL,
			config.GCPMaxConcurrentDownloads, jobs)
		if err != nil {
			log.Fatal(err)
			return false, 1
		}

		x, err = xmpp.NewXMPP(config.XMPPJID, config.ProxyName, config.XMPPServer, config.XMPPPort,
			xmppPingTimeout, xmppPingInterval, g.GetRobotAccessToken, xmppNotifications)
		if err != nil {
			log.Fatal(err)
			return false, 1
		}
		defer x.Quit()
	}

	ws, err := winspool.NewWinSpool(*config.PrefixJobIDToJobTitle, config.DisplayNamePrefix, config.PrinterBlacklist, config.PrinterWhitelist)
	if err != nil {
		log.Fatal(err)
		return false, 1
	}

	nativePrinterPollInterval, err := time.ParseDuration(config.NativePrinterPollInterval)
	if err != nil {
		log.Fatalf("Failed to parse printer poll interval: %s", err)
		return false, 1
	}
	pm, err := manager.NewPrinterManager(ws, g, nil, nativePrinterPollInterval,
		config.NativeJobQueueSize, *config.CUPSJobFullUsername, config.ShareScope, jobs, xmppNotifications)
	if err != nil {
		log.Fatal(err)
		return false, 1
	}
	defer pm.Quit()

	if config.CloudPrintingEnable {
		if config.LocalPrintingEnable {
			log.Infof("Ready to rock as proxy '%s' and in local mode", config.ProxyName)
		} else {
			log.Infof("Ready to rock as proxy '%s'", config.ProxyName)
		}
	} else {
		log.Info("Ready to rock in local-only mode")
	}

	s <- runningStatus
	for {
		request := <-r
		switch request.Cmd {
		case svc.Interrogate:
			s <- runningStatus

		case svc.Stop:
			s <- stoppingStatus
			log.Info("Shutting down")
			time.AfterFunc(time.Second*30, func() {
				log.Fatal("Failed to stop quickly; stopping forcefully")
				os.Exit(1)
			})

			return false, 0

		default:
			log.Errorf("Received unsupported service command from service control manager: %d", request.Cmd)
		}
	}
}
func connector(context *cli.Context) int {
	config, configFilename, err := lib.GetConfig(context)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to read config file: %s", err)
		return 1
	}

	logToJournal := *config.LogToJournal && journal.Enabled()
	logToConsole := context.Bool("log-to-console")

	if logToJournal {
		log.SetJournalEnabled(true)
		if logToConsole {
			log.SetWriter(os.Stderr)
		} else {
			log.SetWriter(ioutil.Discard)
		}
	} else {
		logFileMaxBytes := config.LogFileMaxMegabytes * 1024 * 1024
		var logWriter io.Writer
		logWriter, err = log.NewLogRoller(config.LogFileName, logFileMaxBytes, config.LogMaxFiles)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failed to start log roller: %s", err)
			return 1
		}

		if logToConsole {
			logWriter = io.MultiWriter(logWriter, os.Stderr)
		}
		log.SetWriter(logWriter)
	}

	logLevel, ok := log.LevelFromString(config.LogLevel)
	if !ok {
		fmt.Fprintf(os.Stderr, "Log level %s is not recognized", config.LogLevel)
		return 1
	}
	log.SetLevel(logLevel)

	if configFilename == "" {
		log.Info("No config file was found, so using defaults")
	} else {
		log.Infof("Using config file %s", configFilename)
	}
	completeConfig, _ := json.MarshalIndent(config, "", " ")
	log.Debugf("Config: %s", string(completeConfig))

	log.Info(lib.FullName)
	fmt.Println(lib.FullName)

	if !config.CloudPrintingEnable && !config.LocalPrintingEnable {
		log.Fatal("Cannot run connector with both local_printing_enable and cloud_printing_enable set to false")
		return 1
	}

	if _, err := os.Stat(config.MonitorSocketFilename); !os.IsNotExist(err) {
		if err != nil {
			log.Fatalf("Failed to stat monitor socket: %s", err)
		} else {
			log.Fatalf(
				"A connector is already running, or the monitoring socket %s wasn't cleaned up properly",
				config.MonitorSocketFilename)
		}
		return 1
	}

	jobs := make(chan *lib.Job, 10)
	xmppNotifications := make(chan xmpp.PrinterNotification, 5)

	var g *gcp.GoogleCloudPrint
	var x *xmpp.XMPP
	if config.CloudPrintingEnable {
		xmppPingTimeout, err := time.ParseDuration(config.XMPPPingTimeout)
		if err != nil {
			log.Fatalf("Failed to parse xmpp ping timeout: %s", err)
			return 1
		}
		xmppPingInterval, err := time.ParseDuration(config.XMPPPingInterval)
		if err != nil {
			log.Fatalf("Failed to parse xmpp ping interval default: %s", err)
			return 1
		}

		g, err = gcp.NewGoogleCloudPrint(config.GCPBaseURL, config.RobotRefreshToken,
			config.UserRefreshToken, config.ProxyName, config.GCPOAuthClientID,
			config.GCPOAuthClientSecret, config.GCPOAuthAuthURL, config.GCPOAuthTokenURL,
			config.GCPMaxConcurrentDownloads, jobs)
		if err != nil {
			log.Fatal(err)
			return 1
		}

		x, err = xmpp.NewXMPP(config.XMPPJID, config.ProxyName, config.XMPPServer, config.XMPPPort,
			xmppPingTimeout, xmppPingInterval, g.GetRobotAccessToken, xmppNotifications)
		if err != nil {
			log.Fatal(err)
			return 1
		}
		defer x.Quit()
	}

	cupsConnectTimeout, err := time.ParseDuration(config.CUPSConnectTimeout)
	if err != nil {
		log.Fatalf("Failed to parse CUPS connect timeout: %s", err)
		return 1
	}
	c, err := cups.NewCUPS(*config.CUPSCopyPrinterInfoToDisplayName, *config.PrefixJobIDToJobTitle,
		config.DisplayNamePrefix, config.CUPSPrinterAttributes, config.CUPSMaxConnections,
		cupsConnectTimeout, config.PrinterBlacklist, config.PrinterWhitelist, *config.CUPSIgnoreRawPrinters,
		*config.CUPSIgnoreClassPrinters)
	if err != nil {
		log.Fatal(err)
		return 1
	}
	defer c.Quit()

	var priv *privet.Privet
	if config.LocalPrintingEnable {
		if g == nil {
			priv, err = privet.NewPrivet(jobs, config.LocalPortLow, config.LocalPortHigh, config.GCPBaseURL, nil)
		} else {
			priv, err = privet.NewPrivet(jobs, config.LocalPortLow, config.LocalPortHigh, config.GCPBaseURL, g.ProximityToken)
		}
		if err != nil {
			log.Fatal(err)
			return 1
		}
		defer priv.Quit()
	}

	nativePrinterPollInterval, err := time.ParseDuration(config.NativePrinterPollInterval)
	if err != nil {
		log.Fatalf("Failed to parse CUPS printer poll interval: %s", err)
		return 1
	}
	pm, err := manager.NewPrinterManager(c, g, priv, nativePrinterPollInterval,
		config.NativeJobQueueSize, *config.CUPSJobFullUsername, config.ShareScope,
		jobs, xmppNotifications)
	if err != nil {
		log.Fatal(err)
		return 1
	}
	defer pm.Quit()

	m, err := monitor.NewMonitor(c, g, priv, pm, config.MonitorSocketFilename)
	if err != nil {
		log.Fatal(err)
		return 1
	}
	defer m.Quit()

	if config.CloudPrintingEnable {
		if config.LocalPrintingEnable {
			log.Infof("Ready to rock as proxy '%s' and in local mode", config.ProxyName)
			fmt.Printf("Ready to rock as proxy '%s' and in local mode\n", config.ProxyName)
		} else {
			log.Infof("Ready to rock as proxy '%s'", config.ProxyName)
			fmt.Printf("Ready to rock as proxy '%s'\n", config.ProxyName)
		}
	} else {
		log.Info("Ready to rock in local-only mode")
		fmt.Println("Ready to rock in local-only mode")
	}

	waitIndefinitely()

	log.Info("Shutting down")
	fmt.Println("")
	fmt.Println("Shutting down")

	return 0
}