forked from Silverfish/proton-bridge
We build too many walls and not enough bridges
This commit is contained in:
217
pkg/pmapi/bugs.go
Normal file
217
pkg/pmapi/bugs.go
Normal file
@ -0,0 +1,217 @@
|
||||
// Copyright (c) 2020 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail Bridge is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ProtonMail Bridge is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package pmapi
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/textproto"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ClientType is required by API.
|
||||
const (
|
||||
EmailClientType = iota + 1
|
||||
VPNClientType
|
||||
)
|
||||
|
||||
type reportAtt struct {
|
||||
name, filename string
|
||||
body io.Reader
|
||||
}
|
||||
|
||||
// ReportReq stores data for report.
|
||||
type ReportReq struct {
|
||||
OS string `json:",omitempty"`
|
||||
OSVersion string `json:",omitempty"`
|
||||
Browser string `json:",omitempty"`
|
||||
BrowserVersion string `json:",omitempty"`
|
||||
BrowserExtensions string `json:",omitempty"`
|
||||
Resolution string `json:",omitempty"`
|
||||
DisplayMode string `json:",omitempty"`
|
||||
Client string `json:",omitempty"`
|
||||
ClientVersion string `json:",omitempty"`
|
||||
ClientType int `json:",omitempty"`
|
||||
Title string `json:",omitempty"`
|
||||
Description string `json:",omitempty"`
|
||||
Username string `json:",omitempty"`
|
||||
Email string `json:",omitempty"`
|
||||
Country string `json:",omitempty"`
|
||||
ISP string `json:",omitempty"`
|
||||
Debug string `json:",omitempty"`
|
||||
Attachments []reportAtt `json:",omitempty"`
|
||||
}
|
||||
|
||||
// AddAttachment to report.
|
||||
func (rep *ReportReq) AddAttachment(name, filename string, r io.Reader) {
|
||||
rep.Attachments = append(rep.Attachments, reportAtt{name: name, filename: filename, body: r})
|
||||
}
|
||||
|
||||
func writeMultipartReport(w *multipart.Writer, rep *ReportReq) error { // nolint[funlen]
|
||||
fieldData := map[string]string{
|
||||
"OS": rep.OS,
|
||||
"OSVersion": rep.OSVersion,
|
||||
"Browser": rep.Browser,
|
||||
"BrowserVersion": rep.BrowserVersion,
|
||||
"BrowserExtensions": rep.BrowserExtensions,
|
||||
"Resolution": rep.Resolution,
|
||||
"DisplayMode": rep.DisplayMode,
|
||||
"Client": rep.Client,
|
||||
"ClientVersion": rep.ClientVersion,
|
||||
"ClientType": "1",
|
||||
"Title": rep.Title,
|
||||
"Description": rep.Description,
|
||||
"Username": rep.Username,
|
||||
"Email": rep.Email,
|
||||
"Country": rep.Country,
|
||||
"ISP": rep.ISP,
|
||||
"Debug": rep.Debug,
|
||||
}
|
||||
|
||||
for field, data := range fieldData {
|
||||
if data == "" {
|
||||
continue
|
||||
}
|
||||
if err := w.WriteField(field, data); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
quoteEscaper := strings.NewReplacer("\\", "\\\\", `"`, "\\\"")
|
||||
|
||||
for _, att := range rep.Attachments {
|
||||
h := make(textproto.MIMEHeader)
|
||||
h.Set("Content-Disposition",
|
||||
fmt.Sprintf(`form-data; name="%s"; filename="%s"`,
|
||||
quoteEscaper.Replace(att.name), quoteEscaper.Replace(att.filename+".zip")))
|
||||
h.Set("Content-Type", "application/octet-stream")
|
||||
//h.Set("Content-Transfere-Encoding", "base64")
|
||||
attWr, err := w.CreatePart(h)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
zipArch := zip.NewWriter(attWr)
|
||||
zipWr, err := zipArch.Create(att.filename)
|
||||
//b64 := base64.NewEncoder(base64.StdEncoding, zipWr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = io.Copy(zipWr, att.body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = zipArch.Close()
|
||||
//err = b64.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Report sends request as json or multipart (if has attachment).
|
||||
func (c *Client) Report(rep ReportReq) (err error) {
|
||||
rep.Client = c.config.ClientID
|
||||
rep.ClientVersion = c.config.AppVersion
|
||||
rep.ClientType = EmailClientType
|
||||
|
||||
var req *http.Request
|
||||
var w *MultipartWriter
|
||||
if len(rep.Attachments) > 0 {
|
||||
req, w, err = NewMultipartRequest("POST", "/reports/bug")
|
||||
} else {
|
||||
req, err = NewJSONRequest("POST", "/reports/bug", rep)
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var res Res
|
||||
done := make(chan error, 1)
|
||||
go func() {
|
||||
done <- c.DoJSON(req, &res)
|
||||
}()
|
||||
|
||||
if w != nil {
|
||||
err = writeMultipartReport(w.Writer, &rep)
|
||||
if err != nil {
|
||||
c.log.Errorln("report write: ", err)
|
||||
return
|
||||
}
|
||||
err = w.Close()
|
||||
if err != nil {
|
||||
c.log.Errorln("report close: ", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err = <-done; err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return res.Err()
|
||||
}
|
||||
|
||||
// ReportBug is old. Use Report instead.
|
||||
func (c *Client) ReportBug(os, osVersion, title, description, username, email string) (err error) {
|
||||
return c.ReportBugWithEmailClient(os, osVersion, title, description, username, email, "")
|
||||
}
|
||||
|
||||
// ReportBugWithEmailClient is old. Use Report instead.
|
||||
func (c *Client) ReportBugWithEmailClient(os, osVersion, title, description, username, email, emailClient string) (err error) {
|
||||
bugReq := ReportReq{
|
||||
OS: os,
|
||||
OSVersion: osVersion,
|
||||
Browser: emailClient,
|
||||
Title: title,
|
||||
Description: description,
|
||||
Username: username,
|
||||
Email: email,
|
||||
}
|
||||
|
||||
return c.Report(bugReq)
|
||||
}
|
||||
|
||||
// ReportCrash is old. Use sentry instead.
|
||||
func (c *Client) ReportCrash(stacktrace string) (err error) {
|
||||
crashReq := ReportReq{
|
||||
Client: c.config.ClientID,
|
||||
ClientVersion: c.config.AppVersion,
|
||||
ClientType: EmailClientType,
|
||||
OS: runtime.GOOS,
|
||||
Debug: stacktrace,
|
||||
}
|
||||
req, err := NewJSONRequest("POST", "/reports/crash", crashReq)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var res Res
|
||||
if err = c.DoJSON(req, &res); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = res.Err()
|
||||
return
|
||||
}
|
||||
Reference in New Issue
Block a user