func (api *privetAPI) accesstoken(w http.ResponseWriter, r *http.Request) { log.Debugf("Received /accesstoken request: %+v", r) if ok := api.checkRequest(w, r, "GET"); !ok { return } user := r.Form.Get("user") if len(user) == 0 { writeError(w, "invalid_params", "user parameter expected") return } responseBody, httpStatusCode, err := api.getProximityToken(api.gcpID, user) if err != nil { log.Errorf("Failed to get proximity token: %s", err) } if responseBody == nil || len(responseBody) == 0 { log.Warning("Cloud returned empty response body") writeError(w, "server_error", "Check connector logs") return } var response struct { Success bool `json:"success"` Message string `json:"message"` ErrorCode int `json:"errorCode"` ProximityToken map[string]interface{} `json:"proximity_token"` } if err = json.Unmarshal(responseBody, &response); err != nil { log.Errorf("Failed to unmarshal ticket from cloud: %s", err) writeError(w, "server_error", "Check connector logs") return } if response.Success { token, err := json.MarshalIndent(response.ProximityToken, "", " ") if err != nil { log.Errorf("Failed to marshal something that was just unmarshalled: %s", err) writeError(w, "server_error", "Check connector logs") } else { w.Write(token) } return } if response.ErrorCode != 0 { e := privetError{ Error: "server_error", Description: response.Message, ServerAPI: "/proximitytoken", ServerCode: response.ErrorCode, ServerHTTPCode: httpStatusCode, }.json() w.Write(e) return } writeError(w, "server_error", "Check connector logs") }
func (api *privetAPI) jobstate(w http.ResponseWriter, r *http.Request) { log.Debugf("Received /jobstate request: %+v", r) if ok := api.checkRequest(w, r, "GET"); !ok { return } jobID := r.Form.Get("job_id") jobState, exists := api.jc.jobState(jobID) if !exists { writeError(w, "invalid_print_job", "") return } w.Write(jobState) }
func (api *privetAPI) createjob(w http.ResponseWriter, r *http.Request) { log.Debugf("Received /createjob request: %+v", r) if ok := api.checkRequest(w, r, "POST"); !ok { return } requestBody, err := ioutil.ReadAll(r.Body) if err != nil { log.Warningf("Failed to read request body: %s", err) writeError(w, "invalid_ticket", "Check connector logs") return } var ticket cdd.CloudJobTicket if err = json.Unmarshal(requestBody, &ticket); err != nil { log.Warningf("Failed to read request body: %s", err) writeError(w, "invalid_ticket", "Check connector logs") return } printer, exists := api.getPrinter(api.name) if !exists { w.WriteHeader(http.StatusInternalServerError) return } if printer.State.State == cdd.CloudDeviceStateStopped { writeError(w, "printer_error", "Printer is stopped") return } jobID, expiresIn := api.jc.createJob(&ticket) var response struct { JobID string `json:"job_id"` ExpiresIn int32 `json:"expires_in"` } response.JobID = jobID response.ExpiresIn = expiresIn j, err := json.MarshalIndent(response, "", " ") if err != nil { api.jc.deleteJob(jobID) log.Errorf("Failed to marrshal createJob response: %s", err) w.WriteHeader(http.StatusInternalServerError) } else { w.Write(j) } }
// Fetch calls google.com/cloudprint/fetch to get the outstanding print jobs for // a GCP printer. func (gcp *GoogleCloudPrint) Fetch(gcpID string) ([]Job, error) { form := url.Values{} form.Set("printerid", gcpID) responseBody, errorCode, _, err := postWithRetry(gcp.robotClient, gcp.baseURL+"fetch", form) if err != nil { if errorCode == 413 { // 413 means "Zero print jobs returned", which isn't really an error. return []Job{}, nil } return nil, err } var jobsData struct { Jobs []struct { ID string Title string FileURL string OwnerID string } } if err = json.Unmarshal(responseBody, &jobsData); err != nil { return nil, err } jobs := make([]Job, len(jobsData.Jobs)) for i, jobData := range jobsData.Jobs { jobs[i] = Job{ GCPPrinterID: gcpID, GCPJobID: jobData.ID, FileURL: jobData.FileURL, OwnerID: jobData.OwnerID, Title: jobData.Title, } } log.Debugf("Fetched jobs: %+v", jobs) return jobs, nil }
// listenNotifications handles the messages found on the channels. func (pm *PrinterManager) listenNotifications(jobs <-chan *lib.Job, xmppMessages <-chan xmpp.PrinterNotification) { go func() { for { select { case <-pm.quit: return case job := <-jobs: log.DebugJobf(job.JobID, "Received job: %+v", job) go pm.printJob(job.CUPSPrinterName, job.Filename, job.Title, job.User, job.JobID, job.Ticket, job.UpdateJob) case notification := <-xmppMessages: log.Debugf("Received XMPP message: %+v", notification) if notification.Type == xmpp.PrinterNewJobs { if p, exists := pm.printers.GetByGCPID(notification.GCPID); exists { go pm.gcp.HandleJobs(&p, func() { pm.incrementJobsProcessed(false) }) } } } } }() }
func (api *privetAPI) capabilities(w http.ResponseWriter, r *http.Request) { log.Debugf("Received /capabilities request: %+v", r) if ok := api.checkRequest(w, r, "GET"); !ok { return } printer, exists := api.getPrinter(api.name) if !exists { w.WriteHeader(http.StatusInternalServerError) return } capabilities := cdd.CloudDeviceDescription{ Version: "1.0", Printer: printer.Description, } j, err := json.MarshalIndent(capabilities, "", " ") if err != nil { log.Errorf("Failed to marshal capabilities response: %s", err) w.WriteHeader(http.StatusInternalServerError) } else { w.Write(j) } }
func (t *tee) Write(p []byte) (int, error) { n, err := t.w.Write(p) log.Debugf("XMPP wrote %d %s", n, p[0:n]) return n, err }
func (t *tee) Read(p []byte) (int, error) { n, err := t.r.Read(p) log.Debugf("XMPP read %d %s", n, p[0:n]) return n, err }
func (api *privetAPI) submitdoc(w http.ResponseWriter, r *http.Request) { log.Debugf("Received /submitdoc request: %+v", r) if ok := api.checkRequest(w, r, "POST"); !ok { return } file, err := ioutil.TempFile("", "cups-connector-privet-") if err != nil { log.Errorf("Failed to create file for new Privet job: %s", err) w.WriteHeader(http.StatusInternalServerError) return } defer file.Close() jobSize, err := io.Copy(file, r.Body) if err != nil { log.Errorf("Failed to copy new print job file: %s", err) w.WriteHeader(http.StatusInternalServerError) os.Remove(file.Name()) return } if length, err := strconv.ParseInt(r.Header.Get("Content-Length"), 10, 64); err != nil || length != jobSize { writeError(w, "invalid_params", "Content-Length header doesn't match length of content") os.Remove(file.Name()) return } jobType := r.Header.Get("Content-Type") if jobType == "" { writeError(w, "invalid_document_type", "Content-Type header is missing") os.Remove(file.Name()) return } printer, exists := api.getPrinter(api.name) if !exists { w.WriteHeader(http.StatusInternalServerError) os.Remove(file.Name()) return } if printer.State.State == cdd.CloudDeviceStateStopped { writeError(w, "printer_error", "Printer is stopped") os.Remove(file.Name()) return } jobName := r.Form.Get("job_name") userName := r.Form.Get("user_name") jobID := r.Form.Get("job_id") var expiresIn int32 var ticket *cdd.CloudJobTicket if jobID == "" { jobID, expiresIn = api.jc.createJob(nil) } else { var ok bool if expiresIn, ticket, ok = api.jc.getJobExpiresIn(jobID); !ok { pe := privetError{ Error: "invalid_print_job", Timeout: 5, }.json() w.Write(pe) os.Remove(file.Name()) return } } api.jobs <- &lib.Job{ CUPSPrinterName: api.name, Filename: file.Name(), Title: jobName, User: userName, JobID: jobID, Ticket: ticket, UpdateJob: api.jc.updateJob, } var response struct { JobID string `json:"job_id"` ExpiresIn int32 `json:"expires_in"` JobType string `json:"job_type"` JobSize int64 `json:"job_size"` JobName string `json:"job_name,omitempty"` } response.JobID = jobID response.ExpiresIn = expiresIn response.JobType = jobType response.JobSize = jobSize response.JobName = jobName j, err := json.MarshalIndent(response, "", " ") if err != nil { log.ErrorJobf(jobID, "Failed to marshal submitdoc response: %s", err) w.WriteHeader(http.StatusInternalServerError) return } w.Write(j) }
func (api *privetAPI) info(w http.ResponseWriter, r *http.Request) { log.Debugf("Received /info request: %+v", r) if r.Method != "GET" { w.WriteHeader(http.StatusMethodNotAllowed) return } if _, exists := r.Header["X-Privet-Token"]; !exists { w.WriteHeader(http.StatusBadRequest) writeError(w, "invalid_x_privet_token", "X-Privet-Token request header is missing or invalid") return } printer, exists := api.getPrinter(api.name) if !exists { w.WriteHeader(http.StatusInternalServerError) return } var s cdd.CloudConnectionStateType if api.online { s = cdd.CloudConnectionStateOnline } else { s = cdd.CloudConnectionStateOffline } state := cdd.CloudDeviceState{ Version: "1.0", CloudConnectionState: &s, Printer: printer.State, } var connectionState string var supportedAPIs []string if api.online { connectionState = "online" supportedAPIs = supportedAPIsOnline } else { connectionState = "offline" supportedAPIs = supportedAPIsOffline } response := infoResponse{ Version: "1.0", Name: printer.DefaultDisplayName, URL: api.gcpBaseURL, Type: []string{"printer"}, ID: printer.GCPID, DeviceState: strings.ToLower(string(printer.State.State)), ConnectionState: connectionState, Manufacturer: printer.Manufacturer, Model: printer.Model, SerialNumber: printer.UUID, Firmware: printer.ConnectorVersion, Uptime: uint(time.Since(api.startTime).Seconds()), SetupURL: printer.SetupURL, SupportURL: printer.SupportURL, UpdateURL: printer.UpdateURL, XPrivetToken: api.xsrf.newToken(), API: supportedAPIs, SemanticState: state, } j, err := json.MarshalIndent(response, "", " ") if err != nil { log.Errorf("Failed to marshal Privet info: %s", err) w.WriteHeader(http.StatusInternalServerError) return } w.Write(j) }