Import/Export GUI
This commit is contained in:
committed by
Michal Horejsek
parent
1c10cc5065
commit
7e5e3d3dd4
@ -94,9 +94,9 @@ build-ie-linux:
|
|||||||
script:
|
script:
|
||||||
- make build-ie
|
- make build-ie
|
||||||
artifacts:
|
artifacts:
|
||||||
name: "bridge-linux-$CI_COMMIT_SHORT_SHA"
|
name: "ie-linux-$CI_COMMIT_SHORT_SHA"
|
||||||
paths:
|
paths:
|
||||||
- bridge_*.tgz
|
- ie_*.tgz
|
||||||
expire_in: 2 week
|
expire_in: 2 week
|
||||||
|
|
||||||
build-darwin:
|
build-darwin:
|
||||||
@ -145,9 +145,9 @@ build-ie-darwin:
|
|||||||
script:
|
script:
|
||||||
- make build-ie
|
- make build-ie
|
||||||
artifacts:
|
artifacts:
|
||||||
name: "bridge-darwin-$CI_COMMIT_SHORT_SHA"
|
name: "ie-darwin-$CI_COMMIT_SHORT_SHA"
|
||||||
paths:
|
paths:
|
||||||
- bridge_*.tgz
|
- ie_*.tgz
|
||||||
expire_in: 2 week
|
expire_in: 2 week
|
||||||
|
|
||||||
build-windows:
|
build-windows:
|
||||||
@ -189,9 +189,9 @@ build-ie-windows:
|
|||||||
- go mod download
|
- go mod download
|
||||||
- TARGET_OS=windows make build-ie
|
- TARGET_OS=windows make build-ie
|
||||||
artifacts:
|
artifacts:
|
||||||
name: "bridge-windows-$CI_COMMIT_SHORT_SHA"
|
name: "ie-windows-$CI_COMMIT_SHORT_SHA"
|
||||||
paths:
|
paths:
|
||||||
- bridge_*.tgz
|
- ie_*.tgz
|
||||||
expire_in: 2 week
|
expire_in: 2 week
|
||||||
|
|
||||||
# Stage: MIRROR
|
# Stage: MIRROR
|
||||||
|
|||||||
@ -47,6 +47,8 @@ Changelog [format](http://keepachangelog.com/en/1.0.0/)
|
|||||||
* GODT-554 Detect and notify about "bad certificate" IMAP TLS error.
|
* GODT-554 Detect and notify about "bad certificate" IMAP TLS error.
|
||||||
* IMAP mailbox info update when new mailbox is created.
|
* IMAP mailbox info update when new mailbox is created.
|
||||||
* GODT-72 Use ISO-8859-1 encoding if charset is not specified and it isn't UTF-8.
|
* GODT-72 Use ISO-8859-1 encoding if charset is not specified and it isn't UTF-8.
|
||||||
|
* Structure for transfer rules in QML
|
||||||
|
* GODT-360 Detect charset embedded in html/xml.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
* GODT-360 Detect charset embedded in html/xml.
|
* GODT-360 Detect charset embedded in html/xml.
|
||||||
|
|||||||
2
go.mod
2
go.mod
@ -46,6 +46,7 @@ require (
|
|||||||
github.com/golang/mock v1.4.4
|
github.com/golang/mock v1.4.4
|
||||||
github.com/google/go-cmp v0.5.1
|
github.com/google/go-cmp v0.5.1
|
||||||
github.com/google/uuid v1.1.1
|
github.com/google/uuid v1.1.1
|
||||||
|
github.com/go-delve/delve v1.4.1 // indirect
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c // indirect
|
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c // indirect
|
||||||
github.com/hashicorp/go-multierror v1.1.0
|
github.com/hashicorp/go-multierror v1.1.0
|
||||||
github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7
|
github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7
|
||||||
@ -59,6 +60,7 @@ require (
|
|||||||
github.com/nsf/jsondiff v0.0.0-20200515183724-f29ed568f4ce
|
github.com/nsf/jsondiff v0.0.0-20200515183724-f29ed568f4ce
|
||||||
github.com/olekukonko/tablewriter v0.0.4 // indirect
|
github.com/olekukonko/tablewriter v0.0.4 // indirect
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
|
github.com/psampaz/go-mod-outdated v0.6.0 // indirect
|
||||||
github.com/sirupsen/logrus v1.6.0
|
github.com/sirupsen/logrus v1.6.0
|
||||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
|
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
|
||||||
github.com/stretchr/testify v1.6.1
|
github.com/stretchr/testify v1.6.1
|
||||||
|
|||||||
42
go.sum
42
go.sum
@ -39,6 +39,9 @@ github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
|
|||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
|
github.com/cosiner/argv v0.0.0-20170225145430-13bacc38a0a5/go.mod h1:p/NrK5tF6ICIly4qwEDsf6VDirFiWWz0FenfYBwJaKQ=
|
||||||
|
github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
|
||||||
|
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
|
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
@ -81,6 +84,10 @@ github.com/getsentry/raven-go v0.2.0 h1:no+xWJRb5ZI7eE8TWgIq1jLulQiIoLG0IfYxv5JY
|
|||||||
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
|
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
|
||||||
github.com/go-resty/resty/v2 v2.3.0 h1:JOOeAvjSlapTT92p8xiS19Zxev1neGikoHsXJeOq8So=
|
github.com/go-resty/resty/v2 v2.3.0 h1:JOOeAvjSlapTT92p8xiS19Zxev1neGikoHsXJeOq8So=
|
||||||
github.com/go-resty/resty/v2 v2.3.0/go.mod h1:UpN9CgLZNsv4e9XG50UU8xdI0F43UQ4HmxLBDwaroHU=
|
github.com/go-resty/resty/v2 v2.3.0/go.mod h1:UpN9CgLZNsv4e9XG50UU8xdI0F43UQ4HmxLBDwaroHU=
|
||||||
|
github.com/go-delve/delve v1.4.1 h1:kZs0umEv+VKnK84kY9/ZXWrakdLTeRTyYjFdgLelZCQ=
|
||||||
|
github.com/go-delve/delve v1.4.1/go.mod h1:vmy6iObn7zg8FQ5KOCIe6TruMNsqpoZO8uMiRea+97k=
|
||||||
|
github.com/go-resty/resty/v2 v2.2.0 h1:vgZ1cdblp8Aw4jZj3ZsKh6yKAlMg3CHMrqFSFFd+jgY=
|
||||||
|
github.com/go-resty/resty/v2 v2.2.0/go.mod h1:nYW/8rxqQCmI3bPz9Fsmjbr2FBjGuR2Mzt6kDh3zZ7w=
|
||||||
github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw=
|
github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw=
|
||||||
github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||||
github.com/gogs/chardet v0.0.0-20150115103509-2404f7772561 h1:aBzukfDxQlCTVS0NBUjI5YA3iVeaZ9Tb5PxNrrIP1xs=
|
github.com/gogs/chardet v0.0.0-20150115103509-2404f7772561 h1:aBzukfDxQlCTVS0NBUjI5YA3iVeaZ9Tb5PxNrrIP1xs=
|
||||||
@ -91,6 +98,11 @@ github.com/google/go-cmp v0.5.1 h1:JFrFEBb2xKufg6XkJsJr+WbKb4FQlURi5RUcBveYu9k=
|
|||||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
||||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw=
|
||||||
|
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||||
|
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
||||||
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-dap v0.2.0/go.mod h1:5q8aYQFnHOAZEMP+6vmq25HKYAEwE+LF5yh7JKrrhSQ=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20190411002643-bd77b112433e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20190411002643-bd77b112433e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4=
|
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
@ -100,6 +112,10 @@ github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g
|
|||||||
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
|
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
|
||||||
github.com/jameshoulahan/go-imap v0.0.0-20200728140727-d57327f48843 h1:suxlO4AC4E4bjueAsL0m+qp8kmkxRWMGj+5bBU/KJ8g=
|
github.com/jameshoulahan/go-imap v0.0.0-20200728140727-d57327f48843 h1:suxlO4AC4E4bjueAsL0m+qp8kmkxRWMGj+5bBU/KJ8g=
|
||||||
github.com/jameshoulahan/go-imap v0.0.0-20200728140727-d57327f48843/go.mod h1:yKASt+C3ZiDAiCSssxg9caIckWF/JG7ZQTO7GAmvicU=
|
github.com/jameshoulahan/go-imap v0.0.0-20200728140727-d57327f48843/go.mod h1:yKASt+C3ZiDAiCSssxg9caIckWF/JG7ZQTO7GAmvicU=
|
||||||
|
github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
|
||||||
|
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||||
|
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||||
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
github.com/jaytaylor/html2text v0.0.0-20190408195923-01ec452cbe43/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
|
github.com/jaytaylor/html2text v0.0.0-20190408195923-01ec452cbe43/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
|
||||||
github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7 h1:g0fAGBisHaEQ0TRq1iBvemFRf+8AEWEmBESSiWB3Vsc=
|
github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7 h1:g0fAGBisHaEQ0TRq1iBvemFRf+8AEWEmBESSiWB3Vsc=
|
||||||
github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
|
github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
|
||||||
@ -121,10 +137,14 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
|||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
|
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
|
||||||
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||||
|
github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381 h1:bqDmpDG49ZRnB5PcgP0RXtQvnMSgIF14M7CBd2shtXs=
|
||||||
|
github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||||
github.com/martinlindhe/base36 v1.0.0 h1:eYsumTah144C0A8P1T/AVSUk5ZoLnhfYFM3OGQxB52A=
|
github.com/martinlindhe/base36 v1.0.0 h1:eYsumTah144C0A8P1T/AVSUk5ZoLnhfYFM3OGQxB52A=
|
||||||
github.com/martinlindhe/base36 v1.0.0/go.mod h1:+AtEs8xrBpCeYgSLoY/aJ6Wf37jtBuR0s35750M27+8=
|
github.com/martinlindhe/base36 v1.0.0/go.mod h1:+AtEs8xrBpCeYgSLoY/aJ6Wf37jtBuR0s35750M27+8=
|
||||||
|
github.com/mattn/go-colorable v0.0.0-20170327083344-ded68f7a9561/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||||
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
|
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
|
||||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||||
|
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM=
|
github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM=
|
||||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||||
@ -135,6 +155,8 @@ github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/Qd
|
|||||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||||
github.com/miekg/dns v1.1.30 h1:Qww6FseFn8PRfw07jueqIXqodm0JKiiKuK0DeXSqfyo=
|
github.com/miekg/dns v1.1.30 h1:Qww6FseFn8PRfw07jueqIXqodm0JKiiKuK0DeXSqfyo=
|
||||||
github.com/miekg/dns v1.1.30/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
github.com/miekg/dns v1.1.30/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||||
|
github.com/miekg/dns v1.1.29 h1:xHBEhR+t5RzcFJjBLJlax2daXOrTYtr9z4WdKEfWFzg=
|
||||||
|
github.com/miekg/dns v1.1.29/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||||
github.com/myesui/uuid v1.0.0 h1:xCBmH4l5KuvLYc5L7AS7SZg9/jKdIFubM7OVoLqaQUI=
|
github.com/myesui/uuid v1.0.0 h1:xCBmH4l5KuvLYc5L7AS7SZg9/jKdIFubM7OVoLqaQUI=
|
||||||
github.com/myesui/uuid v1.0.0/go.mod h1:2CDfNgU0LR8mIdO8vdWd8i9gWWxLlcoIGGpSNgafq84=
|
github.com/myesui/uuid v1.0.0/go.mod h1:2CDfNgU0LR8mIdO8vdWd8i9gWWxLlcoIGGpSNgafq84=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||||
@ -145,11 +167,16 @@ github.com/olekukonko/tablewriter v0.0.1 h1:b3iUnf1v+ppJiOfNX4yxxqfWKMQPZR5yoh8u
|
|||||||
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||||
github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
|
github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
|
||||||
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
|
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
|
||||||
|
github.com/peterh/liner v0.0.0-20170317030525-88609521dc4b/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/psampaz/go-mod-outdated v0.6.0 h1:DXS6rdsz4rpezbPsckQflqrYSEBvsF5GAmUWP+UvnQo=
|
||||||
|
github.com/psampaz/go-mod-outdated v0.6.0/go.mod h1:r78NYWd1z+F9Zdsfy70svgXOz363B08BWnTyFSgEESs=
|
||||||
|
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
|
||||||
|
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||||
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
||||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca h1:NugYot0LIVPxTvN8n+Kvkn6TrbMyxQiuvKdEwFdR9vI=
|
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca h1:NugYot0LIVPxTvN8n+Kvkn6TrbMyxQiuvKdEwFdR9vI=
|
||||||
@ -163,6 +190,8 @@ github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I
|
|||||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA=
|
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA=
|
||||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
|
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
|
||||||
|
github.com/spf13/cobra v0.0.0-20170417170307-b6cb39589372/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||||
|
github.com/spf13/pflag v0.0.0-20170417173400-9e4c21054fa1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo=
|
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo=
|
||||||
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM=
|
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
@ -192,6 +221,12 @@ github.com/urfave/cli v1.22.4 h1:u7tSpNPPswAFymm8IehJhy4uJMlUuU/GmqSkvJ1InXA=
|
|||||||
github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||||
go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0=
|
go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0=
|
||||||
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
||||||
|
github.com/urfave/cli v1.22.3 h1:FpNT6zq26xNpHZy08emi755QwzLPs6Pukqjlc7RfOMU=
|
||||||
|
github.com/urfave/cli v1.22.3/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||||
|
go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk=
|
||||||
|
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||||
|
go.starlark.net v0.0.0-20190702223751-32f345186213/go.mod h1:c1/X6cHgvdXj6pUlmWKMkuqRnW4K8x2vwt6JAaaircg=
|
||||||
|
golang.org/x/arch v0.0.0-20190927153633-4e8777c89be4/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4=
|
||||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
@ -211,6 +246,7 @@ golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5h
|
|||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@ -226,8 +262,10 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20191127201027-ecd32218bd7f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 h1:VvQyQJN0tSuecqgcIxMWnnfG5kSmgy9KZR9sW3W5QeA=
|
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 h1:VvQyQJN0tSuecqgcIxMWnnfG5kSmgy9KZR9sW3W5QeA=
|
||||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
@ -237,8 +275,12 @@ gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8X
|
|||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/stretchr/testify.v1 v1.2.2 h1:yhQC6Uy5CqibAIlk1wlusa/MJ3iAN49/BsR/dCCKz3M=
|
gopkg.in/stretchr/testify.v1 v1.2.2 h1:yhQC6Uy5CqibAIlk1wlusa/MJ3iAN49/BsR/dCCKz3M=
|
||||||
gopkg.in/stretchr/testify.v1 v1.2.2/go.mod h1:QI5V/q6UbPmuhtm10CaFZxED9NreB8PnFYN9JcR6TxU=
|
gopkg.in/stretchr/testify.v1 v1.2.2/go.mod h1:QI5V/q6UbPmuhtm10CaFZxED9NreB8PnFYN9JcR6TxU=
|
||||||
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||||
|
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||||
|
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||||
|
|||||||
@ -25,6 +25,7 @@ import (
|
|||||||
"github.com/ProtonMail/proton-bridge/internal/metrics"
|
"github.com/ProtonMail/proton-bridge/internal/metrics"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/preferences"
|
"github.com/ProtonMail/proton-bridge/internal/preferences"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/users"
|
"github.com/ProtonMail/proton-bridge/internal/users"
|
||||||
|
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
|
||||||
|
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/listener"
|
"github.com/ProtonMail/proton-bridge/pkg/listener"
|
||||||
logrus "github.com/sirupsen/logrus"
|
logrus "github.com/sirupsen/logrus"
|
||||||
@ -130,15 +131,17 @@ func (b *Bridge) ReportBug(osType, osVersion, description, accountName, address,
|
|||||||
defer c.Logout()
|
defer c.Logout()
|
||||||
|
|
||||||
title := "[Bridge] Bug"
|
title := "[Bridge] Bug"
|
||||||
if err := c.ReportBugWithEmailClient(
|
report := pmapi.ReportReq{
|
||||||
osType,
|
OS: osType,
|
||||||
osVersion,
|
OSVersion: osVersion,
|
||||||
title,
|
Browser: emailClient,
|
||||||
description,
|
Title: title,
|
||||||
accountName,
|
Description: description,
|
||||||
address,
|
Username: accountName,
|
||||||
emailClient,
|
Email: address,
|
||||||
); err != nil {
|
}
|
||||||
|
|
||||||
|
if err := c.Report(report); err != nil {
|
||||||
log.Error("Reporting bug failed: ", err)
|
log.Error("Reporting bug failed: ", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,7 +15,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
// Code generated by ./release-notes.sh at Wed 29 Jul 2020 07:07:28 AM CEST. DO NOT EDIT.
|
// Code generated by ./release-notes.sh at 'Fri 07 Aug 2020 06:34:27 AM CEST'. DO NOT EDIT.
|
||||||
|
|
||||||
package bridge
|
package bridge
|
||||||
|
|
||||||
|
|||||||
@ -38,7 +38,7 @@ Item {
|
|||||||
property var allMonths : getMonthList(1,12)
|
property var allMonths : getMonthList(1,12)
|
||||||
property var allDays : getDayList(1,31)
|
property var allDays : getDayList(1,31)
|
||||||
|
|
||||||
property var enums : JSON.parse('{"pathOK":1,"pathEmptyPath":2,"pathWrongPath":4,"pathNotADir":8,"pathWrongPermissions":16,"pathDirEmpty":32,"errUnknownError":0,"errEventAPILogout":1,"errUpdateAPI":2,"errUpdateJSON":3,"errUserAuth":4,"errQApplication":18,"errEmailExportFailed":6,"errEmailExportMissing":7,"errNothingToImport":8,"errEmailImportFailed":12,"errDraftImportFailed":13,"errDraftLabelFailed":14,"errEncryptMessageAttachment":15,"errEncryptMessage":16,"errNoInternetWhileImport":17,"errUnlockUser":5,"errSourceMessageNotSelected":19,"errCannotParseMail":5000,"errWrongLoginOrPassword":5001,"errWrongServerPathOrPort":5002,"errWrongAuthMethod":5003,"errIMAPFetchFailed":5004,"errLocalSourceLoadFailed":1000,"errPMLoadFailed":1001,"errRemoteSourceLoadFailed":1002,"errLoadAccountList":1005,"errExit":1006,"errRetry":1007,"errAsk":1008,"errImportFailed":1009,"errCreateLabelFailed":1010,"errCreateFolderFailed":1011,"errUpdateLabelFailed":1012,"errUpdateFolderFailed":1013,"errFillFolderName":1014,"errSelectFolderColor":1015,"errNoInternet":1016,"folderTypeSystem":"","folderTypeLabel":"label","folderTypeFolder":"folder","folderTypeExternal":"external","progressInit":"init","progressLooping":"looping","statusNoInternet":"noInternet","statusCheckingInternet":"internetCheck","statusNewVersionAvailable":"oldVersion","statusUpToDate":"upToDate","statusForceUpdate":"forceupdate"}')
|
property var enums : JSON.parse('{"pathOK":1,"pathEmptyPath":2,"pathWrongPath":4,"pathNotADir":8,"pathWrongPermissions":16,"pathDirEmpty":32,"errUnknownError":0,"errEventAPILogout":1,"errUpdateAPI":2,"errUpdateJSON":3,"errUserAuth":4,"errQApplication":18,"errEmailExportFailed":6,"errEmailExportMissing":7,"errNothingToImport":8,"errEmailImportFailed":12,"errDraftImportFailed":13,"errDraftLabelFailed":14,"errEncryptMessageAttachment":15,"errEncryptMessage":16,"errNoInternetWhileImport":17,"errUnlockUser":5,"errSourceMessageNotSelected":19,"errCannotParseMail":5000,"errWrongLoginOrPassword":5001,"errWrongServerPathOrPort":5002,"errWrongAuthMethod":5003,"errIMAPFetchFailed":5004,"errLocalSourceLoadFailed":1000,"errPMLoadFailed":1001,"errRemoteSourceLoadFailed":1002,"errLoadAccountList":1005,"errExit":1006,"errRetry":1007,"errAsk":1008,"errImportFailed":1009,"errCreateLabelFailed":1010,"errCreateFolderFailed":1011,"errUpdateLabelFailed":1012,"errUpdateFolderFailed":1013,"errFillFolderName":1014,"errSelectFolderColor":1015,"errNoInternet":1016,"folderTypeSystem":"system","folderTypeLabel":"label","folderTypeFolder":"folder","folderTypeExternal":"external","progressInit":"init","progressLooping":"looping","statusNoInternet":"noInternet","statusCheckingInternet":"internetCheck","statusNewVersionAvailable":"oldVersion","statusUpToDate":"upToDate","statusForceUpdate":"forceupdate"}')
|
||||||
|
|
||||||
IEStyle{}
|
IEStyle{}
|
||||||
|
|
||||||
@ -396,7 +396,7 @@ Item {
|
|||||||
onTriggered : go.runCheckVersion(false)
|
onTriggered : go.runCheckVersion(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
property string areYouSureYouWantToQuit : qsTr("Tool does not finished all the jobs. Do you really want to quit?")
|
property string areYouSureYouWantToQuit : qsTr("There are incomplete processes - some items are not yet transferred. Do you really want to stop and quit?")
|
||||||
// On start
|
// On start
|
||||||
Component.onCompleted : {
|
Component.onCompleted : {
|
||||||
// set spell messages
|
// set spell messages
|
||||||
|
|||||||
@ -25,14 +25,15 @@ import ImportExportUI 1.0
|
|||||||
Column {
|
Column {
|
||||||
id: dateRange
|
id: dateRange
|
||||||
|
|
||||||
property var structure : structureExternal
|
property var structure : transferRules
|
||||||
property string sourceID : structureExternal.getID ( -1 )
|
property string sourceID : "-1"
|
||||||
|
|
||||||
property alias allDates : allDatesBox.checked
|
property alias allDates : allDatesBox.checked
|
||||||
property alias inputDateFrom : inputDateFrom
|
property alias inputDateFrom : inputDateFrom
|
||||||
property alias inputDateTo : inputDateTo
|
property alias inputDateTo : inputDateTo
|
||||||
|
|
||||||
function setRange() {common.setRange()}
|
function getRange() {common.getRange()}
|
||||||
|
function setRangeFromTo(from, to) {common.setRangeFromTo(from, to)}
|
||||||
function applyRange() {common.applyRange()}
|
function applyRange() {common.applyRange()}
|
||||||
|
|
||||||
property var dropDownStyle : Style.dropDownLight
|
property var dropDownStyle : Style.dropDownLight
|
||||||
|
|||||||
@ -34,7 +34,7 @@ Item {
|
|||||||
property alias inputDateFrom : inputDateFrom
|
property alias inputDateFrom : inputDateFrom
|
||||||
property alias inputDateTo : inputDateTo
|
property alias inputDateTo : inputDateTo
|
||||||
|
|
||||||
function setRange() {common.setRange()}
|
function getRange() {common.getRange()}
|
||||||
function applyRange() {common.applyRange()}
|
function applyRange() {common.applyRange()}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -43,11 +43,7 @@ Item {
|
|||||||
inputDateTo.setDate((new Date()).getTime())
|
inputDateTo.setDate((new Date()).getTime())
|
||||||
}
|
}
|
||||||
|
|
||||||
function setRange(){ // unix time in seconds
|
function setRangeFromTo(folderFrom, folderTo){ // unix time in seconds
|
||||||
var folderFrom = dateRange.structure.getFrom(dateRange.sourceID)
|
|
||||||
if (folderFrom===undefined) folderFrom = 0
|
|
||||||
var folderTo = dateRange.structure.getTo(dateRange.sourceID)
|
|
||||||
if (folderTo===undefined) folderTo = 0
|
|
||||||
if ( folderFrom == 0 && folderTo ==0 ) {
|
if ( folderFrom == 0 && folderTo ==0 ) {
|
||||||
dateRange.allDates = true
|
dateRange.allDates = true
|
||||||
} else {
|
} else {
|
||||||
@ -57,6 +53,15 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getRange(){ // unix time in seconds
|
||||||
|
//console.log(" ==== GET RANGE === ")
|
||||||
|
//console.trace()
|
||||||
|
var folderFrom = dateRange.structure.globalFromDate
|
||||||
|
var folderTo = dateRange.structure.globalToDate
|
||||||
|
|
||||||
|
root.setRangeFromTo(folderFrom, folderTo)
|
||||||
|
}
|
||||||
|
|
||||||
function applyRange(){ // unix time is seconds
|
function applyRange(){ // unix time is seconds
|
||||||
if (dateRange.allDates) structure.setFromToDate(dateRange.sourceID, 0, 0)
|
if (dateRange.allDates) structure.setFromToDate(dateRange.sourceID, 0, 0)
|
||||||
else {
|
else {
|
||||||
@ -67,15 +72,10 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: dateRange
|
|
||||||
onStructureChanged: setRange()
|
|
||||||
}
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
inputDateFrom.updateRange(gui.netBday)
|
inputDateFrom.updateRange(gui.netBday)
|
||||||
inputDateTo.updateRange(new Date())
|
inputDateTo.updateRange(new Date())
|
||||||
setRange()
|
//getRange()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -31,8 +31,10 @@ Rectangle {
|
|||||||
property real padding : Style.dialog.spacing
|
property real padding : Style.dialog.spacing
|
||||||
property bool down : popup.visible
|
property bool down : popup.visible
|
||||||
|
|
||||||
property var structure : structureExternal
|
property var structure : transferRules
|
||||||
property string sourceID : structureExternal.getID(-1)
|
property string sourceID : ""
|
||||||
|
property int sourceFromDate : 0
|
||||||
|
property int sourceToDate : 0
|
||||||
|
|
||||||
color: Style.transparent
|
color: Style.transparent
|
||||||
|
|
||||||
@ -145,7 +147,17 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onAboutToShow : dateRangeInput.setRange()
|
onAboutToShow : updateRange()
|
||||||
onAboutToHide : dateRangeInput.applyRange()
|
onAboutToHide : dateRangeInput.applyRange()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateRange() {
|
||||||
|
dateRangeInput.setRangeFromTo(root.sourceFromDate, root.sourceToDate)
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target:root
|
||||||
|
onSourceFromDateChanged: root.updateRange()
|
||||||
|
onSourceToDateChanged: root.updateRange()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -91,8 +91,6 @@ Dialog {
|
|||||||
|
|
||||||
DateRange{
|
DateRange{
|
||||||
id: dateRangeInput
|
id: dateRangeInput
|
||||||
structure: structurePM
|
|
||||||
sourceID: structurePM.getID(-1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OutputFormat {
|
OutputFormat {
|
||||||
@ -142,7 +140,7 @@ Dialog {
|
|||||||
id: buttonNext
|
id: buttonNext
|
||||||
fa_icon: Style.fa.check
|
fa_icon: Style.fa.check
|
||||||
text: qsTr("Export","todo")
|
text: qsTr("Export","todo")
|
||||||
enabled: structurePM != 0
|
enabled: transferRules != 0
|
||||||
color_main: Style.dialog.background
|
color_main: Style.dialog.background
|
||||||
color_minor: enabled ? Style.dialog.textBlue : Style.main.textDisabled
|
color_minor: enabled ? Style.dialog.textBlue : Style.main.textDisabled
|
||||||
isOpaque: true
|
isOpaque: true
|
||||||
@ -168,13 +166,17 @@ Dialog {
|
|||||||
spacing: Style.main.rightMargin
|
spacing: Style.main.rightMargin
|
||||||
AccessibleText {
|
AccessibleText {
|
||||||
id: statusLabel
|
id: statusLabel
|
||||||
text : qsTr("Exporting to:")
|
text : qsTr("Status:")
|
||||||
font.pointSize: Style.main.iconSize * Style.pt
|
font.pointSize: Style.main.iconSize * Style.pt
|
||||||
color : Style.main.text
|
color : Style.main.text
|
||||||
}
|
}
|
||||||
AccessibleText {
|
AccessibleText {
|
||||||
anchors.baseline: statusLabel.baseline
|
anchors.baseline: statusLabel.baseline
|
||||||
text : go.progressDescription == gui.enums.progressInit ? outputPathInput.path : go.progressDescription
|
text : {
|
||||||
|
if (progressbarExport.isFinished) return qsTr("finished")
|
||||||
|
if (go.progressDescription == "") return qsTr("exporting")
|
||||||
|
return go.progressDescription
|
||||||
|
}
|
||||||
elide: Text.ElideMiddle
|
elide: Text.ElideMiddle
|
||||||
width: progressbarExport.width - parent.spacing - statusLabel.width
|
width: progressbarExport.width - parent.spacing - statusLabel.width
|
||||||
font.pointSize: Style.dialog.textSize * Style.pt
|
font.pointSize: Style.dialog.textSize * Style.pt
|
||||||
@ -310,15 +312,17 @@ Dialog {
|
|||||||
function check_inputs() {
|
function check_inputs() {
|
||||||
if (currentIndex == 1) {
|
if (currentIndex == 1) {
|
||||||
// at least one email to export
|
// at least one email to export
|
||||||
if (structurePM.rowCount() == 0){
|
if (transferRules.rowCount() == 0){
|
||||||
errorPopup.show(qsTr("No emails found to export. Please try another address.", "todo"))
|
errorPopup.show(qsTr("No emails found to export. Please try another address.", "todo"))
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
// at least one source selected
|
// at least one source selected
|
||||||
if (!structurePM.atLeastOneSelected) {
|
/*
|
||||||
errorPopup.show(qsTr("Please select at least one item to export.", "todo"))
|
if (!transferRules.atLeastOneSelected) {
|
||||||
return false
|
errorPopup.show(qsTr("Please select at least one item to export.", "todo"))
|
||||||
}
|
return false
|
||||||
|
}
|
||||||
|
*/
|
||||||
// check path
|
// check path
|
||||||
var folderCheck = go.checkPathStatus(outputPathInput.path)
|
var folderCheck = go.checkPathStatus(outputPathInput.path)
|
||||||
switch (folderCheck) {
|
switch (folderCheck) {
|
||||||
@ -364,7 +368,6 @@ Dialog {
|
|||||||
errorPopup.buttonYes.visible = true
|
errorPopup.buttonYes.visible = true
|
||||||
errorPopup.buttonNo.visible = true
|
errorPopup.buttonNo.visible = true
|
||||||
errorPopup.buttonOkay.visible = false
|
errorPopup.buttonOkay.visible = false
|
||||||
errorPopup.checkbox.text = root.msgClearUnfished
|
|
||||||
errorPopup.show ("Are you sure you want to cancel this export?")
|
errorPopup.show ("Are you sure you want to cancel this export?")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -374,10 +377,7 @@ Dialog {
|
|||||||
case 0 :
|
case 0 :
|
||||||
case 1 : root.hide(); break;
|
case 1 : root.hide(); break;
|
||||||
case 2 : // progress bar
|
case 2 : // progress bar
|
||||||
go.cancelProcess (
|
go.cancelProcess();
|
||||||
errorPopup.checkbox.text == root.msgClearUnfished &&
|
|
||||||
errorPopup.checkbox.checked
|
|
||||||
);
|
|
||||||
// no break
|
// no break
|
||||||
default:
|
default:
|
||||||
root.clear_status()
|
root.clear_status()
|
||||||
@ -395,7 +395,7 @@ Dialog {
|
|||||||
root.hide()
|
root.hide()
|
||||||
break
|
break
|
||||||
case 0: // loading structure
|
case 0: // loading structure
|
||||||
dateRangeInput.setRange()
|
dateRangeInput.getRange()
|
||||||
//no break
|
//no break
|
||||||
default:
|
default:
|
||||||
incrementCurrentIndex()
|
incrementCurrentIndex()
|
||||||
@ -426,7 +426,7 @@ Dialog {
|
|||||||
switch (currentIndex) {
|
switch (currentIndex) {
|
||||||
case 0:
|
case 0:
|
||||||
go.loadStructureForExport(root.address)
|
go.loadStructureForExport(root.address)
|
||||||
sourceFoldersInput.hasItems = (structurePM.rowCount() > 0)
|
sourceFoldersInput.hasItems = (transferRules.rowCount() > 0)
|
||||||
break
|
break
|
||||||
case 2:
|
case 2:
|
||||||
dateRangeInput.applyRange()
|
dateRangeInput.applyRange()
|
||||||
|
|||||||
@ -327,6 +327,7 @@ Dialog {
|
|||||||
iconText: Style.fa.refresh
|
iconText: Style.fa.refresh
|
||||||
textColor: Style.main.textBlue
|
textColor: Style.main.textBlue
|
||||||
onClicked: {
|
onClicked: {
|
||||||
|
go.resetSource()
|
||||||
root.decrementCurrentIndex()
|
root.decrementCurrentIndex()
|
||||||
timer.start()
|
timer.start()
|
||||||
}
|
}
|
||||||
@ -408,20 +409,13 @@ Dialog {
|
|||||||
spacing: Style.main.rightMargin
|
spacing: Style.main.rightMargin
|
||||||
AccessibleText {
|
AccessibleText {
|
||||||
id: statusLabel
|
id: statusLabel
|
||||||
text : qsTr("Importing from:")
|
text : qsTr("Status:")
|
||||||
font.pointSize: Style.main.iconSize * Style.pt
|
font.pointSize: Style.main.iconSize * Style.pt
|
||||||
color : Style.main.text
|
color : Style.main.text
|
||||||
}
|
}
|
||||||
AccessibleText {
|
AccessibleText {
|
||||||
anchors.baseline: statusLabel.baseline
|
anchors.baseline: statusLabel.baseline
|
||||||
text : {
|
text : go.progressDescription == "" ? qsTr("importing") : go.progressDescription
|
||||||
var sourceFolder = root.isFromFile ? root.inputPath : inputEmail.text
|
|
||||||
if (go.progressDescription != gui.enums.progressInit && go.progress!=0) {
|
|
||||||
sourceFolder += "/"
|
|
||||||
sourceFolder += go.progressDescription
|
|
||||||
}
|
|
||||||
return sourceFolder
|
|
||||||
}
|
|
||||||
elide: Text.ElideMiddle
|
elide: Text.ElideMiddle
|
||||||
width: progressbarImport.width - parent.spacing - statusLabel.width
|
width: progressbarImport.width - parent.spacing - statusLabel.width
|
||||||
font.pointSize: Style.dialog.textSize * Style.pt
|
font.pointSize: Style.dialog.textSize * Style.pt
|
||||||
@ -582,9 +576,9 @@ Dialog {
|
|||||||
spacing : Style.dialog.heightSeparator
|
spacing : Style.dialog.heightSeparator
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: Style.fa.check_circle + " " + qsTr("Import completed successfully")
|
text: go.progressDescription!="" ? qsTr("Import failed: %1").arg(go.progressDescription) : Style.fa.check_circle + " " + qsTr("Import completed successfully")
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
color: Style.main.textGreen
|
color: go.progressDescription!="" ? Style.main.textRed : Style.main.textGreen
|
||||||
font.bold : true
|
font.bold : true
|
||||||
font.family: Style.fontawesome.name
|
font.family: Style.fontawesome.name
|
||||||
}
|
}
|
||||||
@ -605,11 +599,7 @@ Dialog {
|
|||||||
text : qsTr("View errors")
|
text : qsTr("View errors")
|
||||||
color_main : Style.dialog.textBlue
|
color_main : Style.dialog.textBlue
|
||||||
onClicked : {
|
onClicked : {
|
||||||
if (go.importLogFileName=="") {
|
go.loadImportReports()
|
||||||
console.log("onViewErrors: missing import log file name")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
go.loadImportReports(go.importLogFileName)
|
|
||||||
reportList.show()
|
reportList.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -619,10 +609,6 @@ Dialog {
|
|||||||
text : qsTr("Report files")
|
text : qsTr("Report files")
|
||||||
color_main : Style.dialog.textBlue
|
color_main : Style.dialog.textBlue
|
||||||
onClicked : {
|
onClicked : {
|
||||||
if (go.importLogFileName=="") {
|
|
||||||
console.log("onReportError: missing import log file name")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
root.ask_send_report()
|
root.ask_send_report()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -755,7 +741,6 @@ Dialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function clear() {
|
function clear() {
|
||||||
go.resetSource()
|
|
||||||
root.inputPath = ""
|
root.inputPath = ""
|
||||||
clear_status()
|
clear_status()
|
||||||
inputEmail.clear()
|
inputEmail.clear()
|
||||||
@ -781,7 +766,7 @@ Dialog {
|
|||||||
onClickedYes : {
|
onClickedYes : {
|
||||||
if (errorPopup.msgID == "ask_send_report") {
|
if (errorPopup.msgID == "ask_send_report") {
|
||||||
errorPopup.hide()
|
errorPopup.hide()
|
||||||
root.report_sent(go.sendImportReport(root.address,go.importLogFileName))
|
root.report_sent(go.sendImportReport(root.address))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
root.cancel()
|
root.cancel()
|
||||||
@ -857,10 +842,13 @@ Dialog {
|
|||||||
}
|
}
|
||||||
break
|
break
|
||||||
case 3: // import insturctions
|
case 3: // import insturctions
|
||||||
if (!structureExternal.hasTarget()) {
|
/*
|
||||||
errorPopup.show(qsTr("Nothing selected for import."))
|
console.log(" ====== TODO ======== ")
|
||||||
return false
|
if (!structureExternal.hasTarget()) {
|
||||||
}
|
errorPopup.show(qsTr("Nothing selected for import."))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
*/
|
||||||
break
|
break
|
||||||
case 4: // import status
|
case 4: // import status
|
||||||
}
|
}
|
||||||
@ -880,7 +868,7 @@ Dialog {
|
|||||||
root.hide()
|
root.hide()
|
||||||
break
|
break
|
||||||
case DialogImport.Page.Progress:
|
case DialogImport.Page.Progress:
|
||||||
go.cancelProcess(false)
|
go.cancelProcess()
|
||||||
root.currentIndex=3
|
root.currentIndex=3
|
||||||
root.clear_status()
|
root.clear_status()
|
||||||
globalLabels.reset()
|
globalLabels.reset()
|
||||||
@ -905,7 +893,7 @@ Dialog {
|
|||||||
globalLabels.labelName,
|
globalLabels.labelName,
|
||||||
globalLabels.labelColor,
|
globalLabels.labelColor,
|
||||||
true,
|
true,
|
||||||
structureExternal.getID(-1)
|
"-1"
|
||||||
)
|
)
|
||||||
if (!isOK) return
|
if (!isOK) return
|
||||||
}
|
}
|
||||||
@ -919,7 +907,8 @@ Dialog {
|
|||||||
|
|
||||||
case DialogImport.Page.LoadingStructure:
|
case DialogImport.Page.LoadingStructure:
|
||||||
globalLabels.reset()
|
globalLabels.reset()
|
||||||
importInstructions.hasItems = (structureExternal.rowCount() > 0)
|
// TODO_: importInstructions.hasItems = (structureExternal.rowCount() > 0)
|
||||||
|
importInstructions.hasItems = true
|
||||||
case DialogImport.Page.ImapSource:
|
case DialogImport.Page.ImapSource:
|
||||||
default:
|
default:
|
||||||
incrementCurrentIndex()
|
incrementCurrentIndex()
|
||||||
@ -1008,7 +997,7 @@ Dialog {
|
|||||||
case DialogImport.Page.SelectSourceType:
|
case DialogImport.Page.SelectSourceType:
|
||||||
case DialogImport.Page.ImapSource:
|
case DialogImport.Page.ImapSource:
|
||||||
case DialogImport.Page.SourceToTarget:
|
case DialogImport.Page.SourceToTarget:
|
||||||
globalDateRange.setRange()
|
globalDateRange.getRange()
|
||||||
break
|
break
|
||||||
case DialogImport.Page.LoadingStructure:
|
case DialogImport.Page.LoadingStructure:
|
||||||
go.setupAndLoadForImport(
|
go.setupAndLoadForImport(
|
||||||
|
|||||||
@ -92,7 +92,7 @@ Rectangle {
|
|||||||
clip : true
|
clip : true
|
||||||
orientation : ListView.Vertical
|
orientation : ListView.Vertical
|
||||||
boundsBehavior : Flickable.StopAtBounds
|
boundsBehavior : Flickable.StopAtBounds
|
||||||
model : structurePM
|
model : transferRules
|
||||||
cacheBuffer : 10000
|
cacheBuffer : 10000
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
@ -125,27 +125,25 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
delegate: FolderRowButton {
|
delegate: FolderRowButton {
|
||||||
|
property variant modelData: model
|
||||||
width : root.width - 5*root.border.width
|
width : root.width - 5*root.border.width
|
||||||
type : folderType
|
type : modelData.type
|
||||||
color : folderColor
|
folderIconColor : modelData.iconColor
|
||||||
title : folderName
|
title : modelData.name
|
||||||
isSelected : isFolderSelected
|
isSelected : modelData.isActive
|
||||||
onClicked : {
|
onClicked : {
|
||||||
//console.log("Clicked", folderId, isSelected)
|
//console.log("Clicked", folderId, isSelected)
|
||||||
structurePM.setFolderSelection(folderId,!isSelected)
|
transferRules.setIsRuleActive(modelData.mboxID,!model.isActive)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
section.property: "folderType"
|
section.property: "type"
|
||||||
section.delegate: FolderRowButton {
|
section.delegate: FolderRowButton {
|
||||||
isSection : true
|
isSection : true
|
||||||
width : root.width - 5*root.border.width
|
width : root.width - 5*root.border.width
|
||||||
title : gui.folderTypeTitle(section)
|
title : gui.folderTypeTitle(section)
|
||||||
isSelected : {
|
isSelected : section == gui.enums.folderTypeLabel ? transferRules.isLabelGroupSelected : transferRules.isFolderGroupSelected
|
||||||
//console.log("section selected changed: ", section)
|
onClicked : transferRules.setIsGroupActive(section,!isSelected)
|
||||||
return section == gui.enums.folderTypeLabel ? structurePM.selectedLabels : structurePM.selectedFolders
|
|
||||||
}
|
|
||||||
onClicked : structurePM.selectType(section,!isSelected)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,9 +26,9 @@ AccessibleButton {
|
|||||||
|
|
||||||
property bool isSection : false
|
property bool isSection : false
|
||||||
property bool isSelected : false
|
property bool isSelected : false
|
||||||
property string title : "N/A"
|
property string title : "N/A"
|
||||||
property string type : ""
|
property string type : ""
|
||||||
property color color : "black"
|
property string folderIconColor : Style.main.textBlue
|
||||||
|
|
||||||
height : Style.exporting.rowHeight
|
height : Style.exporting.rowHeight
|
||||||
padding : 0.0
|
padding : 0.0
|
||||||
@ -72,7 +72,7 @@ AccessibleButton {
|
|||||||
left : checkbox.left
|
left : checkbox.left
|
||||||
leftMargin : Style.dialog.fontSize + Style.exporting.leftMargin
|
leftMargin : Style.dialog.fontSize + Style.exporting.leftMargin
|
||||||
}
|
}
|
||||||
color : root.type==gui.enums.folderTypeSystem ? Style.main.textBlue : root.color
|
color : root.type=="" ? Style.main.textBlue : root.folderIconColor
|
||||||
font {
|
font {
|
||||||
family : Style.fontawesome.name
|
family : Style.fontawesome.name
|
||||||
pointSize : Style.dialog.fontSize * Style.pt
|
pointSize : Style.dialog.fontSize * Style.pt
|
||||||
|
|||||||
@ -39,7 +39,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
property real iconWidth : nameWidth*0.3
|
property real iconWidth : nameWidth*0.3
|
||||||
|
|
||||||
property bool isSourceSelected: targetFolderID!=""
|
property bool isSourceSelected: isActive
|
||||||
property string lastTargetFolder: "6" // Archive
|
property string lastTargetFolder: "6" // Archive
|
||||||
property string lastTargetLabels: "" // no flag by default
|
property string lastTargetLabels: "" // no flag by default
|
||||||
|
|
||||||
@ -71,7 +71,7 @@ Rectangle {
|
|||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: folderIcon
|
id: folderIcon
|
||||||
text : gui.folderIcon(folderName, gui.enums.folderTypeFolder)
|
text : gui.folderIcon(name, gui.enums.folderTypeFolder)
|
||||||
anchors.verticalCenter : parent.verticalCenter
|
anchors.verticalCenter : parent.verticalCenter
|
||||||
color: root.isSourceSelected ? Style.main.text : Style.main.textDisabled
|
color: root.isSourceSelected ? Style.main.text : Style.main.textDisabled
|
||||||
font {
|
font {
|
||||||
@ -81,7 +81,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text : folderName
|
text : name
|
||||||
width: nameWidth
|
width: nameWidth
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
anchors.verticalCenter : parent.verticalCenter
|
anchors.verticalCenter : parent.verticalCenter
|
||||||
@ -102,24 +102,27 @@ Rectangle {
|
|||||||
|
|
||||||
SelectFolderMenu {
|
SelectFolderMenu {
|
||||||
id: selectFolder
|
id: selectFolder
|
||||||
sourceID: folderId
|
sourceID: mboxID
|
||||||
selectedIDs: targetFolderID
|
targets: transferRules.targetFolders(mboxID)
|
||||||
width: nameWidth
|
width: nameWidth
|
||||||
anchors.verticalCenter : parent.verticalCenter
|
anchors.verticalCenter : parent.verticalCenter
|
||||||
|
enabled: root.isSourceSelected
|
||||||
onDoNotImport: root.toggleImport()
|
onDoNotImport: root.toggleImport()
|
||||||
onImportToFolder: root.importToFolder(newTargetID)
|
onImportToFolder: root.importToFolder(newTargetID)
|
||||||
}
|
}
|
||||||
|
|
||||||
SelectLabelsMenu {
|
SelectLabelsMenu {
|
||||||
sourceID: folderId
|
sourceID: mboxID
|
||||||
selectedIDs: targetLabelIDs
|
targets: transferRules.targetLabels(mboxID)
|
||||||
width: nameWidth
|
width: nameWidth
|
||||||
anchors.verticalCenter : parent.verticalCenter
|
anchors.verticalCenter : parent.verticalCenter
|
||||||
enabled: root.isSourceSelected
|
enabled: root.isSourceSelected
|
||||||
|
onAddTargetLabel: { transferRules.addTargetID(sourceID, newTargetID) }
|
||||||
|
onRemoveTargetLabel: { transferRules.removeTargetID(sourceID, newTargetID) }
|
||||||
}
|
}
|
||||||
|
|
||||||
LabelIconList {
|
LabelIconList {
|
||||||
selectedIDs: targetLabelIDs
|
colorList: labelColors=="" ? [] : labelColors.split(";")
|
||||||
width: iconWidth
|
width: iconWidth
|
||||||
anchors.verticalCenter : parent.verticalCenter
|
anchors.verticalCenter : parent.verticalCenter
|
||||||
enabled: root.isSourceSelected
|
enabled: root.isSourceSelected
|
||||||
@ -127,38 +130,23 @@ Rectangle {
|
|||||||
|
|
||||||
DateRangeMenu {
|
DateRangeMenu {
|
||||||
id: dateRangeMenu
|
id: dateRangeMenu
|
||||||
sourceID: folderId
|
sourceID: mboxID
|
||||||
|
sourceFromDate: fromDate
|
||||||
|
sourceToDate: toDate
|
||||||
|
|
||||||
enabled: root.isSourceSelected
|
enabled: root.isSourceSelected
|
||||||
anchors.verticalCenter : parent.verticalCenter
|
anchors.verticalCenter : parent.verticalCenter
|
||||||
|
|
||||||
|
Component.onCompleted : dateRangeMenu.updateRange()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function importToFolder(newTargetID) {
|
function importToFolder(newTargetID) {
|
||||||
if (root.isSourceSelected) {
|
transferRules.addTargetID(mboxID,newTargetID)
|
||||||
structureExternal.setTargetFolderID(folderId,newTargetID)
|
|
||||||
} else {
|
|
||||||
lastTargetFolder = newTargetID
|
|
||||||
toggleImport()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleImport() {
|
function toggleImport() {
|
||||||
if (root.isSourceSelected) {
|
transferRules.setIsRuleActive(mboxID, !root.isSourceSelected)
|
||||||
lastTargetFolder = targetFolderID
|
|
||||||
lastTargetLabels = targetLabelIDs
|
|
||||||
structureExternal.setTargetFolderID(folderId,"")
|
|
||||||
return Qt.Unchecked
|
|
||||||
} else {
|
|
||||||
structureExternal.setTargetFolderID(folderId,lastTargetFolder)
|
|
||||||
var labelsSplit = lastTargetLabels.split(";")
|
|
||||||
for (var labelIndex in labelsSplit) {
|
|
||||||
var labelID = labelsSplit[labelIndex]
|
|
||||||
structureExternal.addTargetLabelID(folderId,labelID)
|
|
||||||
}
|
|
||||||
return Qt.Checked
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -50,7 +50,6 @@ Rectangle {
|
|||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
text: qsTr("No emails found for this source.","todo")
|
text: qsTr("No emails found for this source.","todo")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
@ -70,7 +69,7 @@ Rectangle {
|
|||||||
clip : true
|
clip : true
|
||||||
orientation : ListView.Vertical
|
orientation : ListView.Vertical
|
||||||
boundsBehavior : Flickable.StopAtBounds
|
boundsBehavior : Flickable.StopAtBounds
|
||||||
model : structureExternal
|
model : transferRules
|
||||||
cacheBuffer : 10000
|
cacheBuffer : 10000
|
||||||
delegate : ImportDelegate {
|
delegate : ImportDelegate {
|
||||||
width: root.width
|
width: root.width
|
||||||
|
|||||||
@ -25,8 +25,8 @@ import ImportExportUI 1.0
|
|||||||
Row {
|
Row {
|
||||||
id: dateRange
|
id: dateRange
|
||||||
|
|
||||||
property var structure : structureExternal
|
property var structure : transferRules
|
||||||
property string sourceID : structureExternal.getID ( -1 )
|
property string sourceID : "-1"
|
||||||
|
|
||||||
property alias allDates : allDatesBox.checked
|
property alias allDates : allDatesBox.checked
|
||||||
property alias inputDateFrom : inputDateFrom
|
property alias inputDateFrom : inputDateFrom
|
||||||
@ -34,7 +34,7 @@ Row {
|
|||||||
|
|
||||||
property alias labelWidth: label.width
|
property alias labelWidth: label.width
|
||||||
|
|
||||||
function setRange() {common.setRange()}
|
function getRange() {common.getRange()}
|
||||||
function applyRange() {common.applyRange()}
|
function applyRange() {common.applyRange()}
|
||||||
|
|
||||||
DateRangeFunctions {id:common}
|
DateRangeFunctions {id:common}
|
||||||
|
|||||||
@ -26,42 +26,16 @@ Rectangle {
|
|||||||
id: root
|
id: root
|
||||||
width: Style.main.fontSize * 2
|
width: Style.main.fontSize * 2
|
||||||
height: metrics.height
|
height: metrics.height
|
||||||
property string selectedIDs : ""
|
property var colorList
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
DelegateModel {
|
DelegateModel {
|
||||||
id: selectedLabels
|
id: selectedLabels
|
||||||
filterOnGroup: "selected"
|
model : colorList
|
||||||
groups: DelegateModelGroup {
|
|
||||||
id: selected
|
|
||||||
name: "selected"
|
|
||||||
includeByDefault: true
|
|
||||||
}
|
|
||||||
model : structurePM
|
|
||||||
delegate : Text {
|
delegate : Text {
|
||||||
text : metrics.text
|
text : metrics.text
|
||||||
font : metrics.font
|
font : metrics.font
|
||||||
color : folderColor===undefined ? "#000": folderColor
|
color : modelData
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateFilter() {
|
|
||||||
var selected = root.selectedIDs.split(";")
|
|
||||||
var rowCount = selectedLabels.items.count
|
|
||||||
//console.log(" log ::", root.selectedIDs, rowCount, selectedLabels.model)
|
|
||||||
// filter
|
|
||||||
for (var iItem = 0; iItem < rowCount; iItem++) {
|
|
||||||
var entry = selectedLabels.items.get(iItem);
|
|
||||||
//console.log(" log filter ", iItem, rowCount, entry.model.folderId, entry.model.folderType, selected[iSel], entry.inSelected )
|
|
||||||
for (var iSel in selected) {
|
|
||||||
entry.inSelected = (
|
|
||||||
entry.model.folderType == gui.enums.folderTypeLabel &&
|
|
||||||
entry.model.folderId == selected[iSel]
|
|
||||||
)
|
|
||||||
if (entry.inSelected) break // found match, skip rest
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,7 +51,7 @@ Rectangle {
|
|||||||
Row {
|
Row {
|
||||||
anchors.left : root.left
|
anchors.left : root.left
|
||||||
spacing : {
|
spacing : {
|
||||||
var n = Math.max(2,selectedLabels.count)
|
var n = Math.max(2,root.colorList.length)
|
||||||
var tagWidth = Math.max(1.0,metrics.width)
|
var tagWidth = Math.max(1.0,metrics.width)
|
||||||
var space = Math.min(1*Style.px, (root.width - n*tagWidth)/(n-1)) // not more than 1px
|
var space = Math.min(1*Style.px, (root.width - n*tagWidth)/(n-1)) // not more than 1px
|
||||||
space = Math.max(space,-tagWidth) // not less than tag width
|
space = Math.max(space,-tagWidth) // not less than tag width
|
||||||
@ -88,9 +62,4 @@ Rectangle {
|
|||||||
model: selectedLabels
|
model: selectedLabels
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: root.updateFilter()
|
|
||||||
onSelectedIDsChanged: root.updateFilter()
|
|
||||||
Connections { target: structurePM; onDataChanged:root.updateFilter() }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -26,19 +26,19 @@ ComboBox {
|
|||||||
//fixme rounded
|
//fixme rounded
|
||||||
height: Style.main.fontSize*2 //fixme
|
height: Style.main.fontSize*2 //fixme
|
||||||
property string folderType: gui.enums.folderTypeFolder
|
property string folderType: gui.enums.folderTypeFolder
|
||||||
property string selectedIDs
|
|
||||||
property string sourceID
|
property string sourceID
|
||||||
|
property var targets
|
||||||
property bool isFolderType: root.folderType == gui.enums.folderTypeFolder
|
property bool isFolderType: root.folderType == gui.enums.folderTypeFolder
|
||||||
property bool hasTarget: root.selectedIDs != ""
|
|
||||||
property bool below: true
|
property bool below: true
|
||||||
|
|
||||||
signal doNotImport()
|
signal doNotImport()
|
||||||
signal importToFolder(string newTargetID)
|
signal importToFolder(string newTargetID)
|
||||||
|
signal addTargetLabel(string newTargetID)
|
||||||
|
signal removeTargetLabel(string newTargetID)
|
||||||
|
|
||||||
leftPadding: Style.dialog.spacing
|
leftPadding: Style.dialog.spacing
|
||||||
|
|
||||||
onDownChanged : {
|
onDownChanged : {
|
||||||
if (root.down) view.model.updateFilter()
|
|
||||||
root.below = popup.y>0
|
root.below = popup.y>0
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,30 +58,22 @@ ComboBox {
|
|||||||
}
|
}
|
||||||
|
|
||||||
displayText: {
|
displayText: {
|
||||||
//console.trace()
|
console.log("Target Menu current", view.currentItem, view.currentIndex)
|
||||||
//console.log("updatebox", view.currentIndex, root.hasTarget, root.selectedIDs, root.sourceID, root.folderType)
|
if (view.currentIndex >= 0) {
|
||||||
if (!root.hasTarget) {
|
if (!root.isFolderType) return Style.fa.tags + " " + qsTr("Add/Remove labels")
|
||||||
if (root.isFolderType) return qsTr("Do not import")
|
|
||||||
return qsTr("No labels selected")
|
|
||||||
}
|
|
||||||
if (!root.isFolderType) return Style.fa.tags + " " + qsTr("Add/Remove labels")
|
|
||||||
|
|
||||||
// We know here that it has a target and this is folder dropdown so we must find the first folder
|
var tgtName = view.currentItem.folderName
|
||||||
var selSplit = root.selectedIDs.split(";")
|
var tgtIcon = view.currentItem.folderIcon
|
||||||
for (var selIndex in selSplit) {
|
var tgtColor = view.currentItem.folderColor
|
||||||
var selectedID = selSplit[selIndex]
|
|
||||||
var selectedType = structurePM.getType(selectedID)
|
if (tgtIcon != Style.fa.folder_open) {
|
||||||
if (selectedType == gui.enums.folderTypeLabel) continue; // skip type::labele
|
return tgtIcon + " " + tgtName
|
||||||
var selectedName = structurePM.getName(selectedID)
|
|
||||||
if (selectedName == "") continue; // empty name seems like wrong ID
|
|
||||||
var icon = gui.folderIcon(selectedName, selectedType)
|
|
||||||
if (selectedType == gui.enums.folderTypeSystem) {
|
|
||||||
return icon + " " + selectedName
|
|
||||||
}
|
}
|
||||||
var iconColor = structurePM.getColor(selectedID)
|
|
||||||
return '<font color="'+iconColor+'">'+ icon + "</font> " + selectedName
|
return '<font color="'+tgtColor+'">'+ tgtIcon + "</font> " + tgtName
|
||||||
}
|
}
|
||||||
return ""
|
if (root.isFolderType) return qsTr("No folder selected")
|
||||||
|
return qsTr("No labels selected")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -116,7 +108,7 @@ ComboBox {
|
|||||||
color: root.enabled && !root.down ? Style.main.textBlue : root.contentItem.color
|
color: root.enabled && !root.down ? Style.main.textBlue : root.contentItem.color
|
||||||
}
|
}
|
||||||
|
|
||||||
// Popup objects
|
// Popup row
|
||||||
delegate: Rectangle {
|
delegate: Rectangle {
|
||||||
id: thisDelegate
|
id: thisDelegate
|
||||||
|
|
||||||
@ -127,22 +119,15 @@ ComboBox {
|
|||||||
|
|
||||||
color: isHovered ? root.popup.hoverColor : root.popup.backColor
|
color: isHovered ? root.popup.hoverColor : root.popup.backColor
|
||||||
|
|
||||||
|
property bool isSelected : isActive
|
||||||
property bool isSelected : {
|
property string folderName: name
|
||||||
var selected = root.selectedIDs.split(";")
|
property string folderIcon: gui.folderIcon(name,type)
|
||||||
for (var iSel in selected) {
|
property string folderColor: (type == gui.enums.folderTypeLabel || type == gui.enums.folderTypeFolder) ? iconColor : root.popup.textColor
|
||||||
var sel = selected[iSel]
|
|
||||||
if (folderId == sel){
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: targetIcon
|
id: targetIcon
|
||||||
text: gui.folderIcon(folderName,folderType)
|
text: thisDelegate.folderIcon
|
||||||
color : folderType != gui.enums.folderTypeSystem ? folderColor : root.popup.textColor
|
color : thisDelegate.folderColor
|
||||||
anchors {
|
anchors {
|
||||||
verticalCenter: parent.verticalCenter
|
verticalCenter: parent.verticalCenter
|
||||||
left: parent.left
|
left: parent.left
|
||||||
@ -157,6 +142,7 @@ ComboBox {
|
|||||||
Text {
|
Text {
|
||||||
id: targetName
|
id: targetName
|
||||||
|
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
verticalCenter: parent.verticalCenter
|
verticalCenter: parent.verticalCenter
|
||||||
left: targetIcon.right
|
left: targetIcon.right
|
||||||
@ -165,7 +151,7 @@ ComboBox {
|
|||||||
rightMargin: Style.dialog.spacing
|
rightMargin: Style.dialog.spacing
|
||||||
}
|
}
|
||||||
|
|
||||||
text: folderName
|
text: thisDelegate.folderName
|
||||||
color : root.popup.textColor
|
color : root.popup.textColor
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
|
|
||||||
@ -209,16 +195,15 @@ ComboBox {
|
|||||||
onClicked: {
|
onClicked: {
|
||||||
//console.log(" click delegate")
|
//console.log(" click delegate")
|
||||||
if (root.isFolderType) { // don't update if selected
|
if (root.isFolderType) { // don't update if selected
|
||||||
if (!thisDelegate.isSelected) {
|
|
||||||
root.importToFolder(folderId)
|
|
||||||
}
|
|
||||||
root.popup.close()
|
root.popup.close()
|
||||||
}
|
if (!isActive) {
|
||||||
if (root.folderType==gui.enums.folderTypeLabel) {
|
root.importToFolder(mboxID)
|
||||||
if (thisDelegate.isSelected) {
|
}
|
||||||
structureExternal.removeTargetLabelID(sourceID,folderId)
|
} else {
|
||||||
|
if (isActive) {
|
||||||
|
root.removeTargetLabel(mboxID)
|
||||||
} else {
|
} else {
|
||||||
structureExternal.addTargetLabelID(sourceID,folderId)
|
root.addTargetLabel(mboxID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -295,14 +280,10 @@ ComboBox {
|
|||||||
|
|
||||||
clip : true
|
clip : true
|
||||||
anchors.fill : parent
|
anchors.fill : parent
|
||||||
|
model : root.targets
|
||||||
|
delegate : root.delegate
|
||||||
|
|
||||||
section.property : "sectionName"
|
currentIndex: view.model.selectedIndex
|
||||||
section.delegate : Text{text: sectionName}
|
|
||||||
|
|
||||||
model : FilterStructure {
|
|
||||||
filterOnGroup : root.folderType
|
|
||||||
delegate : root.delegate
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -338,10 +319,7 @@ ComboBox {
|
|||||||
|
|
||||||
onClicked : {
|
onClicked : {
|
||||||
//console.log("click", addButton.text)
|
//console.log("click", addButton.text)
|
||||||
var newName = ""
|
var newName = name
|
||||||
if ( typeof folderName !== 'undefined' && !structurePM.hasFolderWithName (folderName) ) {
|
|
||||||
newName = folderName
|
|
||||||
}
|
|
||||||
winMain.popupFolderEdit.show(newName, "", "", root.folderType, sourceID)
|
winMain.popupFolderEdit.show(newName, "", "", root.folderType, sourceID)
|
||||||
root.popup.close()
|
root.popup.close()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -210,7 +210,7 @@ Window {
|
|||||||
|
|
||||||
|
|
||||||
Component.onCompleted : {
|
Component.onCompleted : {
|
||||||
testgui.winMain.x = 150
|
testgui.winMain.x = 350
|
||||||
testgui.winMain.y = 100
|
testgui.winMain.y = 100
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,7 +230,7 @@ Window {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ListModel{
|
ListModel{
|
||||||
id: structureExternal
|
id: structureExternalOFF
|
||||||
|
|
||||||
property var globalOptions: JSON.parse('{ "folderId" : "global--uniq" , "folderName" : "" , "folderColor" : "" , "folderType" : "" , "folderEntries" : 0, "fromDate": 0, "toDate": 0, "isFolderSelected" : false , "targetFolderID": "14" , "targetLabelIDs": ";20;29" }')
|
property var globalOptions: JSON.parse('{ "folderId" : "global--uniq" , "folderName" : "" , "folderColor" : "" , "folderType" : "" , "folderEntries" : 0, "fromDate": 0, "toDate": 0, "isFolderSelected" : false , "targetFolderID": "14" , "targetLabelIDs": ";20;29" }')
|
||||||
|
|
||||||
@ -265,7 +265,7 @@ Window {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ListModel{
|
ListModel{
|
||||||
id: structurePM
|
id: structurePMOFF
|
||||||
|
|
||||||
// group selectors
|
// group selectors
|
||||||
property bool selectedLabels : false
|
property bool selectedLabels : false
|
||||||
@ -328,6 +328,7 @@ Window {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function setTypeSelected (model, folderType , toSelect ) {
|
function setTypeSelected (model, folderType , toSelect ) {
|
||||||
console.log(" select type ", folderType, toSelect)
|
console.log(" select type ", folderType, toSelect)
|
||||||
for (var i= -1; i<model.count; i++) {
|
for (var i= -1; i<model.count; i++) {
|
||||||
@ -457,6 +458,355 @@ Window {
|
|||||||
ListElement{ mailSubject : "Pop art is cool again" ; mailDate : "March 2 , 2019 12 : 00 : 22" ; inputFolder : "Archive" ; mailFrom : "me@me.me" ; errorMessage : "Something went wrong and import retry was not successful" ; }
|
ListElement{ mailSubject : "Pop art is cool again" ; mailDate : "March 2 , 2019 12 : 00 : 22" ; inputFolder : "Archive" ; mailFrom : "me@me.me" ; errorMessage : "Something went wrong and import retry was not successful" ; }
|
||||||
ListElement{ mailSubject : "Check this cute kittens play volleyball on Copacabanana beach" ; mailDate : "March 2 , 2019 12 : 00 : 22" ; inputFolder : "Archive" ; mailFrom : "me@me.me" ; errorMessage : "Something went wrong and import retry was not successful" ; }
|
ListElement{ mailSubject : "Check this cute kittens play volleyball on Copacabanana beach" ; mailDate : "March 2 , 2019 12 : 00 : 22" ; inputFolder : "Archive" ; mailFrom : "me@me.me" ; errorMessage : "Something went wrong and import retry was not successful" ; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Transfer rules
|
||||||
|
ListModel {
|
||||||
|
id: transferRules
|
||||||
|
|
||||||
|
property var targets : new Object();
|
||||||
|
|
||||||
|
// test data for import
|
||||||
|
property var importRules : JSON.parse('[
|
||||||
|
{"isActive" : true , "mboxID" : "src1" , "fromDate" : 0 , "toDate" : 0 , "targetIDs" : [ "0" , "label1" ] } ,
|
||||||
|
{"isActive" : true , "mboxID" : "src2" , "fromDate" : 0 , "toDate" : 0 , "targetIDs" : [ "6" , "label2" ] } ,
|
||||||
|
{"isActive" : true , "mboxID" : "src3" , "fromDate" : 350000 , "toDate" : 5000000 , "targetIDs" : [ "folder1" ] } ,
|
||||||
|
{"isActive" : true , "mboxID" : "src4" , "fromDate" : 0 , "toDate" : 0 , "targetIDs" : [ "folder2" , "label1" , "label2" ] }
|
||||||
|
]')
|
||||||
|
|
||||||
|
property var selectedForExport : [ "0", "7", "folder1", "folde2", "label1", "label2", "label3"]
|
||||||
|
|
||||||
|
property var extMailboxes: JSON.parse('{
|
||||||
|
"src1": {"name" : "Source Inbox" , "type" : "external" , "color" : "#000"} ,
|
||||||
|
"src2": {"name" : "Source Sent" , "type" : "external" , "color" : "#000"} ,
|
||||||
|
"src3": {"name" : "Source Folder" , "type" : "external" , "color" : "#000"} ,
|
||||||
|
"src4": {"name" : "Source Trash" , "type" : "external" , "color" : "#000"}
|
||||||
|
}')
|
||||||
|
|
||||||
|
property var pmMailboxes : JSON.parse('{
|
||||||
|
"0": {"name" : "Inbox" , "type" : "system" , "color" : "#000"} ,
|
||||||
|
"3": {"name" : "Draft" , "type" : "system" , "color" : "#000"} ,
|
||||||
|
"6": {"name" : "Archive" , "type" : "system" , "color" : "#000"} ,
|
||||||
|
"5": {"name" : "All Mail" , "type" : "system" , "color" : "#000"} ,
|
||||||
|
"3": {"name" : "Trash" , "type" : "system" , "color" : "#000"} ,
|
||||||
|
"7": {"name" : "Sent" , "type" : "system" , "color" : "#000"} ,
|
||||||
|
"4": {"name" : "Spam" , "type" : "system" , "color" : "#000"} ,
|
||||||
|
|
||||||
|
"folder1": {"name": "Folder 1", "type":"folder", "color":"#57c"},
|
||||||
|
"folder2": {"name": "Folder 2", "type":"folder", "color":"#5c7"},
|
||||||
|
"folder3": {"name": "Folder 3", "type":"folder", "color":"#c57"},
|
||||||
|
|
||||||
|
"label1": {"name": "Label 1", "type":"label", "color":"#a5a"},
|
||||||
|
"label2": {"name": "Label 2", "type":"label", "color":"#5aa"},
|
||||||
|
"label3": {"name": "Label 3", "type":"label", "color":"#aa5"}
|
||||||
|
}')
|
||||||
|
|
||||||
|
ListElement{isActive : true ; mboxID : "source1" ; name : "Source folder 1" ; iconColor : "#cccccc" ; type : "external" ; fromDate : 0 ; toDate : 0 ; labelColors : "red ; green ; blue"}
|
||||||
|
ListElement{isActive : false ; mboxID : "source2" ; name : "Source folder 2" ; iconColor : "#cccccc" ; type : "external" ; fromDate : 300000 ; toDate : 15000000 ; labelColors : "red ; green ; blue"}
|
||||||
|
ListElement{isActive : true ; mboxID : "source3" ; name : "Source folder 4" ; iconColor : "#cccccc" ; type : "external" ; fromDate : 0 ; toDate : 0 ; labelColors : "red ; green ; blue"}
|
||||||
|
|
||||||
|
|
||||||
|
// TransferRules INTERFACE
|
||||||
|
|
||||||
|
// TransferRules properties
|
||||||
|
property int globalFromDate : 0 // 45000
|
||||||
|
property int globalToDate : 0 // 120000
|
||||||
|
property bool isLabelGroupSelected : false
|
||||||
|
property bool isFolderGroupSelected : false
|
||||||
|
|
||||||
|
// TransferRules default getters
|
||||||
|
// func (*TransferRules) count() int
|
||||||
|
// func (*TransferRules) roleNames() map[int] QByteArray
|
||||||
|
// func (*TransferRules) data(index, role) *QVariant
|
||||||
|
//
|
||||||
|
// Expected roles for TransferRules
|
||||||
|
//
|
||||||
|
// isActive bool
|
||||||
|
// mboxID string // constant
|
||||||
|
// name string // constant
|
||||||
|
// type string // constant, expected values: "label", "folder", ""
|
||||||
|
// iconColor string // constant
|
||||||
|
// fromDate int64
|
||||||
|
// toDate int64
|
||||||
|
// labelColors string // list of hex RGB strings delimited by `;`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// TransferRules custom getters
|
||||||
|
|
||||||
|
function targetFolders(sourceID) {
|
||||||
|
//ListElement{isActive: true; mboxID: "target1"; name: "Target system folder"; type: "system"; iconColor:"red"}
|
||||||
|
return getTargets(sourceID, "folder")
|
||||||
|
}
|
||||||
|
|
||||||
|
function targetLabels(sourceID) {
|
||||||
|
//ListElement{isActive: false; mboxID: "target3"; name: "Target custom label 1"; type: "label"; iconColor:"green"}
|
||||||
|
return getTargets(sourceID, "label")
|
||||||
|
}
|
||||||
|
|
||||||
|
// For target drop down menu (labels and folders) we need
|
||||||
|
// additional model TargetList (QAbstractListModel).
|
||||||
|
//
|
||||||
|
// func (*TransferRules) targetFolders(sourceID string) * QAbstractListModel
|
||||||
|
//
|
||||||
|
// There is no setter functions for this list all actions are
|
||||||
|
// handled by TransferRules.
|
||||||
|
//
|
||||||
|
// The target models have therefore only data functions and one property as interface:
|
||||||
|
//
|
||||||
|
// TargetList properties:
|
||||||
|
// property int selectedIndex : -1
|
||||||
|
//
|
||||||
|
// TargetList default getters
|
||||||
|
// func (*TargetList) count() int
|
||||||
|
// func (*TargetList) roleNames() map[int] QByteArray
|
||||||
|
// func (*TargetList) data(index, role) *QVariant
|
||||||
|
//
|
||||||
|
// Expected roles for TargetList
|
||||||
|
//
|
||||||
|
// isActive bool
|
||||||
|
// mboxID string // constant
|
||||||
|
// name string // constant
|
||||||
|
// type string // constant, expected values: "label", "folder", ""
|
||||||
|
// iconColor string // constant
|
||||||
|
//
|
||||||
|
// The tricky part here is the QAbstractListModel implemetation: it
|
||||||
|
// needs to return all targets of certain type and their index.
|
||||||
|
|
||||||
|
// Setters
|
||||||
|
function setIsRuleActive(srcID, isActive){
|
||||||
|
console.log("setIsRuleActive", srcID, isActive)
|
||||||
|
var groupLabelsSelected = true
|
||||||
|
var groupFoldersSelected = true
|
||||||
|
for (var i = 0; i < transferRules.count; i++) {
|
||||||
|
var rule = transferRules.get(i)
|
||||||
|
if (rule.mboxID ==srcID) rule.isActive = isActive;
|
||||||
|
if (!rule.isActive && rule.type == "label") groupLabelsSelected = false
|
||||||
|
if (!rule.isActive && rule.type == "folder") groupFoldersSelected = false
|
||||||
|
}
|
||||||
|
transferRules.isLabelGroupSelected = groupLabelsSelected
|
||||||
|
transferRules.isFolderGroupSelected = groupFoldersSelected
|
||||||
|
}
|
||||||
|
|
||||||
|
function setIsGroupActive(groupName,isActive){
|
||||||
|
console.log("setIsGroupActive", groupName, isActive)
|
||||||
|
var groupLabelsSelected = true
|
||||||
|
var groupFoldersSelected = true
|
||||||
|
for (var i = 0; i < transferRules.count; i++) {
|
||||||
|
var rule = transferRules.get(i)
|
||||||
|
if (rule.type == groupName) rule.isActive = isActive;
|
||||||
|
if (!rule.isActive && rule.type == "label") groupLabelsSelected = false
|
||||||
|
if (!rule.isActive && rule.type == "folder") groupFoldersSelected = false
|
||||||
|
}
|
||||||
|
transferRules.isLabelGroupSelected = groupLabelsSelected
|
||||||
|
transferRules.isFolderGroupSelected = groupFoldersSelected
|
||||||
|
}
|
||||||
|
|
||||||
|
function setFromToDate(srcID, fromDate, toDate){
|
||||||
|
console.log("setFromToDate", srcID, fromDate, toDate)
|
||||||
|
for (var i = 0; i < transferRules.count; i++) {
|
||||||
|
var rule = transferRules.get(i)
|
||||||
|
if (rule.mboxID ==srcID) {
|
||||||
|
rule.fromDate = fromDate
|
||||||
|
rule.toDate = toDate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function addTargetID(srcID, targetID){
|
||||||
|
console.log("addTargetID", srcID, targetID)
|
||||||
|
changeTargetID(srcID, targetID, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeTargetID(srcID, targetID){
|
||||||
|
console.log("removeTargetID", srcID, targetID)
|
||||||
|
changeTargetID(srcID, targetID, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MOCK METHODS: NOT PART OF INTERFACE
|
||||||
|
|
||||||
|
Component.onCompleted: prepareImport()
|
||||||
|
|
||||||
|
// Fill model with import rules
|
||||||
|
function prepareImport() {
|
||||||
|
console.log(" ==== Prepare IMPORT ==== ")
|
||||||
|
console.trace()
|
||||||
|
transferRules.clear()
|
||||||
|
for (var ruleI in transferRules.importRules) {
|
||||||
|
var rule = transferRules.importRules[ruleI]
|
||||||
|
var src = transferRules.extMailboxes[rule.mboxID];
|
||||||
|
|
||||||
|
var labelColors = [];
|
||||||
|
for (var tid in rule.targetIDs) {
|
||||||
|
var targetID = rule.targetIDs[tid]
|
||||||
|
if (pmMailboxes[targetID].type == "label") {
|
||||||
|
labelColors.push(pmMailboxes[targetID]["color"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
transferRules.append({
|
||||||
|
"isActive" : rule.isActive,
|
||||||
|
"mboxID" : rule.mboxID,
|
||||||
|
"name" : src.name,
|
||||||
|
"type" : src.type,
|
||||||
|
"iconColor" : src["color"],
|
||||||
|
"fromDate" : rule.fromDate,
|
||||||
|
"toDate" : rule.toDate,
|
||||||
|
"labelColors" : labelColors.join(";"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill model with export rules
|
||||||
|
function prepareExport() {
|
||||||
|
console.log(" ==== Prepare EXPORT ==== ")
|
||||||
|
console.trace()
|
||||||
|
transferRules.clear()
|
||||||
|
var groupLabelsSelected = true
|
||||||
|
var groupFoldersSelected = true
|
||||||
|
for (var srcID in transferRules.pmMailboxes) {
|
||||||
|
var src = transferRules.pmMailboxes[srcID]
|
||||||
|
|
||||||
|
var isActive = transferRules.selectedForExport.find(function(mboxID){return mboxID == srcID}) !== undefined
|
||||||
|
|
||||||
|
transferRules.append({
|
||||||
|
"isActive" : isActive,
|
||||||
|
"mboxID" : srcID,
|
||||||
|
"name" : src.name,
|
||||||
|
"type" : (src.type == "system" ? "" : src["type"]),
|
||||||
|
"iconColor" : src["color"],
|
||||||
|
"fromDate" : 0,
|
||||||
|
"toDate" : 0,
|
||||||
|
"labelColors" : src["color"]
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!isActive) {
|
||||||
|
if (src.type == "label") {
|
||||||
|
groupLabelsSelected = false
|
||||||
|
}
|
||||||
|
if (src.type == "folder") {
|
||||||
|
groupFoldersSelected = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
transferRules.isLabelGroupSelected = groupLabelsSelected
|
||||||
|
transferRules.isFolderGroupSelected = groupFoldersSelected
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTargets(sourceID, type) {
|
||||||
|
console.log("get targets:", type, sourceID)
|
||||||
|
|
||||||
|
if (! (type+sourceID in transferRules.targets)){
|
||||||
|
var source;
|
||||||
|
|
||||||
|
for (var srcI in transferRules.importRules) {
|
||||||
|
source = transferRules.importRules[srcI]
|
||||||
|
if (source.mboxID == sourceID ) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var model = Qt.createQmlObject ('import QtQuick 2.3; ListModel { property int selectedIndex: -1; }', transferRules);
|
||||||
|
|
||||||
|
var i = -1
|
||||||
|
for (var tgtID in transferRules.pmMailboxes) {
|
||||||
|
var tgt = transferRules.pmMailboxes[tgtID]
|
||||||
|
|
||||||
|
if (type == "label" && tgt.type != "label") continue
|
||||||
|
if (type != "label" && tgt.type == "label") continue
|
||||||
|
|
||||||
|
i++;
|
||||||
|
|
||||||
|
|
||||||
|
var isActive = false
|
||||||
|
for (var tid in source.targetIDs ) {
|
||||||
|
var selectedID = source.targetIDs[tid]
|
||||||
|
if (selectedID == tgtID) {
|
||||||
|
isActive = true
|
||||||
|
model.selectedIndex=i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var row = {
|
||||||
|
"isActive" : isActive,
|
||||||
|
"mboxID" : tgtID,
|
||||||
|
"name" : tgt.name,
|
||||||
|
"type" : tgt.type,
|
||||||
|
"iconColor" : tgt["color"]
|
||||||
|
};
|
||||||
|
|
||||||
|
model.append (row) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
transferRules.targets[type+sourceID] = model;
|
||||||
|
}
|
||||||
|
|
||||||
|
return transferRules.targets[type+sourceID];
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeTargetID(srcID, targetID, add){
|
||||||
|
console.log("change target ID ", srcID, targetID, add)
|
||||||
|
for (var targetsName in transferRules.targets) {
|
||||||
|
var targets = transferRules.targets[targetsName]
|
||||||
|
var areFolders = targetsName == "folder"+srcID
|
||||||
|
var areLabels = targetsName == "label"+srcID
|
||||||
|
if (areFolders || areLabels) {
|
||||||
|
console.log("matched targets ", targetsName, targets)
|
||||||
|
var deactivateOthers = false
|
||||||
|
var colorList = []
|
||||||
|
for (var i =0; i<targets.count; i++) {
|
||||||
|
var tgt = targets.get(i)
|
||||||
|
console.log(" tgt", i, tgt.mboxID, tgt.isActive)
|
||||||
|
if (tgt.mboxID == targetID) {
|
||||||
|
console.log(" matched tgt", i, tgt.mboxID)
|
||||||
|
if (areFolders && !add) {
|
||||||
|
console.exception("WRONG LOGIC: removing folder")
|
||||||
|
}
|
||||||
|
if (add) {
|
||||||
|
targets.selectedIndex=i
|
||||||
|
}
|
||||||
|
|
||||||
|
tgt.isActive = add
|
||||||
|
deactivateOthers = add && areFolders
|
||||||
|
console.log(" active ", tgt.isActive)
|
||||||
|
}
|
||||||
|
if (areLabels && tgt.isActive) {
|
||||||
|
colorList.push(tgt.iconColor)
|
||||||
|
console.log(" colors", i, colorList)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (areLabels) {
|
||||||
|
if (colorList.length == 0){
|
||||||
|
targets.selectedIndex = -1
|
||||||
|
}
|
||||||
|
for (var i = 0; i<transferRules.count; i++) {
|
||||||
|
var rule = transferRules.get(i)
|
||||||
|
console.log (" are labels: color list", i, rule.mboxID )
|
||||||
|
if (rule.mboxID == srcID) {
|
||||||
|
rule.labelColors = colorList.join(";")
|
||||||
|
console.log("updated label color list", rule.labelColors)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deactivateOthers) {
|
||||||
|
for (var i =0; i<targets.count; i++) {
|
||||||
|
var tgt = targets.get(i)
|
||||||
|
if (tgt.mboxID != targetID) {
|
||||||
|
tgt.isActive = false
|
||||||
|
console.log(" deactivate ", tgt.mboxID, tgt.isActive)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -497,6 +847,14 @@ Window {
|
|||||||
|
|
||||||
signal toggleMainWin(int systX, int systY, int systW, int systH)
|
signal toggleMainWin(int systX, int systY, int systW, int systH)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
signal notifyHasNoKeychain()
|
||||||
|
signal notifyKeychainRebuild()
|
||||||
|
signal notifyAddressChangedLogout()
|
||||||
|
signal notifyAddressChanged()
|
||||||
|
signal notifyUpdate()
|
||||||
|
|
||||||
signal showWindow()
|
signal showWindow()
|
||||||
signal showHelp()
|
signal showHelp()
|
||||||
signal showQuit()
|
signal showQuit()
|
||||||
@ -692,8 +1050,8 @@ Window {
|
|||||||
go.animateProgressBar.resume()
|
go.animateProgressBar.resume()
|
||||||
}
|
}
|
||||||
|
|
||||||
function cancelProcess(clearUnfinished) {
|
function cancelProcess() {
|
||||||
console.log("stopped at ", go.progress, " clearing unfinished", clearUnfinished)
|
console.log("stopped at ", go.progress)
|
||||||
go.animateProgressBar.stop()
|
go.animateProgressBar.stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -718,6 +1076,7 @@ Window {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case "loadStructureForExport" :
|
case "loadStructureForExport" :
|
||||||
|
transferRules.prepareExport()
|
||||||
go.exportStructureLoadFinished(true)
|
go.exportStructureLoadFinished(true)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
@ -43,10 +43,10 @@ const (
|
|||||||
// Constants for data map
|
// Constants for data map
|
||||||
const (
|
const (
|
||||||
// Account info
|
// Account info
|
||||||
Account = int(core.Qt__UserRole) + 1<<iota
|
Account = int(core.Qt__UserRole) + 1 + iota // 256 + 1 = 257
|
||||||
Status
|
Status // 258
|
||||||
Password
|
Password // 259
|
||||||
Aliases
|
Aliases // ...
|
||||||
IsExpanded
|
IsExpanded
|
||||||
// Folder info
|
// Folder info
|
||||||
FolderId
|
FolderId
|
||||||
@ -65,4 +65,56 @@ const (
|
|||||||
MailFrom
|
MailFrom
|
||||||
InputFolder
|
InputFolder
|
||||||
ErrorMessage
|
ErrorMessage
|
||||||
|
// Transfer rules mbox
|
||||||
|
MboxSelectedIndex
|
||||||
|
MboxIsActive
|
||||||
|
MboxID
|
||||||
|
MboxName
|
||||||
|
MboxType
|
||||||
|
MboxColor
|
||||||
|
// Transfer Rules
|
||||||
|
RuleTargetLabelColors
|
||||||
|
RuleFromDate
|
||||||
|
RuleToDate
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// This should match enums in GuiIE.qml
|
||||||
|
errUnknownError = 0
|
||||||
|
errEventAPILogout = 1
|
||||||
|
errUpdateAPI = 2
|
||||||
|
errUpdateJSON = 3
|
||||||
|
errUserAuth = 4
|
||||||
|
errQApplication = 18
|
||||||
|
errEmailExportFailed = 6
|
||||||
|
errEmailExportMissing = 7
|
||||||
|
errNothingToImport = 8
|
||||||
|
errEmailImportFailed = 12
|
||||||
|
errDraftImportFailed = 13
|
||||||
|
errDraftLabelFailed = 14
|
||||||
|
errEncryptMessageAttachment = 15
|
||||||
|
errEncryptMessage = 16
|
||||||
|
errNoInternetWhileImport = 17
|
||||||
|
errUnlockUser = 5
|
||||||
|
errSourceMessageNotSelected = 19
|
||||||
|
errCannotParseMail = 5000
|
||||||
|
errWrongLoginOrPassword = 5001
|
||||||
|
errWrongServerPathOrPort = 5002
|
||||||
|
errWrongAuthMethod = 5003
|
||||||
|
errIMAPFetchFailed = 5004
|
||||||
|
errLocalSourceLoadFailed = 1000
|
||||||
|
errPMLoadFailed = 1001
|
||||||
|
errRemoteSourceLoadFailed = 1002
|
||||||
|
errLoadAccountList = 1005
|
||||||
|
errExit = 1006
|
||||||
|
errRetry = 1007
|
||||||
|
errAsk = 1008
|
||||||
|
errImportFailed = 1009
|
||||||
|
errCreateLabelFailed = 1010
|
||||||
|
errCreateFolderFailed = 1011
|
||||||
|
errUpdateLabelFailed = 1012
|
||||||
|
errUpdateFolderFailed = 1013
|
||||||
|
errFillFolderName = 1014
|
||||||
|
errSelectFolderColor = 1015
|
||||||
|
errNoInternet = 1016
|
||||||
)
|
)
|
||||||
|
|||||||
@ -21,14 +21,10 @@ package qtie
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
qtcommon "github.com/ProtonMail/proton-bridge/internal/frontend/qt-common"
|
qtcommon "github.com/ProtonMail/proton-bridge/internal/frontend/qt-common"
|
||||||
|
"github.com/ProtonMail/proton-bridge/internal/transfer"
|
||||||
"github.com/therecipe/qt/core"
|
"github.com/therecipe/qt/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrorDetail stores information about email and error
|
|
||||||
type ErrorDetail struct {
|
|
||||||
MailSubject, MailDate, MailFrom, InputFolder, ErrorMessage string
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
ErrorListModel_QRegisterMetaType()
|
ErrorListModel_QRegisterMetaType()
|
||||||
}
|
}
|
||||||
@ -42,11 +38,12 @@ type ErrorListModel struct {
|
|||||||
_ map[int]*core.QByteArray `property:"roles"`
|
_ map[int]*core.QByteArray `property:"roles"`
|
||||||
_ int `property:"count"`
|
_ int `property:"count"`
|
||||||
|
|
||||||
Details []*ErrorDetail
|
Progress *transfer.Progress
|
||||||
|
records []*transfer.MessageStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ErrorListModel) init() {
|
func (e *ErrorListModel) init() {
|
||||||
s.SetRoles(map[int]*core.QByteArray{
|
e.SetRoles(map[int]*core.QByteArray{
|
||||||
MailSubject: qtcommon.NewQByteArrayFromString("mailSubject"),
|
MailSubject: qtcommon.NewQByteArrayFromString("mailSubject"),
|
||||||
MailDate: qtcommon.NewQByteArrayFromString("mailDate"),
|
MailDate: qtcommon.NewQByteArrayFromString("mailDate"),
|
||||||
MailFrom: qtcommon.NewQByteArrayFromString("mailFrom"),
|
MailFrom: qtcommon.NewQByteArrayFromString("mailFrom"),
|
||||||
@ -54,76 +51,50 @@ func (s *ErrorListModel) init() {
|
|||||||
ErrorMessage: qtcommon.NewQByteArrayFromString("errorMessage"),
|
ErrorMessage: qtcommon.NewQByteArrayFromString("errorMessage"),
|
||||||
})
|
})
|
||||||
// basic QAbstractListModel mehods
|
// basic QAbstractListModel mehods
|
||||||
s.ConnectData(s.data)
|
e.ConnectData(e.data)
|
||||||
s.ConnectRowCount(s.rowCount)
|
e.ConnectRowCount(e.rowCount)
|
||||||
s.ConnectColumnCount(s.columnCount)
|
e.ConnectColumnCount(e.columnCount)
|
||||||
s.ConnectRoleNames(s.roleNames)
|
e.ConnectRoleNames(e.roleNames)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ErrorListModel) data(index *core.QModelIndex, role int) *core.QVariant {
|
func (e *ErrorListModel) data(index *core.QModelIndex, role int) *core.QVariant {
|
||||||
if !index.IsValid() {
|
if !index.IsValid() {
|
||||||
return core.NewQVariant()
|
return core.NewQVariant()
|
||||||
}
|
}
|
||||||
|
|
||||||
if index.Row() >= len(s.Details) {
|
if index.Row() >= len(e.records) {
|
||||||
return core.NewQVariant()
|
return core.NewQVariant()
|
||||||
}
|
}
|
||||||
|
|
||||||
var p = s.Details[index.Row()]
|
var r = e.records[index.Row()]
|
||||||
|
|
||||||
switch role {
|
switch role {
|
||||||
case MailSubject:
|
case MailSubject:
|
||||||
return qtcommon.NewQVariantString(p.MailSubject)
|
return qtcommon.NewQVariantString(r.Subject)
|
||||||
case MailDate:
|
case MailDate:
|
||||||
return qtcommon.NewQVariantString(p.MailDate)
|
return qtcommon.NewQVariantString(r.Time.String())
|
||||||
case MailFrom:
|
case MailFrom:
|
||||||
return qtcommon.NewQVariantString(p.MailFrom)
|
return qtcommon.NewQVariantString(r.From)
|
||||||
case InputFolder:
|
case InputFolder:
|
||||||
return qtcommon.NewQVariantString(p.InputFolder)
|
return qtcommon.NewQVariantString(r.SourceID)
|
||||||
case ErrorMessage:
|
case ErrorMessage:
|
||||||
return qtcommon.NewQVariantString(p.ErrorMessage)
|
return qtcommon.NewQVariantString(r.GetErrorMessage())
|
||||||
default:
|
default:
|
||||||
return core.NewQVariant()
|
return core.NewQVariant()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ErrorListModel) rowCount(parent *core.QModelIndex) int { return len(s.Details) }
|
func (e *ErrorListModel) rowCount(parent *core.QModelIndex) int { return len(e.records) }
|
||||||
func (s *ErrorListModel) columnCount(parent *core.QModelIndex) int { return 1 }
|
func (e *ErrorListModel) columnCount(parent *core.QModelIndex) int { return 1 }
|
||||||
func (s *ErrorListModel) roleNames() map[int]*core.QByteArray { return s.Roles() }
|
func (e *ErrorListModel) roleNames() map[int]*core.QByteArray { return e.Roles() }
|
||||||
|
|
||||||
// Add more errors to list
|
func (e *ErrorListModel) load() {
|
||||||
func (s *ErrorListModel) Add(more []*ErrorDetail) {
|
if e.Progress == nil {
|
||||||
s.BeginInsertRows(core.NewQModelIndex(), len(s.Details), len(s.Details))
|
log.Error("Progress not connected")
|
||||||
s.Details = append(s.Details, more...)
|
return
|
||||||
s.SetCount(len(s.Details))
|
}
|
||||||
s.EndInsertRows()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear removes all items in model
|
e.BeginResetModel()
|
||||||
func (s *ErrorListModel) Clear() {
|
e.records = e.Progress.GetFailedMessages()
|
||||||
s.BeginRemoveRows(core.NewQModelIndex(), 0, len(s.Details))
|
e.EndResetModel()
|
||||||
s.Details = s.Details[0:0]
|
|
||||||
s.SetCount(len(s.Details))
|
|
||||||
s.EndRemoveRows()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *ErrorListModel) load(importLogFileName string) {
|
|
||||||
/*
|
|
||||||
err := backend.LoopDetailsInFile(importLogFileName, func(d *backend.MessageDetails) {
|
|
||||||
if d.MessageID != "" { // imported ok
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ed := &ErrorDetail{
|
|
||||||
MailSubject: d.Subject,
|
|
||||||
MailDate: d.Time,
|
|
||||||
MailFrom: d.From,
|
|
||||||
InputFolder: d.Folder,
|
|
||||||
ErrorMessage: d.Error,
|
|
||||||
}
|
|
||||||
s.Add([]*ErrorDetail{ed})
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("load import report from %q: %v", importLogFileName, err)
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,6 +21,7 @@ package qtie
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/ProtonMail/proton-bridge/internal/transfer"
|
"github.com/ProtonMail/proton-bridge/internal/transfer"
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -29,10 +30,11 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (f *FrontendQt) LoadStructureForExport(addressOrID string) {
|
func (f *FrontendQt) LoadStructureForExport(addressOrID string) {
|
||||||
|
errCode := errUnknownError
|
||||||
var err error
|
var err error
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
f.showError(err)
|
f.showError(errCode, errors.Wrap(err, "failed to load structure for "+addressOrID))
|
||||||
f.Qml.ExportStructureLoadFinished(false)
|
f.Qml.ExportStructureLoadFinished(false)
|
||||||
} else {
|
} else {
|
||||||
f.Qml.ExportStructureLoadFinished(true)
|
f.Qml.ExportStructureLoadFinished(true)
|
||||||
@ -40,20 +42,12 @@ func (f *FrontendQt) LoadStructureForExport(addressOrID string) {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
if f.transfer, err = f.ie.GetEMLExporter(addressOrID, ""); err != nil {
|
if f.transfer, err = f.ie.GetEMLExporter(addressOrID, ""); err != nil {
|
||||||
|
// The only error can be problem to load PM user and address.
|
||||||
|
errCode = errPMLoadFailed
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
f.PMStructure.Clear()
|
f.TransferRules.setTransfer(f.transfer)
|
||||||
sourceMailboxes, err := f.transfer.SourceMailboxes()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, mbox := range sourceMailboxes {
|
|
||||||
rule := f.transfer.GetRule(mbox)
|
|
||||||
f.PMStructure.addEntry(newFolderInfo(mbox, rule))
|
|
||||||
}
|
|
||||||
|
|
||||||
f.PMStructure.transfer = f.transfer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FrontendQt) StartExport(rootPath, login, fileType string, attachEncryptedBody bool) {
|
func (f *FrontendQt) StartExport(rootPath, login, fileType string, attachEncryptedBody bool) {
|
||||||
|
|||||||
@ -65,11 +65,11 @@ type FrontendQt struct {
|
|||||||
programVersion string // Program version
|
programVersion string // Program version
|
||||||
buildVersion string // Program build version
|
buildVersion string // Program build version
|
||||||
|
|
||||||
PMStructure *FolderStructure // Providing data for account labels and folders for ProtonMail account
|
TransferRules *TransferRules
|
||||||
ExternalStructure *FolderStructure // Providing data for account labels and folders for MBOX, EML or external IMAP account
|
ErrorList *ErrorListModel // Providing data for error reporting
|
||||||
ErrorList *ErrorListModel // Providing data for error reporting
|
|
||||||
|
|
||||||
transfer *transfer.Transfer
|
transfer *transfer.Transfer
|
||||||
|
progress *transfer.Progress
|
||||||
|
|
||||||
notifyHasNoKeychain bool
|
notifyHasNoKeychain bool
|
||||||
}
|
}
|
||||||
@ -103,102 +103,99 @@ func New(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// IsAppRestarting for Import-Export is always false i.e never restarts
|
// IsAppRestarting for Import-Export is always false i.e never restarts
|
||||||
func (s *FrontendQt) IsAppRestarting() bool {
|
func (f *FrontendQt) IsAppRestarting() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loop function for Import-Export interface. It runs QtExecute in main thread
|
// Loop function for Import-Export interface. It runs QtExecute in main thread
|
||||||
// with no additional function.
|
// with no additional function.
|
||||||
func (s *FrontendQt) Loop(setupError error) (err error) {
|
func (f *FrontendQt) Loop(setupError error) (err error) {
|
||||||
if setupError != nil {
|
if setupError != nil {
|
||||||
s.notifyHasNoKeychain = true
|
f.notifyHasNoKeychain = true
|
||||||
}
|
}
|
||||||
go func() {
|
go func() {
|
||||||
defer s.panicHandler.HandlePanic()
|
defer f.panicHandler.HandlePanic()
|
||||||
s.watchEvents()
|
f.watchEvents()
|
||||||
}()
|
}()
|
||||||
err = s.QtExecute(func(s *FrontendQt) error { return nil })
|
err = f.QtExecute(func(f *FrontendQt) error { return nil })
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *FrontendQt) watchEvents() {
|
func (f *FrontendQt) watchEvents() {
|
||||||
internetOffCh := qtcommon.MakeAndRegisterEvent(s.eventListener, events.InternetOffEvent)
|
internetOffCh := qtcommon.MakeAndRegisterEvent(f.eventListener, events.InternetOffEvent)
|
||||||
internetOnCh := qtcommon.MakeAndRegisterEvent(s.eventListener, events.InternetOnEvent)
|
internetOnCh := qtcommon.MakeAndRegisterEvent(f.eventListener, events.InternetOnEvent)
|
||||||
restartBridgeCh := qtcommon.MakeAndRegisterEvent(s.eventListener, events.RestartBridgeEvent)
|
restartBridgeCh := qtcommon.MakeAndRegisterEvent(f.eventListener, events.RestartBridgeEvent)
|
||||||
addressChangedCh := qtcommon.MakeAndRegisterEvent(s.eventListener, events.AddressChangedEvent)
|
addressChangedCh := qtcommon.MakeAndRegisterEvent(f.eventListener, events.AddressChangedEvent)
|
||||||
addressChangedLogoutCh := qtcommon.MakeAndRegisterEvent(s.eventListener, events.AddressChangedLogoutEvent)
|
addressChangedLogoutCh := qtcommon.MakeAndRegisterEvent(f.eventListener, events.AddressChangedLogoutEvent)
|
||||||
logoutCh := qtcommon.MakeAndRegisterEvent(s.eventListener, events.LogoutEvent)
|
logoutCh := qtcommon.MakeAndRegisterEvent(f.eventListener, events.LogoutEvent)
|
||||||
updateApplicationCh := qtcommon.MakeAndRegisterEvent(s.eventListener, events.UpgradeApplicationEvent)
|
updateApplicationCh := qtcommon.MakeAndRegisterEvent(f.eventListener, events.UpgradeApplicationEvent)
|
||||||
newUserCh := qtcommon.MakeAndRegisterEvent(s.eventListener, events.UserRefreshEvent)
|
newUserCh := qtcommon.MakeAndRegisterEvent(f.eventListener, events.UserRefreshEvent)
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-internetOffCh:
|
case <-internetOffCh:
|
||||||
s.Qml.SetConnectionStatus(false)
|
f.Qml.SetConnectionStatus(false)
|
||||||
case <-internetOnCh:
|
case <-internetOnCh:
|
||||||
s.Qml.SetConnectionStatus(true)
|
f.Qml.SetConnectionStatus(true)
|
||||||
case <-restartBridgeCh:
|
case <-restartBridgeCh:
|
||||||
s.Qml.SetIsRestarting(true)
|
f.Qml.SetIsRestarting(true)
|
||||||
s.App.Quit()
|
f.App.Quit()
|
||||||
case address := <-addressChangedCh:
|
case address := <-addressChangedCh:
|
||||||
s.Qml.NotifyAddressChanged(address)
|
f.Qml.NotifyAddressChanged(address)
|
||||||
case address := <-addressChangedLogoutCh:
|
case address := <-addressChangedLogoutCh:
|
||||||
s.Qml.NotifyAddressChangedLogout(address)
|
f.Qml.NotifyAddressChangedLogout(address)
|
||||||
case userID := <-logoutCh:
|
case userID := <-logoutCh:
|
||||||
user, err := s.ie.GetUser(userID)
|
user, err := f.ie.GetUser(userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
s.Qml.NotifyLogout(user.Username())
|
f.Qml.NotifyLogout(user.Username())
|
||||||
case <-updateApplicationCh:
|
case <-updateApplicationCh:
|
||||||
s.Qml.ProcessFinished()
|
f.Qml.ProcessFinished()
|
||||||
s.Qml.NotifyUpdate()
|
f.Qml.NotifyUpdate()
|
||||||
case <-newUserCh:
|
case <-newUserCh:
|
||||||
s.Qml.LoadAccounts()
|
f.Qml.LoadAccounts()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *FrontendQt) qtSetupQmlAndStructures() {
|
func (f *FrontendQt) qtSetupQmlAndStructures() {
|
||||||
s.App = widgets.NewQApplication(len(os.Args), os.Args)
|
f.App = widgets.NewQApplication(len(os.Args), os.Args)
|
||||||
// view
|
// view
|
||||||
s.View = qml.NewQQmlApplicationEngine(s.App)
|
f.View = qml.NewQQmlApplicationEngine(f.App)
|
||||||
// Add Go-QML Import-Export
|
// Add Go-QML Import-Export
|
||||||
s.Qml = NewGoQMLInterface(nil)
|
f.Qml = NewGoQMLInterface(nil)
|
||||||
s.Qml.SetFrontend(s) // provides access
|
f.Qml.SetFrontend(f) // provides access
|
||||||
s.View.RootContext().SetContextProperty("go", s.Qml)
|
f.View.RootContext().SetContextProperty("go", f.Qml)
|
||||||
|
|
||||||
// Add AccountsModel
|
// Add AccountsModel
|
||||||
s.Accounts.SetupAccounts(s.Qml, s.ie)
|
f.Accounts.SetupAccounts(f.Qml, f.ie)
|
||||||
s.View.RootContext().SetContextProperty("accountsModel", s.Accounts.Model)
|
f.View.RootContext().SetContextProperty("accountsModel", f.Accounts.Model)
|
||||||
|
|
||||||
// Add ProtonMail FolderStructure
|
// Add TransferRules structure
|
||||||
s.PMStructure = NewFolderStructure(nil)
|
f.TransferRules = NewTransferRules(nil)
|
||||||
s.View.RootContext().SetContextProperty("structurePM", s.PMStructure)
|
f.View.RootContext().SetContextProperty("transferRules", f.TransferRules)
|
||||||
|
|
||||||
// Add external FolderStructure
|
|
||||||
s.ExternalStructure = NewFolderStructure(nil)
|
|
||||||
s.View.RootContext().SetContextProperty("structureExternal", s.ExternalStructure)
|
|
||||||
|
|
||||||
// Add error list modal
|
// Add error list modal
|
||||||
s.ErrorList = NewErrorListModel(nil)
|
f.ErrorList = NewErrorListModel(nil)
|
||||||
s.View.RootContext().SetContextProperty("errorList", s.ErrorList)
|
f.View.RootContext().SetContextProperty("errorList", f.ErrorList)
|
||||||
s.Qml.ConnectLoadImportReports(s.ErrorList.load)
|
f.Qml.ConnectLoadImportReports(f.ErrorList.load)
|
||||||
|
|
||||||
// Import path and load QML files
|
// Import path and load QML files
|
||||||
s.View.AddImportPath("qrc:///")
|
f.View.AddImportPath("qrc:///")
|
||||||
s.View.Load(core.NewQUrl3("qrc:/uiie.qml", 0))
|
f.View.Load(core.NewQUrl3("qrc:/uiie.qml", 0))
|
||||||
|
|
||||||
// TODO set the first start flag
|
// TODO set the first start flag
|
||||||
log.Error("Get FirstStart: Not implemented")
|
log.Error("Get FirstStart: Not implemented")
|
||||||
//if prefs.Get(prefs.FirstStart) == "true" {
|
//if prefs.Get(prefs.FirstStart) == "true" {
|
||||||
if false {
|
if false {
|
||||||
s.Qml.SetIsFirstStart(true)
|
f.Qml.SetIsFirstStart(true)
|
||||||
} else {
|
} else {
|
||||||
s.Qml.SetIsFirstStart(false)
|
f.Qml.SetIsFirstStart(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify user about error during initialization.
|
// Notify user about error during initialization.
|
||||||
if s.notifyHasNoKeychain {
|
if f.notifyHasNoKeychain {
|
||||||
s.Qml.NotifyHasNoKeychain()
|
f.Qml.NotifyHasNoKeychain()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,18 +204,18 @@ func (s *FrontendQt) qtSetupQmlAndStructures() {
|
|||||||
// It is needed to have just one Qt application per program (at least per same
|
// It is needed to have just one Qt application per program (at least per same
|
||||||
// thread). This functions reads the main user interface defined in QML files.
|
// thread). This functions reads the main user interface defined in QML files.
|
||||||
// The files are appended to library by Qt-QRC.
|
// The files are appended to library by Qt-QRC.
|
||||||
func (s *FrontendQt) QtExecute(Procedure func(*FrontendQt) error) error {
|
func (f *FrontendQt) QtExecute(Procedure func(*FrontendQt) error) error {
|
||||||
qtcommon.QtSetupCoreAndControls(s.programName, s.programVersion)
|
qtcommon.QtSetupCoreAndControls(f.programName, f.programVersion)
|
||||||
s.qtSetupQmlAndStructures()
|
f.qtSetupQmlAndStructures()
|
||||||
// Check QML is loaded properly
|
// Check QML is loaded properly
|
||||||
if len(s.View.RootObjects()) == 0 {
|
if len(f.View.RootObjects()) == 0 {
|
||||||
//return errors.New(errors.ErrQApplication, "QML not loaded properly")
|
//return errors.New(errors.ErrQApplication, "QML not loaded properly")
|
||||||
return errors.New("QML not loaded properly")
|
return errors.New("QML not loaded properly")
|
||||||
}
|
}
|
||||||
// Obtain main window (need for invoke method)
|
// Obtain main window (need for invoke method)
|
||||||
s.MainWin = s.View.RootObjects()[0]
|
f.MainWin = f.View.RootObjects()[0]
|
||||||
// Injected procedure for out-of-main-thread applications
|
// Injected procedure for out-of-main-thread applications
|
||||||
if err := Procedure(s); err != nil {
|
if err := Procedure(f); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Loop
|
// Loop
|
||||||
@ -234,63 +231,55 @@ func (s *FrontendQt) QtExecute(Procedure func(*FrontendQt) error) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *FrontendQt) openLogs() {
|
func (f *FrontendQt) openLogs() {
|
||||||
go open.Run(s.config.GetLogDir())
|
go open.Run(f.config.GetLogDir())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *FrontendQt) openReport() {
|
func (f *FrontendQt) openReport() {
|
||||||
go open.Run(s.Qml.ImportLogFileName())
|
go open.Run(f.Qml.ImportLogFileName())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *FrontendQt) openDownloadLink() {
|
func (f *FrontendQt) openDownloadLink() {
|
||||||
go open.Run(s.updates.GetDownloadLink())
|
go open.Run(f.updates.GetDownloadLink())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *FrontendQt) sendImportReport(address, reportFile string) (isOK bool) {
|
// sendImportReport sends an anonymized import or export report file to our customer support
|
||||||
/*
|
func (f *FrontendQt) sendImportReport(address string) bool { // Todo_: Rename to sendReport?
|
||||||
accname := "[No account logged in]"
|
|
||||||
if s.Accounts.Count() > 0 {
|
|
||||||
accname = s.Accounts.get(0).Account()
|
|
||||||
}
|
|
||||||
|
|
||||||
basename := filepath.Base(reportFile)
|
|
||||||
req := pmapi.ReportReq{
|
|
||||||
OS: core.QSysInfo_ProductType(),
|
|
||||||
OSVersion: core.QSysInfo_PrettyProductName(),
|
|
||||||
Title: "[Import Export] Import report: " + basename,
|
|
||||||
Description: "Sending import report file in attachment.",
|
|
||||||
Username: accname,
|
|
||||||
Email: address,
|
|
||||||
}
|
|
||||||
|
|
||||||
report, err := os.Open(reportFile)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorln("report file open:", err)
|
|
||||||
isOK = false
|
|
||||||
}
|
|
||||||
req.AddAttachment("log", basename, report)
|
|
||||||
|
|
||||||
c := pmapi.NewClient(backend.APIConfig, "import_reporter")
|
|
||||||
err = c.Report(req)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorln("while sendReport:", err)
|
|
||||||
isOK = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.Infof("Report %q send successfully", basename)
|
|
||||||
isOK = true
|
|
||||||
*/
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// sendBug is almost idetical to bridge
|
|
||||||
func (s *FrontendQt) sendBug(description, emailClient, address string) (isOK bool) {
|
|
||||||
isOK = true
|
|
||||||
var accname = "No account logged in"
|
var accname = "No account logged in"
|
||||||
if s.Accounts.Model.Count() > 0 {
|
if f.Accounts.Model.Count() > 0 {
|
||||||
accname = s.Accounts.Model.Get(0).Account()
|
accname = f.Accounts.Model.Get(0).Account()
|
||||||
}
|
}
|
||||||
if err := s.ie.ReportBug(
|
|
||||||
|
if f.progress == nil {
|
||||||
|
log.Errorln("Failed to send process report: Missing progress")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
report := f.progress.GenerateBugReport()
|
||||||
|
|
||||||
|
if err := f.ie.ReportFile(
|
||||||
|
core.QSysInfo_ProductType(),
|
||||||
|
core.QSysInfo_PrettyProductName(),
|
||||||
|
accname,
|
||||||
|
address,
|
||||||
|
report,
|
||||||
|
); err != nil {
|
||||||
|
log.Errorln("Failed to send process report:", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("Report send successfully")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// sendBug sends a bug report described by user to our customer support
|
||||||
|
func (f *FrontendQt) sendBug(description, emailClient, address string) bool {
|
||||||
|
var accname = "No account logged in"
|
||||||
|
if f.Accounts.Model.Count() > 0 {
|
||||||
|
accname = f.Accounts.Model.Get(0).Account()
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := f.ie.ReportBug(
|
||||||
core.QSysInfo_ProductType(),
|
core.QSysInfo_ProductType(),
|
||||||
core.QSysInfo_PrettyProductName(),
|
core.QSysInfo_PrettyProductName(),
|
||||||
description,
|
description,
|
||||||
@ -299,41 +288,43 @@ func (s *FrontendQt) sendBug(description, emailClient, address string) (isOK boo
|
|||||||
emailClient,
|
emailClient,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
log.Errorln("while sendBug:", err)
|
log.Errorln("while sendBug:", err)
|
||||||
isOK = false
|
return false
|
||||||
}
|
}
|
||||||
return
|
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkInternet is almost idetical to bridge
|
// checkInternet is almost idetical to bridge
|
||||||
func (s *FrontendQt) checkInternet() {
|
func (f *FrontendQt) checkInternet() {
|
||||||
s.Qml.SetConnectionStatus(s.ie.CheckConnection() == nil)
|
f.Qml.SetConnectionStatus(f.ie.CheckConnection() == nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *FrontendQt) showError(err error) {
|
func (f *FrontendQt) showError(code int, err error) {
|
||||||
code := 0 // TODO err.Code()
|
f.Qml.SetErrorDescription(err.Error())
|
||||||
s.Qml.SetErrorDescription(err.Error())
|
|
||||||
log.WithField("code", code).Errorln(err.Error())
|
log.WithField("code", code).Errorln(err.Error())
|
||||||
s.Qml.NotifyError(code)
|
f.Qml.NotifyError(code)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *FrontendQt) emitEvent(evType, msg string) {
|
func (f *FrontendQt) emitEvent(evType, msg string) {
|
||||||
s.eventListener.Emit(evType, msg)
|
f.eventListener.Emit(evType, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *FrontendQt) setProgressManager(progress *transfer.Progress) {
|
func (f *FrontendQt) setProgressManager(progress *transfer.Progress) {
|
||||||
s.Qml.ConnectPauseProcess(func() { progress.Pause("user") })
|
f.progress = progress
|
||||||
s.Qml.ConnectResumeProcess(progress.Resume)
|
f.ErrorList.Progress = progress
|
||||||
s.Qml.ConnectCancelProcess(func(clearUnfinished bool) {
|
|
||||||
// TODO clear unfinished
|
f.Qml.ConnectPauseProcess(func() { progress.Pause("paused") })
|
||||||
|
f.Qml.ConnectResumeProcess(progress.Resume)
|
||||||
|
f.Qml.ConnectCancelProcess(func() {
|
||||||
progress.Stop()
|
progress.Stop()
|
||||||
})
|
})
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
defer func() {
|
defer func() {
|
||||||
s.Qml.DisconnectPauseProcess()
|
f.Qml.DisconnectPauseProcess()
|
||||||
s.Qml.DisconnectResumeProcess()
|
f.Qml.DisconnectResumeProcess()
|
||||||
s.Qml.DisconnectCancelProcess()
|
f.Qml.DisconnectCancelProcess()
|
||||||
s.Qml.SetProgress(1)
|
f.Qml.SetProgress(1)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
//TODO get log file (in old code it was here, but this is ugly place probably somewhere else)
|
//TODO get log file (in old code it was here, but this is ugly place probably somewhere else)
|
||||||
@ -344,119 +335,123 @@ func (s *FrontendQt) setProgressManager(progress *transfer.Progress) {
|
|||||||
}
|
}
|
||||||
failed, imported, _, _, total := progress.GetCounts()
|
failed, imported, _, _, total := progress.GetCounts()
|
||||||
if total != 0 { // udate total
|
if total != 0 { // udate total
|
||||||
s.Qml.SetTotal(int(total))
|
f.Qml.SetTotal(int(total))
|
||||||
}
|
}
|
||||||
s.Qml.SetProgressFails(int(failed))
|
f.Qml.SetProgressFails(int(failed))
|
||||||
s.Qml.SetProgressDescription(progress.PauseReason()) // TODO add description when changing folders?
|
f.Qml.SetProgressDescription(progress.PauseReason()) // TODO add description when changing folders?
|
||||||
if total > 0 {
|
if total > 0 {
|
||||||
newProgress := float32(imported+failed) / float32(total)
|
newProgress := float32(imported+failed) / float32(total)
|
||||||
if newProgress >= 0 && newProgress != s.Qml.Progress() {
|
if newProgress >= 0 && newProgress != f.Qml.Progress() {
|
||||||
s.Qml.SetProgress(newProgress)
|
f.Qml.SetProgress(newProgress)
|
||||||
s.Qml.ProgressChanged(newProgress)
|
f.Qml.ProgressChanged(newProgress)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO fatal error?
|
if err := progress.GetFatalError(); err != nil {
|
||||||
|
f.Qml.SetProgressDescription(err.Error())
|
||||||
|
} else {
|
||||||
|
f.Qml.SetProgressDescription("")
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
// StartUpdate is identical to bridge
|
// StartUpdate is identical to bridge
|
||||||
func (s *FrontendQt) StartUpdate() {
|
func (f *FrontendQt) StartUpdate() {
|
||||||
progress := make(chan updates.Progress)
|
progress := make(chan updates.Progress)
|
||||||
go func() { // Update progress in QML.
|
go func() { // Update progress in QML.
|
||||||
defer s.panicHandler.HandlePanic()
|
defer f.panicHandler.HandlePanic()
|
||||||
for current := range progress {
|
for current := range progress {
|
||||||
s.Qml.SetProgress(current.Processed)
|
f.Qml.SetProgress(current.Processed)
|
||||||
s.Qml.SetProgressDescription(strconv.Itoa(current.Description))
|
f.Qml.SetProgressDescription(strconv.Itoa(current.Description))
|
||||||
// Error happend
|
// Error happend
|
||||||
if current.Err != nil {
|
if current.Err != nil {
|
||||||
log.Error("update progress: ", current.Err)
|
log.Error("update progress: ", current.Err)
|
||||||
s.Qml.UpdateFinished(true)
|
f.Qml.UpdateFinished(true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Finished everything OK.
|
// Finished everything OK.
|
||||||
if current.Description >= updates.InfoQuitApp {
|
if current.Description >= updates.InfoQuitApp {
|
||||||
s.Qml.UpdateFinished(false)
|
f.Qml.UpdateFinished(false)
|
||||||
time.Sleep(3 * time.Second) // Just notify.
|
time.Sleep(3 * time.Second) // Just notify.
|
||||||
s.Qml.SetIsRestarting(current.Description == updates.InfoRestartApp)
|
f.Qml.SetIsRestarting(current.Description == updates.InfoRestartApp)
|
||||||
s.App.Quit()
|
f.App.Quit()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
go func() {
|
go func() {
|
||||||
defer s.panicHandler.HandlePanic()
|
defer f.panicHandler.HandlePanic()
|
||||||
s.updates.StartUpgrade(progress)
|
f.updates.StartUpgrade(progress)
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
// isNewVersionAvailable is identical to bridge
|
// isNewVersionAvailable is identical to bridge
|
||||||
// return 0 when local version is fine
|
// return 0 when local version is fine
|
||||||
// return 1 when new version is available
|
// return 1 when new version is available
|
||||||
func (s *FrontendQt) isNewVersionAvailable(showMessage bool) {
|
func (f *FrontendQt) isNewVersionAvailable(showMessage bool) {
|
||||||
go func() {
|
go func() {
|
||||||
defer s.Qml.ProcessFinished()
|
defer f.Qml.ProcessFinished()
|
||||||
isUpToDate, latestVersionInfo, err := s.updates.CheckIsUpToDate()
|
isUpToDate, latestVersionInfo, err := f.updates.CheckIsUpToDate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnln("Cannot retrieve version info: ", err)
|
log.Warnln("Cannot retrieve version info: ", err)
|
||||||
s.checkInternet()
|
f.checkInternet()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
s.Qml.SetConnectionStatus(true) // if we are here connection is ok
|
f.Qml.SetConnectionStatus(true) // if we are here connection is ok
|
||||||
if isUpToDate {
|
if isUpToDate {
|
||||||
s.Qml.SetUpdateState(StatusUpToDate)
|
f.Qml.SetUpdateState(StatusUpToDate)
|
||||||
if showMessage {
|
if showMessage {
|
||||||
s.Qml.NotifyVersionIsTheLatest()
|
f.Qml.NotifyVersionIsTheLatest()
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
s.Qml.SetNewversion(latestVersionInfo.Version)
|
f.Qml.SetNewversion(latestVersionInfo.Version)
|
||||||
s.Qml.SetChangelog(latestVersionInfo.ReleaseNotes)
|
f.Qml.SetChangelog(latestVersionInfo.ReleaseNotes)
|
||||||
s.Qml.SetBugfixes(latestVersionInfo.ReleaseFixedBugs)
|
f.Qml.SetBugfixes(latestVersionInfo.ReleaseFixedBugs)
|
||||||
s.Qml.SetLandingPage(latestVersionInfo.LandingPage)
|
f.Qml.SetLandingPage(latestVersionInfo.LandingPage)
|
||||||
s.Qml.SetDownloadLink(latestVersionInfo.GetDownloadLink())
|
f.Qml.SetDownloadLink(latestVersionInfo.GetDownloadLink())
|
||||||
s.Qml.SetUpdateState(StatusNewVersionAvailable)
|
f.Qml.SetUpdateState(StatusNewVersionAvailable)
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *FrontendQt) resetSource() {
|
func (f *FrontendQt) resetSource() {
|
||||||
if s.transfer != nil {
|
if f.transfer != nil {
|
||||||
s.transfer.ResetRules()
|
f.transfer.ResetRules()
|
||||||
if err := s.loadStructuresForImport(); err != nil {
|
if err := f.loadStructuresForImport(); err != nil {
|
||||||
log.WithError(err).Error("Cannot reload structures after reseting rules.")
|
log.WithError(err).Error("Cannot reload structures after reseting rules.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// getLocalVersionInfo is identical to bridge.
|
// getLocalVersionInfo is identical to bridge.
|
||||||
func (s *FrontendQt) getLocalVersionInfo() {
|
func (f *FrontendQt) getLocalVersionInfo() {
|
||||||
defer s.Qml.ProcessFinished()
|
defer f.Qml.ProcessFinished()
|
||||||
localVersion := s.updates.GetLocalVersion()
|
localVersion := f.updates.GetLocalVersion()
|
||||||
s.Qml.SetNewversion(localVersion.Version)
|
f.Qml.SetNewversion(localVersion.Version)
|
||||||
s.Qml.SetChangelog(localVersion.ReleaseNotes)
|
f.Qml.SetChangelog(localVersion.ReleaseNotes)
|
||||||
s.Qml.SetBugfixes(localVersion.ReleaseFixedBugs)
|
f.Qml.SetBugfixes(localVersion.ReleaseFixedBugs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LeastUsedColor is intended to return color for creating a new inbox or label.
|
// LeastUsedColor is intended to return color for creating a new inbox or label.
|
||||||
func (s *FrontendQt) leastUsedColor() string {
|
func (f *FrontendQt) leastUsedColor() string {
|
||||||
if s.transfer == nil {
|
if f.transfer == nil {
|
||||||
log.Errorln("Getting least used color before transfer exist.")
|
log.Errorln("Getting least used color before transfer exist.")
|
||||||
return "#7272a7"
|
return "#7272a7"
|
||||||
}
|
}
|
||||||
|
|
||||||
m, err := s.transfer.TargetMailboxes()
|
m, err := f.transfer.TargetMailboxes()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorln("Getting least used color:", err)
|
log.Errorln("Getting least used color:", err)
|
||||||
s.showError(err)
|
f.showError(errUnknownError, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return transfer.LeastUsedColor(m)
|
return transfer.LeastUsedColor(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
// createLabelOrFolder performs an IE target mailbox creation.
|
// createLabelOrFolder performs an IE target mailbox creation.
|
||||||
func (s *FrontendQt) createLabelOrFolder(email, name, color string, isLabel bool, sourceID string) bool {
|
func (f *FrontendQt) createLabelOrFolder(email, name, color string, isLabel bool, sourceID string) bool {
|
||||||
// Prepare new mailbox.
|
// Prepare new mailbox.
|
||||||
m := transfer.Mailbox{
|
m := transfer.Mailbox{
|
||||||
Name: name,
|
Name: name,
|
||||||
@ -466,32 +461,28 @@ func (s *FrontendQt) createLabelOrFolder(email, name, color string, isLabel bool
|
|||||||
|
|
||||||
// Select least used color if no color given.
|
// Select least used color if no color given.
|
||||||
if m.Color == "" {
|
if m.Color == "" {
|
||||||
m.Color = s.leastUsedColor()
|
m.Color = f.leastUsedColor()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
f.TransferRules.BeginResetModel()
|
||||||
|
defer f.TransferRules.EndResetModel()
|
||||||
|
|
||||||
// Create mailbox.
|
// Create mailbox.
|
||||||
newLabel, err := s.transfer.CreateTargetMailbox(m)
|
m, err := f.transfer.CreateTargetMailbox(m)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorln("Folder/Label creating:", err)
|
log.Errorln("Folder/Label creating:", err)
|
||||||
s.showError(err)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: notify UI of newly added folders/labels
|
|
||||||
/*errc := s.PMStructure.Load(email, false)
|
|
||||||
if errc != nil {
|
|
||||||
s.showError(errc)
|
|
||||||
return false
|
|
||||||
}*/
|
|
||||||
|
|
||||||
if sourceID != "" {
|
|
||||||
if isLabel {
|
if isLabel {
|
||||||
s.ExternalStructure.addTargetLabelID(sourceID, newLabel.ID)
|
f.showError(errCreateLabelFailed, err)
|
||||||
} else {
|
} else {
|
||||||
s.ExternalStructure.setTargetFolderID(sourceID, newLabel.ID)
|
f.showError(errCreateFolderFailed, err)
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if sourceID == "-1" {
|
||||||
|
f.transfer.SetGlobalMailbox(&m)
|
||||||
|
} else {
|
||||||
|
f.TransferRules.addTargetID(sourceID, m.Hash())
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,14 +19,19 @@
|
|||||||
|
|
||||||
package qtie
|
package qtie
|
||||||
|
|
||||||
import "github.com/ProtonMail/proton-bridge/internal/transfer"
|
import (
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
|
"github.com/ProtonMail/proton-bridge/internal/transfer"
|
||||||
|
)
|
||||||
|
|
||||||
// wrapper for QML
|
// wrapper for QML
|
||||||
func (f *FrontendQt) setupAndLoadForImport(isFromIMAP bool, sourcePath, sourceEmail, sourcePassword, sourceServer, sourcePort, targetAddress string) {
|
func (f *FrontendQt) setupAndLoadForImport(isFromIMAP bool, sourcePath, sourceEmail, sourcePassword, sourceServer, sourcePort, targetAddress string) {
|
||||||
|
errCode := errUnknownError
|
||||||
var err error
|
var err error
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
f.showError(err)
|
f.showError(errCode, err)
|
||||||
f.Qml.ImportStructuresLoadFinished(false)
|
f.Qml.ImportStructuresLoadFinished(false)
|
||||||
} else {
|
} else {
|
||||||
f.Qml.ImportStructuresLoadFinished(true)
|
f.Qml.ImportStructuresLoadFinished(true)
|
||||||
@ -36,11 +41,23 @@ func (f *FrontendQt) setupAndLoadForImport(isFromIMAP bool, sourcePath, sourceEm
|
|||||||
if isFromIMAP {
|
if isFromIMAP {
|
||||||
f.transfer, err = f.ie.GetRemoteImporter(targetAddress, sourceEmail, sourcePassword, sourceServer, sourcePort)
|
f.transfer, err = f.ie.GetRemoteImporter(targetAddress, sourceEmail, sourcePassword, sourceServer, sourcePort)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
switch {
|
||||||
|
case errors.Is(err, &transfer.ErrIMAPConnection{}):
|
||||||
|
errCode = errWrongServerPathOrPort
|
||||||
|
case errors.Is(err, &transfer.ErrIMAPAuth{}):
|
||||||
|
errCode = errWrongLoginOrPassword
|
||||||
|
case errors.Is(err, &transfer.ErrIMAPAuthMethod{}):
|
||||||
|
errCode = errWrongAuthMethod
|
||||||
|
default:
|
||||||
|
errCode = errRemoteSourceLoadFailed
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
f.transfer, err = f.ie.GetLocalImporter(targetAddress, sourcePath)
|
f.transfer, err = f.ie.GetLocalImporter(targetAddress, sourcePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// The only error can be problem to load PM user and address.
|
||||||
|
errCode = errPMLoadFailed
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -51,27 +68,7 @@ func (f *FrontendQt) setupAndLoadForImport(isFromIMAP bool, sourcePath, sourceEm
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (f *FrontendQt) loadStructuresForImport() error {
|
func (f *FrontendQt) loadStructuresForImport() error {
|
||||||
f.PMStructure.Clear()
|
f.TransferRules.setTransfer(f.transfer)
|
||||||
targetMboxes, err := f.transfer.TargetMailboxes()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, mbox := range targetMboxes {
|
|
||||||
rule := &transfer.Rule{}
|
|
||||||
f.PMStructure.addEntry(newFolderInfo(mbox, rule))
|
|
||||||
}
|
|
||||||
|
|
||||||
f.ExternalStructure.Clear()
|
|
||||||
sourceMboxes, err := f.transfer.SourceMailboxes()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, mbox := range sourceMboxes {
|
|
||||||
rule := f.transfer.GetRule(mbox)
|
|
||||||
f.ExternalStructure.addEntry(newFolderInfo(mbox, rule))
|
|
||||||
}
|
|
||||||
|
|
||||||
f.ExternalStructure.transfer = f.transfer
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -82,8 +79,9 @@ func (f *FrontendQt) StartImport(email string) { // TODO email not needed
|
|||||||
f.Qml.SetProgress(0.0)
|
f.Qml.SetProgress(0.0)
|
||||||
f.Qml.SetTotal(1)
|
f.Qml.SetTotal(1)
|
||||||
f.Qml.SetImportLogFileName("")
|
f.Qml.SetImportLogFileName("")
|
||||||
f.ErrorList.Clear()
|
|
||||||
|
|
||||||
progress := f.transfer.Start()
|
progress := f.transfer.Start()
|
||||||
|
|
||||||
|
f.Qml.SetImportLogFileName(progress.FileReport())
|
||||||
f.setProgressManager(progress)
|
f.setProgressManager(progress)
|
||||||
}
|
}
|
||||||
|
|||||||
188
internal/frontend/qt-ie/mbox.go
Normal file
188
internal/frontend/qt-ie/mbox.go
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
// +build !nogui
|
||||||
|
|
||||||
|
package qtie
|
||||||
|
|
||||||
|
import (
|
||||||
|
qtcommon "github.com/ProtonMail/proton-bridge/internal/frontend/qt-common"
|
||||||
|
"github.com/ProtonMail/proton-bridge/internal/transfer"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/therecipe/qt/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MboxList is an interface between QML and targets for given rule.
|
||||||
|
type MboxList struct {
|
||||||
|
core.QAbstractListModel
|
||||||
|
|
||||||
|
containsFolders bool // Provides only folders if true. On the other hand provides only labels if false
|
||||||
|
transfer *transfer.Transfer
|
||||||
|
rule *transfer.Rule
|
||||||
|
log *logrus.Entry
|
||||||
|
|
||||||
|
_ int `property:"selectedIndex"`
|
||||||
|
|
||||||
|
_ func() `constructor:"init"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// This is needed so the type exists in QML files.
|
||||||
|
MboxList_QRegisterMetaType()
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMboxList(t *TransferRules, rule *transfer.Rule, containsFolders bool) *MboxList {
|
||||||
|
m := NewMboxList(t)
|
||||||
|
m.BeginResetModel()
|
||||||
|
m.transfer = t.transfer
|
||||||
|
m.rule = rule
|
||||||
|
m.containsFolders = containsFolders
|
||||||
|
m.log = log.
|
||||||
|
WithField("rule", m.rule.SourceMailbox.Hash()).
|
||||||
|
WithField("folders", m.containsFolders)
|
||||||
|
m.EndResetModel()
|
||||||
|
m.itemsChanged(rule)
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MboxList) init() {
|
||||||
|
m.ConnectRowCount(m.rowCount)
|
||||||
|
m.ConnectRoleNames(m.roleNames)
|
||||||
|
m.ConnectData(m.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MboxList) rowCount(index *core.QModelIndex) int {
|
||||||
|
return len(m.targetMailboxes())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MboxList) roleNames() map[int]*core.QByteArray {
|
||||||
|
m.log.
|
||||||
|
WithField("isActive", MboxIsActive).
|
||||||
|
WithField("id", MboxID).
|
||||||
|
WithField("color", MboxColor).
|
||||||
|
Debug("role names")
|
||||||
|
return map[int]*core.QByteArray{
|
||||||
|
MboxIsActive: qtcommon.NewQByteArrayFromString("isActive"),
|
||||||
|
MboxID: qtcommon.NewQByteArrayFromString("mboxID"),
|
||||||
|
MboxName: qtcommon.NewQByteArrayFromString("name"),
|
||||||
|
MboxType: qtcommon.NewQByteArrayFromString("type"),
|
||||||
|
MboxColor: qtcommon.NewQByteArrayFromString("iconColor"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MboxList) data(index *core.QModelIndex, role int) *core.QVariant {
|
||||||
|
allTargets := m.targetMailboxes()
|
||||||
|
|
||||||
|
i, valid := index.Row(), index.IsValid()
|
||||||
|
l := m.log.WithField("row", i).WithField("role", role)
|
||||||
|
l.Trace("called data()")
|
||||||
|
|
||||||
|
if !valid || i >= len(allTargets) {
|
||||||
|
l.WithField("row", i).Warning("Invalid index")
|
||||||
|
return core.NewQVariant()
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.transfer == nil {
|
||||||
|
l.Warning("Requested mbox list data before transfer is connected")
|
||||||
|
return qtcommon.NewQVariantString("")
|
||||||
|
}
|
||||||
|
|
||||||
|
mbox := allTargets[i]
|
||||||
|
|
||||||
|
switch role {
|
||||||
|
|
||||||
|
case MboxIsActive:
|
||||||
|
for _, selectedMailbox := range m.rule.TargetMailboxes {
|
||||||
|
if selectedMailbox.Hash() == mbox.Hash() {
|
||||||
|
return qtcommon.NewQVariantBool(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return qtcommon.NewQVariantBool(false)
|
||||||
|
|
||||||
|
case MboxID:
|
||||||
|
return qtcommon.NewQVariantString(mbox.Hash())
|
||||||
|
|
||||||
|
case MboxName, int(core.Qt__DisplayRole):
|
||||||
|
return qtcommon.NewQVariantString(mbox.Name)
|
||||||
|
|
||||||
|
case MboxType:
|
||||||
|
t := "label"
|
||||||
|
if mbox.IsExclusive {
|
||||||
|
t = "folder"
|
||||||
|
}
|
||||||
|
return qtcommon.NewQVariantString(t)
|
||||||
|
|
||||||
|
case MboxColor:
|
||||||
|
return qtcommon.NewQVariantString(mbox.Color)
|
||||||
|
|
||||||
|
default:
|
||||||
|
l.Error("Requested mbox list data with unknown role")
|
||||||
|
return qtcommon.NewQVariantString("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MboxList) targetMailboxes() []transfer.Mailbox {
|
||||||
|
if m.transfer == nil {
|
||||||
|
m.log.Warning("Requested target mailboxes before transfer is connected")
|
||||||
|
}
|
||||||
|
|
||||||
|
mailboxes, err := m.transfer.TargetMailboxes()
|
||||||
|
if err != nil {
|
||||||
|
m.log.WithError(err).Error("Unable to get target mailboxes")
|
||||||
|
}
|
||||||
|
|
||||||
|
return m.filter(mailboxes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MboxList) filter(mailboxes []transfer.Mailbox) (filtered []transfer.Mailbox) {
|
||||||
|
for _, mailbox := range mailboxes {
|
||||||
|
if mailbox.IsExclusive == m.containsFolders {
|
||||||
|
filtered = append(filtered, mailbox)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MboxList) itemsChanged(rule *transfer.Rule) {
|
||||||
|
m.rule = rule
|
||||||
|
allTargets := m.targetMailboxes()
|
||||||
|
l := m.log.WithField("count", len(allTargets))
|
||||||
|
l.Trace("called itemChanged()")
|
||||||
|
defer func() {
|
||||||
|
l.WithField("selected", m.SelectedIndex()).Trace("index updated")
|
||||||
|
}()
|
||||||
|
|
||||||
|
// NOTE: Be careful with indices: If they are invalid the DataChanged
|
||||||
|
// signal will not be sent to QML e.g. `end == rowCount - 1`
|
||||||
|
if len(allTargets) > 0 {
|
||||||
|
begin := m.Index(0, 0, core.NewQModelIndex())
|
||||||
|
end := m.Index(len(allTargets)-1, 0, core.NewQModelIndex())
|
||||||
|
changedRoles := []int{MboxIsActive}
|
||||||
|
m.DataChanged(begin, end, changedRoles)
|
||||||
|
}
|
||||||
|
|
||||||
|
for index, targetMailbox := range allTargets {
|
||||||
|
for _, selectedTarget := range m.rule.TargetMailboxes {
|
||||||
|
if targetMailbox.Hash() == selectedTarget.Hash() {
|
||||||
|
m.SetSelectedIndex(index)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m.SetSelectedIndex(-1)
|
||||||
|
}
|
||||||
377
internal/frontend/qt-ie/transfer_rules.go
Normal file
377
internal/frontend/qt-ie/transfer_rules.go
Normal file
@ -0,0 +1,377 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
// +build !nogui
|
||||||
|
|
||||||
|
package qtie
|
||||||
|
|
||||||
|
import (
|
||||||
|
qtcommon "github.com/ProtonMail/proton-bridge/internal/frontend/qt-common"
|
||||||
|
"github.com/ProtonMail/proton-bridge/internal/transfer"
|
||||||
|
"github.com/therecipe/qt/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TransferRules is an interface between QML and transfer.
|
||||||
|
type TransferRules struct {
|
||||||
|
core.QAbstractListModel
|
||||||
|
|
||||||
|
transfer *transfer.Transfer
|
||||||
|
|
||||||
|
targetFoldersCache map[string]*MboxList
|
||||||
|
targetLabelsCache map[string]*MboxList
|
||||||
|
|
||||||
|
_ func() `constructor:"init"`
|
||||||
|
|
||||||
|
_ func(sourceID string) *MboxList `slot:"targetFolders,auto"`
|
||||||
|
_ func(sourceID string) *MboxList `slot:"targetLabels,auto"`
|
||||||
|
_ func(sourceID string, isActive bool) `slot:"setIsRuleActive,auto"`
|
||||||
|
_ func(groupName string, isActive bool) `slot:"setIsGroupActive,auto"`
|
||||||
|
_ func(sourceID string, fromDate int64, toDate int64) `slot:"setFromToDate,auto"`
|
||||||
|
_ func(sourceID string, targetID string) `slot:"addTargetID,auto"`
|
||||||
|
_ func(sourceID string, targetID string) `slot:"removeTargetID,auto"`
|
||||||
|
|
||||||
|
_ int `property:"globalFromDate"`
|
||||||
|
_ int `property:"globalToDate"`
|
||||||
|
_ bool `property:"isLabelGroupSelected"`
|
||||||
|
_ bool `property:"isFolderGroupSelected"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// This is needed so the type exists in QML files.
|
||||||
|
TransferRules_QRegisterMetaType()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TransferRules) init() {
|
||||||
|
log.Trace("Initializing transfer rules")
|
||||||
|
|
||||||
|
t.targetFoldersCache = make(map[string]*MboxList)
|
||||||
|
t.targetLabelsCache = make(map[string]*MboxList)
|
||||||
|
|
||||||
|
t.SetGlobalFromDate(0)
|
||||||
|
t.SetGlobalToDate(0)
|
||||||
|
|
||||||
|
t.ConnectRowCount(t.rowCount)
|
||||||
|
t.ConnectRoleNames(t.roleNames)
|
||||||
|
t.ConnectData(t.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TransferRules) rowCount(index *core.QModelIndex) int {
|
||||||
|
if t.transfer == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return len(t.transfer.GetRules())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TransferRules) roleNames() map[int]*core.QByteArray {
|
||||||
|
return map[int]*core.QByteArray{
|
||||||
|
MboxIsActive: qtcommon.NewQByteArrayFromString("isActive"),
|
||||||
|
MboxID: qtcommon.NewQByteArrayFromString("mboxID"),
|
||||||
|
MboxName: qtcommon.NewQByteArrayFromString("name"),
|
||||||
|
MboxType: qtcommon.NewQByteArrayFromString("type"),
|
||||||
|
MboxColor: qtcommon.NewQByteArrayFromString("iconColor"),
|
||||||
|
RuleTargetLabelColors: qtcommon.NewQByteArrayFromString("labelColors"),
|
||||||
|
RuleFromDate: qtcommon.NewQByteArrayFromString("fromDate"),
|
||||||
|
RuleToDate: qtcommon.NewQByteArrayFromString("toDate"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TransferRules) data(index *core.QModelIndex, role int) *core.QVariant {
|
||||||
|
i, valid := index.Row(), index.IsValid()
|
||||||
|
|
||||||
|
if !valid || i >= t.rowCount(index) {
|
||||||
|
log.WithField("row", i).Warning("Invalid index")
|
||||||
|
return core.NewQVariant()
|
||||||
|
}
|
||||||
|
|
||||||
|
log := log.WithField("row", i).WithField("role", role)
|
||||||
|
|
||||||
|
if t.transfer == nil {
|
||||||
|
log.Warning("Requested transfer rules data before transfer is connected")
|
||||||
|
return qtcommon.NewQVariantString("")
|
||||||
|
}
|
||||||
|
|
||||||
|
rule := t.transfer.GetRules()[i]
|
||||||
|
|
||||||
|
switch role {
|
||||||
|
case MboxIsActive:
|
||||||
|
return qtcommon.NewQVariantBool(rule.Active)
|
||||||
|
|
||||||
|
case MboxID:
|
||||||
|
return qtcommon.NewQVariantString(rule.SourceMailbox.Hash())
|
||||||
|
|
||||||
|
case MboxName:
|
||||||
|
return qtcommon.NewQVariantString(rule.SourceMailbox.Name)
|
||||||
|
|
||||||
|
case MboxType:
|
||||||
|
if rule.SourceMailbox.IsSystemFolder() {
|
||||||
|
return qtcommon.NewQVariantString(FolderTypeSystem)
|
||||||
|
}
|
||||||
|
if rule.SourceMailbox.IsExclusive {
|
||||||
|
return qtcommon.NewQVariantString(FolderTypeFolder)
|
||||||
|
}
|
||||||
|
return qtcommon.NewQVariantString(FolderTypeLabel)
|
||||||
|
|
||||||
|
case MboxColor:
|
||||||
|
return qtcommon.NewQVariantString(rule.SourceMailbox.Color)
|
||||||
|
|
||||||
|
case RuleTargetLabelColors:
|
||||||
|
colors := ""
|
||||||
|
for _, m := range rule.TargetMailboxes {
|
||||||
|
if m.IsExclusive {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if colors != "" {
|
||||||
|
colors += ";"
|
||||||
|
}
|
||||||
|
colors += m.Color
|
||||||
|
}
|
||||||
|
return qtcommon.NewQVariantString(colors)
|
||||||
|
|
||||||
|
case RuleFromDate:
|
||||||
|
return qtcommon.NewQVariantLong(rule.FromTime)
|
||||||
|
|
||||||
|
case RuleToDate:
|
||||||
|
return qtcommon.NewQVariantLong(rule.ToTime)
|
||||||
|
|
||||||
|
default:
|
||||||
|
log.Error("Requested transfer rules data with unknown role")
|
||||||
|
return qtcommon.NewQVariantString("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TransferRules) setTransfer(transfer *transfer.Transfer) {
|
||||||
|
log.Debug("Setting transfer")
|
||||||
|
t.BeginResetModel()
|
||||||
|
defer t.EndResetModel()
|
||||||
|
|
||||||
|
t.transfer = transfer
|
||||||
|
|
||||||
|
t.updateGroupSelection()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getters
|
||||||
|
|
||||||
|
func (t *TransferRules) targetFolders(sourceID string) *MboxList {
|
||||||
|
rule := t.getRule(sourceID)
|
||||||
|
if rule == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.targetFoldersCache[sourceID] == nil {
|
||||||
|
log.WithField("source", sourceID).Debug("New target folder")
|
||||||
|
t.targetFoldersCache[sourceID] = newMboxList(t, rule, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
return t.targetFoldersCache[sourceID]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TransferRules) targetLabels(sourceID string) *MboxList {
|
||||||
|
rule := t.getRule(sourceID)
|
||||||
|
if rule == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.targetLabelsCache[sourceID] == nil {
|
||||||
|
log.WithField("source", sourceID).Debug("New target label")
|
||||||
|
t.targetLabelsCache[sourceID] = newMboxList(t, rule, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return t.targetLabelsCache[sourceID]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setters
|
||||||
|
|
||||||
|
func (t *TransferRules) setIsGroupActive(groupName string, isActive bool) {
|
||||||
|
wantExclusive := (groupName == FolderTypeLabel)
|
||||||
|
for _, rule := range t.transfer.GetRules() {
|
||||||
|
if rule.SourceMailbox.IsExclusive != wantExclusive {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if rule.SourceMailbox.IsSystemFolder() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if rule.Active != isActive {
|
||||||
|
t.setIsRuleActive(rule.SourceMailbox.Hash(), isActive)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TransferRules) setIsRuleActive(sourceID string, isActive bool) {
|
||||||
|
log.WithField("source", sourceID).WithField("active", isActive).Trace("Setting rule as active/inactive")
|
||||||
|
|
||||||
|
rule := t.getRule(sourceID)
|
||||||
|
if rule == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if isActive {
|
||||||
|
t.setRule(rule.SourceMailbox, rule.TargetMailboxes, rule.FromTime, rule.ToTime, []int{MboxIsActive})
|
||||||
|
} else {
|
||||||
|
t.unsetRule(rule.SourceMailbox)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TransferRules) setFromToDate(sourceID string, fromTime int64, toTime int64) {
|
||||||
|
log.WithField("source", sourceID).WithField("fromTime", fromTime).WithField("toTime", toTime).Trace("Setting from and to dates")
|
||||||
|
|
||||||
|
if sourceID == "-1" {
|
||||||
|
t.transfer.SetGlobalTimeLimit(fromTime, toTime)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rule := t.getRule(sourceID)
|
||||||
|
if rule == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.setRule(rule.SourceMailbox, rule.TargetMailboxes, fromTime, toTime, []int{RuleFromDate, RuleToDate})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TransferRules) addTargetID(sourceID string, targetID string) {
|
||||||
|
log.WithField("source", sourceID).WithField("target", targetID).Trace("Adding target")
|
||||||
|
|
||||||
|
rule := t.getRule(sourceID)
|
||||||
|
if rule == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
targetMailboxToAdd := t.getMailbox(t.transfer.TargetMailboxes, targetID)
|
||||||
|
if targetMailboxToAdd == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
newTargetMailboxes := []transfer.Mailbox{}
|
||||||
|
found := false
|
||||||
|
for _, targetMailbox := range rule.TargetMailboxes {
|
||||||
|
if targetMailbox.Hash() == targetMailboxToAdd.Hash() {
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
if !targetMailboxToAdd.IsExclusive || (targetMailboxToAdd.IsExclusive && !targetMailbox.IsExclusive) {
|
||||||
|
newTargetMailboxes = append(newTargetMailboxes, targetMailbox)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
newTargetMailboxes = append(newTargetMailboxes, *targetMailboxToAdd)
|
||||||
|
}
|
||||||
|
t.setRule(rule.SourceMailbox, newTargetMailboxes, rule.FromTime, rule.ToTime, []int{RuleTargetLabelColors})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TransferRules) removeTargetID(sourceID string, targetID string) {
|
||||||
|
log.WithField("source", sourceID).WithField("target", targetID).Trace("Removing target")
|
||||||
|
|
||||||
|
rule := t.getRule(sourceID)
|
||||||
|
if rule == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
targetMailboxToRemove := t.getMailbox(t.transfer.TargetMailboxes, targetID)
|
||||||
|
if targetMailboxToRemove == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
newTargetMailboxes := []transfer.Mailbox{}
|
||||||
|
for _, targetMailbox := range rule.TargetMailboxes {
|
||||||
|
if targetMailbox.Hash() != targetMailboxToRemove.Hash() {
|
||||||
|
newTargetMailboxes = append(newTargetMailboxes, targetMailbox)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.setRule(rule.SourceMailbox, newTargetMailboxes, rule.FromTime, rule.ToTime, []int{RuleTargetLabelColors})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helpers
|
||||||
|
|
||||||
|
func (t *TransferRules) getRule(sourceID string) *transfer.Rule {
|
||||||
|
mailbox := t.getMailbox(t.transfer.SourceMailboxes, sourceID)
|
||||||
|
if mailbox == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return t.transfer.GetRule(*mailbox)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TransferRules) getMailbox(mailboxesGetter func() ([]transfer.Mailbox, error), sourceID string) *transfer.Mailbox {
|
||||||
|
if t.transfer == nil {
|
||||||
|
log.Warn("Getting mailbox without avaiable transfer")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
mailboxes, err := mailboxesGetter()
|
||||||
|
if err != nil {
|
||||||
|
log.WithError(err).Error("Failed to get source mailboxes")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for _, mailbox := range mailboxes {
|
||||||
|
if mailbox.Hash() == sourceID {
|
||||||
|
return &mailbox
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.WithField("source", sourceID).Error("Mailbox not found for source")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TransferRules) setRule(sourceMailbox transfer.Mailbox, targetMailboxes []transfer.Mailbox, fromTime, toTime int64, changedRoles []int) {
|
||||||
|
if err := t.transfer.SetRule(sourceMailbox, targetMailboxes, fromTime, toTime); err != nil {
|
||||||
|
log.WithError(err).WithField("source", sourceMailbox.Hash()).Error("Failed to set rule")
|
||||||
|
}
|
||||||
|
t.ruleChanged(sourceMailbox, changedRoles)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TransferRules) unsetRule(sourceMailbox transfer.Mailbox) {
|
||||||
|
t.transfer.UnsetRule(sourceMailbox)
|
||||||
|
t.ruleChanged(sourceMailbox, []int{MboxIsActive})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TransferRules) ruleChanged(sourceMailbox transfer.Mailbox, changedRoles []int) {
|
||||||
|
for row, rule := range t.transfer.GetRules() {
|
||||||
|
if rule.SourceMailbox.Hash() != sourceMailbox.Hash() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
t.targetFolders(sourceMailbox.Hash()).itemsChanged(rule)
|
||||||
|
t.targetLabels(sourceMailbox.Hash()).itemsChanged(rule)
|
||||||
|
|
||||||
|
index := t.Index(row, 0, core.NewQModelIndex())
|
||||||
|
if !index.IsValid() || row >= t.rowCount(index) {
|
||||||
|
log.WithField("row", row).Warning("Invalid index")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t.DataChanged(index, index, changedRoles)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
t.updateGroupSelection()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TransferRules) updateGroupSelection() {
|
||||||
|
areAllLabelsSelected, areAllFoldersSelected := true, true
|
||||||
|
for _, rule := range t.transfer.GetRules() {
|
||||||
|
if rule.Active {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if rule.SourceMailbox.IsSystemFolder() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if rule.SourceMailbox.IsExclusive {
|
||||||
|
areAllFoldersSelected = false
|
||||||
|
} else {
|
||||||
|
areAllLabelsSelected = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !areAllLabelsSelected && !areAllFoldersSelected {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t.SetIsLabelGroupSelected(areAllLabelsSelected)
|
||||||
|
t.SetIsFolderGroupSelected(areAllFoldersSelected)
|
||||||
|
}
|
||||||
@ -71,7 +71,7 @@ type GoQMLInterface struct {
|
|||||||
_ func() `signal:"openManual"`
|
_ func() `signal:"openManual"`
|
||||||
_ func(showMessage bool) `signal:"runCheckVersion"`
|
_ func(showMessage bool) `signal:"runCheckVersion"`
|
||||||
_ func() `slot:"getLocalVersionInfo"`
|
_ func() `slot:"getLocalVersionInfo"`
|
||||||
_ func(fname string) `slot:"loadImportReports"`
|
_ func() `slot:"loadImportReports"`
|
||||||
|
|
||||||
_ func() `slot:"quit"`
|
_ func() `slot:"quit"`
|
||||||
_ func() `slot:"loadAccounts"`
|
_ func() `slot:"loadAccounts"`
|
||||||
@ -87,7 +87,7 @@ type GoQMLInterface struct {
|
|||||||
_ func() string `slot:"getBackendVersion"`
|
_ func() string `slot:"getBackendVersion"`
|
||||||
|
|
||||||
_ func(description, client, address string) bool `slot:"sendBug"`
|
_ func(description, client, address string) bool `slot:"sendBug"`
|
||||||
_ func(address, fname string) bool `slot:"sendImportReport"`
|
_ func(address string) bool `slot:"sendImportReport"`
|
||||||
_ func(address string) `slot:"loadStructureForExport"`
|
_ func(address string) `slot:"loadStructureForExport"`
|
||||||
_ func() string `slot:"leastUsedColor"`
|
_ func() string `slot:"leastUsedColor"`
|
||||||
_ func(username string, name string, color string, isLabel bool, sourceID string) bool `slot:"createLabelOrFolder"`
|
_ func(username string, name string, color string, isLabel bool, sourceID string) bool `slot:"createLabelOrFolder"`
|
||||||
@ -104,13 +104,13 @@ type GoQMLInterface struct {
|
|||||||
_ func(evType string, msg string) `signal:"emitEvent"`
|
_ func(evType string, msg string) `signal:"emitEvent"`
|
||||||
_ func(tabIndex int, message string) `signal:"notifyBubble"`
|
_ func(tabIndex int, message string) `signal:"notifyBubble"`
|
||||||
|
|
||||||
_ func() `signal:"bubbleClosed"`
|
_ func() `signal:"bubbleClosed"`
|
||||||
_ func() `signal:"simpleErrorHappen"`
|
_ func() `signal:"simpleErrorHappen"`
|
||||||
_ func() `signal:"askErrorHappen"`
|
_ func() `signal:"askErrorHappen"`
|
||||||
_ func() `signal:"retryErrorHappen"`
|
_ func() `signal:"retryErrorHappen"`
|
||||||
_ func() `signal:"pauseProcess"`
|
_ func() `signal:"pauseProcess"`
|
||||||
_ func() `signal:"resumeProcess"`
|
_ func() `signal:"resumeProcess"`
|
||||||
_ func(clearUnfinished bool) `signal:"cancelProcess"`
|
_ func() `signal:"cancelProcess"`
|
||||||
|
|
||||||
_ func(iAccount int, prefRem bool) `slot:"deleteAccount"`
|
_ func(iAccount int, prefRem bool) `slot:"deleteAccount"`
|
||||||
_ func(iAccount int) `slot:"logoutAccount"`
|
_ func(iAccount int) `slot:"logoutAccount"`
|
||||||
|
|||||||
@ -114,6 +114,7 @@ type ImportExporter interface {
|
|||||||
GetMBOXExporter(string, string) (*transfer.Transfer, error)
|
GetMBOXExporter(string, string) (*transfer.Transfer, error)
|
||||||
SetCurrentOS(os string)
|
SetCurrentOS(os string)
|
||||||
ReportBug(osType, osVersion, description, accountName, address, emailClient string) error
|
ReportBug(osType, osVersion, description, accountName, address, emailClient string) error
|
||||||
|
ReportFile(osType, osVersion, accountName, address string, logdata []byte) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type importExportWrap struct {
|
type importExportWrap struct {
|
||||||
|
|||||||
@ -15,8 +15,8 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
// Code generated by ./credits.sh at Mon Jul 13 14:02:21 CEST 2020. DO NOT EDIT.
|
// Code generated by ./credits.sh at Fri 07 Aug 2020 06:34:27 AM CEST. DO NOT EDIT.
|
||||||
|
|
||||||
package importexport
|
package importexport
|
||||||
|
|
||||||
const Credits = "github.com/0xAX/notificator;github.com/ProtonMail/bcrypt;github.com/ProtonMail/crypto;github.com/ProtonMail/docker-credential-helpers;github.com/ProtonMail/go-appdir;github.com/ProtonMail/go-apple-mobileconfig;github.com/ProtonMail/go-autostart;github.com/ProtonMail/go-imap-id;github.com/ProtonMail/go-smtp;github.com/ProtonMail/go-vcard;github.com/ProtonMail/gopenpgp/v2;github.com/abiosoft/ishell;github.com/abiosoft/readline;github.com/allan-simon/go-singleinstance;github.com/andybalholm/cascadia;github.com/certifi/gocertifi;github.com/chzyer/logex;github.com/chzyer/test;github.com/cucumber/godog;github.com/docker/docker-credential-helpers;github.com/emersion/go-imap;github.com/emersion/go-imap-appendlimit;github.com/emersion/go-imap-idle;github.com/emersion/go-imap-move;github.com/emersion/go-imap-quota;github.com/emersion/go-imap-specialuse;github.com/emersion/go-imap-unselect;github.com/emersion/go-mbox;github.com/emersion/go-message;github.com/emersion/go-sasl;github.com/emersion/go-smtp;github.com/emersion/go-textwrapper;github.com/emersion/go-vcard;github.com/fatih/color;github.com/flynn-archive/go-shlex;github.com/getsentry/raven-go;github.com/go-resty/resty/v2;github.com/golang/mock;github.com/google/go-cmp;github.com/gopherjs/gopherjs;github.com/hashicorp/go-multierror;github.com/jameskeane/bcrypt;github.com/jaytaylor/html2text;github.com/jhillyerd/enmime;github.com/kardianos/osext;github.com/keybase/go-keychain;github.com/logrusorgru/aurora;github.com/miekg/dns;github.com/myesui/uuid;github.com/nsf/jsondiff;github.com/pkg/errors;github.com/sirupsen/logrus;github.com/skratchdot/open-golang;github.com/stretchr/testify;github.com/therecipe/qt;github.com/twinj/uuid;github.com/urfave/cli;go.etcd.io/bbolt;golang.org/x/crypto;golang.org/x/net;golang.org/x/text;gopkg.in/stretchr/testify.v1;;Font Awesome 4.7.0;;Qt 5.13 by Qt group;"
|
const Credits = "github.com/0xAX/notificator;github.com/abiosoft/ishell;github.com/abiosoft/readline;github.com/allan-simon/go-singleinstance;github.com/andybalholm/cascadia;github.com/certifi/gocertifi;github.com/chzyer/logex;github.com/chzyer/test;github.com/cucumber/godog;github.com/docker/docker-credential-helpers;github.com/emersion/go-imap;github.com/emersion/go-imap-appendlimit;github.com/emersion/go-imap-idle;github.com/emersion/go-imap-move;github.com/emersion/go-imap-quota;github.com/emersion/go-imap-specialuse;github.com/emersion/go-imap-unselect;github.com/emersion/go-mbox;github.com/emersion/go-message;github.com/emersion/go-sasl;github.com/emersion/go-smtp;github.com/emersion/go-textwrapper;github.com/emersion/go-vcard;github.com/fatih/color;github.com/flynn-archive/go-shlex;github.com/getsentry/raven-go;github.com/go-delve/delve;github.com/golang/mock;github.com/google/go-cmp;github.com/gopherjs/gopherjs;github.com/go-resty/resty/v2;github.com/hashicorp/go-multierror;github.com/jameskeane/bcrypt;github.com/jaytaylor/html2text;github.com/jhillyerd/enmime;github.com/kardianos/osext;github.com/keybase/go-keychain;github.com/logrusorgru/aurora;github.com/miekg/dns;github.com/myesui/uuid;github.com/nsf/jsondiff;github.com/pkg/errors;github.com/ProtonMail/bcrypt;github.com/ProtonMail/crypto;github.com/ProtonMail/docker-credential-helpers;github.com/ProtonMail/go-appdir;github.com/ProtonMail/go-apple-mobileconfig;github.com/ProtonMail/go-autostart;github.com/ProtonMail/go-imap-id;github.com/ProtonMail/gopenpgp/v2;github.com/ProtonMail/go-smtp;github.com/ProtonMail/go-vcard;github.com/psampaz/go-mod-outdated;github.com/sirupsen/logrus;github.com/skratchdot/open-golang;github.com/stretchr/testify;github.com/therecipe/qt;github.com/twinj/uuid;github.com/urfave/cli;go.etcd.io/bbolt;golang.org/x/crypto;golang.org/x/net;golang.org/x/text;gopkg.in/stretchr/testify.v1;;Font Awesome 4.7.0;;Qt 5.13 by Qt group;"
|
||||||
|
|||||||
@ -19,8 +19,11 @@
|
|||||||
package importexport
|
package importexport
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
|
||||||
"github.com/ProtonMail/proton-bridge/internal/transfer"
|
"github.com/ProtonMail/proton-bridge/internal/transfer"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/users"
|
"github.com/ProtonMail/proton-bridge/internal/users"
|
||||||
|
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
|
||||||
|
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/listener"
|
"github.com/ProtonMail/proton-bridge/pkg/listener"
|
||||||
logrus "github.com/sirupsen/logrus"
|
logrus "github.com/sirupsen/logrus"
|
||||||
@ -61,15 +64,17 @@ func (ie *ImportExport) ReportBug(osType, osVersion, description, accountName, a
|
|||||||
defer c.Logout()
|
defer c.Logout()
|
||||||
|
|
||||||
title := "[Import-Export] Bug"
|
title := "[Import-Export] Bug"
|
||||||
if err := c.ReportBugWithEmailClient(
|
report := pmapi.ReportReq{
|
||||||
osType,
|
OS: osType,
|
||||||
osVersion,
|
OSVersion: osVersion,
|
||||||
title,
|
Browser: emailClient,
|
||||||
description,
|
Title: title,
|
||||||
accountName,
|
Description: description,
|
||||||
address,
|
Username: accountName,
|
||||||
emailClient,
|
Email: address,
|
||||||
); err != nil {
|
}
|
||||||
|
|
||||||
|
if err := c.Report(report); err != nil {
|
||||||
log.Error("Reporting bug failed: ", err)
|
log.Error("Reporting bug failed: ", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -79,6 +84,35 @@ func (ie *ImportExport) ReportBug(osType, osVersion, description, accountName, a
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReportFile submits import report file
|
||||||
|
func (ie *ImportExport) ReportFile(osType, osVersion, accountName, address string, logdata []byte) error {
|
||||||
|
c := ie.clientManager.GetAnonymousClient()
|
||||||
|
defer c.Logout()
|
||||||
|
|
||||||
|
title := "[Import-Export] report file"
|
||||||
|
description := "An import/export report from the user swam down the river."
|
||||||
|
|
||||||
|
report := pmapi.ReportReq{
|
||||||
|
OS: osType,
|
||||||
|
OSVersion: osVersion,
|
||||||
|
Description: description,
|
||||||
|
Title: title,
|
||||||
|
Username: accountName,
|
||||||
|
Email: address,
|
||||||
|
}
|
||||||
|
|
||||||
|
report.AddAttachment("log", "report.log", bytes.NewReader(logdata))
|
||||||
|
|
||||||
|
if err := c.Report(report); err != nil {
|
||||||
|
log.Error("Sending report failed: ", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("Report successfully sent")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetLocalImporter returns transferrer from local EML or MBOX structure to ProtonMail account.
|
// GetLocalImporter returns transferrer from local EML or MBOX structure to ProtonMail account.
|
||||||
func (ie *ImportExport) GetLocalImporter(address, path string) (*transfer.Transfer, error) {
|
func (ie *ImportExport) GetLocalImporter(address, path string) (*transfer.Transfer, error) {
|
||||||
source := transfer.NewLocalProvider(path)
|
source := transfer.NewLocalProvider(path)
|
||||||
@ -130,7 +164,7 @@ func (ie *ImportExport) getPMAPIProvider(address string) (*transfer.PMAPIProvide
|
|||||||
|
|
||||||
addressID, err := user.GetAddressID(address)
|
addressID, err := user.GetAddressID(address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
log.WithError(err).Info("Address does not exist, using all addresses")
|
||||||
}
|
}
|
||||||
|
|
||||||
return transfer.NewPMAPIProvider(ie.clientManager, user.ID(), addressID)
|
return transfer.NewPMAPIProvider(ie.clientManager, user.ID(), addressID)
|
||||||
|
|||||||
@ -15,7 +15,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
// Code generated by ./release-notes.sh at 'Thu Jun 25 10:06:16 CEST 2020'. DO NOT EDIT.
|
// Code generated by ./release-notes.sh at 'Fri 07 Aug 2020 06:34:27 AM CEST'. DO NOT EDIT.
|
||||||
|
|
||||||
package importexport
|
package importexport
|
||||||
|
|
||||||
|
|||||||
@ -5,10 +5,9 @@
|
|||||||
package mocks
|
package mocks
|
||||||
|
|
||||||
import (
|
import (
|
||||||
reflect "reflect"
|
|
||||||
|
|
||||||
pmapi "github.com/ProtonMail/proton-bridge/pkg/pmapi"
|
pmapi "github.com/ProtonMail/proton-bridge/pkg/pmapi"
|
||||||
gomock "github.com/golang/mock/gomock"
|
gomock "github.com/golang/mock/gomock"
|
||||||
|
reflect "reflect"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MockPanicHandler is a mock of PanicHandler interface
|
// MockPanicHandler is a mock of PanicHandler interface
|
||||||
|
|||||||
@ -5,10 +5,9 @@
|
|||||||
package mocks
|
package mocks
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
gomock "github.com/golang/mock/gomock"
|
||||||
reflect "reflect"
|
reflect "reflect"
|
||||||
time "time"
|
time "time"
|
||||||
|
|
||||||
gomock "github.com/golang/mock/gomock"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// MockListener is a mock of Listener interface
|
// MockListener is a mock of Listener interface
|
||||||
|
|||||||
@ -33,6 +33,11 @@ type Mailbox struct {
|
|||||||
IsExclusive bool
|
IsExclusive bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsSystemFolder returns true when ID corresponds to PM system folder.
|
||||||
|
func (m Mailbox) IsSystemFolder() bool {
|
||||||
|
return pmapi.IsSystemLabel(m.ID)
|
||||||
|
}
|
||||||
|
|
||||||
// Hash returns unique identifier to be used for matching.
|
// Hash returns unique identifier to be used for matching.
|
||||||
func (m Mailbox) Hash() string {
|
func (m Mailbox) Hash() string {
|
||||||
return fmt.Sprintf("%x", sha256.Sum256([]byte(m.Name)))
|
return fmt.Sprintf("%x", sha256.Sum256([]byte(m.Name)))
|
||||||
|
|||||||
@ -198,7 +198,7 @@ func (p *Progress) callWrap(callback func() error) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
p.Pause(err.Error())
|
p.Pause("paused due to " + err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -333,3 +333,11 @@ func (p *Progress) GenerateBugReport() []byte {
|
|||||||
}
|
}
|
||||||
return bugReport.getData()
|
return bugReport.getData()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Progress) FileReport() (path string) {
|
||||||
|
if r := p.fileReport; r != nil {
|
||||||
|
path = r.path
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|||||||
@ -39,9 +39,13 @@ func (p *EMLProvider) ID() string {
|
|||||||
// Mailboxes returns all available folder names from root of EML files.
|
// Mailboxes returns all available folder names from root of EML files.
|
||||||
// In case the same folder name is used more than once (for example root/a/foo
|
// In case the same folder name is used more than once (for example root/a/foo
|
||||||
// and root/b/foo), it's treated as the same folder.
|
// and root/b/foo), it's treated as the same folder.
|
||||||
func (p *EMLProvider) Mailboxes(includeEmpty, includeAllMail bool) ([]Mailbox, error) {
|
func (p *EMLProvider) Mailboxes(includeEmpty, includeAllMail bool) (mailboxes []Mailbox, err error) {
|
||||||
|
// Special case for exporting--we don't know the path before setup if finished.
|
||||||
|
if p.root == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var folderNames []string
|
var folderNames []string
|
||||||
var err error
|
|
||||||
if includeEmpty {
|
if includeEmpty {
|
||||||
folderNames, err = getFolderNames(p.root)
|
folderNames, err = getFolderNames(p.root)
|
||||||
} else {
|
} else {
|
||||||
@ -51,7 +55,6 @@ func (p *EMLProvider) Mailboxes(includeEmpty, includeAllMail bool) ([]Mailbox, e
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
mailboxes := []Mailbox{}
|
|
||||||
for _, folderName := range folderNames {
|
for _, folderName := range folderNames {
|
||||||
mailboxes = append(mailboxes, Mailbox{
|
mailboxes = append(mailboxes, Mailbox{
|
||||||
ID: "",
|
ID: "",
|
||||||
|
|||||||
66
internal/transfer/provider_imap_errors.go
Normal file
66
internal/transfer/provider_imap_errors.go
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
// 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 transfer
|
||||||
|
|
||||||
|
// imapError is base for all IMAP errors.
|
||||||
|
type imapError struct {
|
||||||
|
Message string
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e imapError) Error() string {
|
||||||
|
return e.Message + ": " + e.Err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e imapError) Unwrap() error {
|
||||||
|
return e.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e imapError) Cause() error {
|
||||||
|
return e.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrIMAPConnection is error representing connection issues.
|
||||||
|
type ErrIMAPConnection struct {
|
||||||
|
imapError
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrIMAPConnection) Is(target error) bool {
|
||||||
|
_, ok := target.(*ErrIMAPConnection)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrIMAPAuth is error representing authentication issues.
|
||||||
|
type ErrIMAPAuth struct {
|
||||||
|
imapError
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrIMAPAuth) Is(target error) bool {
|
||||||
|
_, ok := target.(*ErrIMAPAuth)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrIMAPAuthMethod is error representing wrong auth method.
|
||||||
|
type ErrIMAPAuthMethod struct {
|
||||||
|
imapError
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrIMAPAuthMethod) Is(target error) bool {
|
||||||
|
_, ok := target.(*ErrIMAPAuthMethod)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
@ -137,7 +137,7 @@ func (p *IMAPProvider) auth() error { //nolint[funlen]
|
|||||||
log.Info("Connecting to server")
|
log.Info("Connecting to server")
|
||||||
|
|
||||||
if _, err := net.DialTimeout("tcp", p.addr, imapDialTimeout); err != nil {
|
if _, err := net.DialTimeout("tcp", p.addr, imapDialTimeout); err != nil {
|
||||||
return errors.Wrap(err, "failed to dial server")
|
return ErrIMAPConnection{imapError{Err: err, Message: "failed to dial server"}}
|
||||||
}
|
}
|
||||||
|
|
||||||
var client *imapClient.Client
|
var client *imapClient.Client
|
||||||
@ -149,7 +149,7 @@ func (p *IMAPProvider) auth() error { //nolint[funlen]
|
|||||||
client, err = imapClient.DialTLS(p.addr, nil)
|
client, err = imapClient.DialTLS(p.addr, nil)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "failed to connect to server")
|
return ErrIMAPConnection{imapError{Err: err, Message: "failed to connect to server"}}
|
||||||
}
|
}
|
||||||
|
|
||||||
client.ErrorLog = &imapErrorLogger{logrus.WithField("pkg", "imap-client")}
|
client.ErrorLog = &imapErrorLogger{logrus.WithField("pkg", "imap-client")}
|
||||||
@ -170,7 +170,7 @@ func (p *IMAPProvider) auth() error { //nolint[funlen]
|
|||||||
capability, err := p.client.Capability()
|
capability, err := p.client.Capability()
|
||||||
log.WithField("capability", capability).WithError(err).Debug("Server capability")
|
log.WithField("capability", capability).WithError(err).Debug("Server capability")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "failed to get capabilities")
|
return ErrIMAPConnection{imapError{Err: err, Message: "failed to get capabilities"}}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SASL AUTH PLAIN
|
// SASL AUTH PLAIN
|
||||||
@ -178,7 +178,7 @@ func (p *IMAPProvider) auth() error { //nolint[funlen]
|
|||||||
log.Debug("Trying plain auth")
|
log.Debug("Trying plain auth")
|
||||||
authPlain := sasl.NewPlainClient("", p.username, p.password)
|
authPlain := sasl.NewPlainClient("", p.username, p.password)
|
||||||
if err = p.client.Authenticate(authPlain); err != nil {
|
if err = p.client.Authenticate(authPlain); err != nil {
|
||||||
return errors.Wrap(err, "plain auth failed")
|
return ErrIMAPAuth{imapError{Err: err, Message: "plain auth failed"}}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,12 +186,12 @@ func (p *IMAPProvider) auth() error { //nolint[funlen]
|
|||||||
if ok, _ := p.client.Support("IMAP4rev1"); p.client.State() == imap.NotAuthenticatedState && ok {
|
if ok, _ := p.client.Support("IMAP4rev1"); p.client.State() == imap.NotAuthenticatedState && ok {
|
||||||
log.Debug("Trying login")
|
log.Debug("Trying login")
|
||||||
if err = p.client.Login(p.username, p.password); err != nil {
|
if err = p.client.Login(p.username, p.password); err != nil {
|
||||||
return errors.Wrap(err, "login failed")
|
return ErrIMAPAuth{imapError{Err: err, Message: "login failed"}}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.client.State() == imap.NotAuthenticatedState {
|
if p.client.State() == imap.NotAuthenticatedState {
|
||||||
return errors.New("unknown auth method")
|
return ErrIMAPAuthMethod{imapError{Err: err, Message: "unknown auth method"}}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("Logged in")
|
log.Info("Logged in")
|
||||||
|
|||||||
@ -38,20 +38,24 @@ type PMAPIProvider struct {
|
|||||||
|
|
||||||
// NewPMAPIProvider returns new PMAPIProvider.
|
// NewPMAPIProvider returns new PMAPIProvider.
|
||||||
func NewPMAPIProvider(clientManager ClientManager, userID, addressID string) (*PMAPIProvider, error) {
|
func NewPMAPIProvider(clientManager ClientManager, userID, addressID string) (*PMAPIProvider, error) {
|
||||||
keyRing, err := clientManager.GetClient(userID).KeyRingForAddressID(addressID)
|
provider := &PMAPIProvider{
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to get key ring")
|
|
||||||
}
|
|
||||||
|
|
||||||
return &PMAPIProvider{
|
|
||||||
clientManager: clientManager,
|
clientManager: clientManager,
|
||||||
userID: userID,
|
userID: userID,
|
||||||
addressID: addressID,
|
addressID: addressID,
|
||||||
keyRing: keyRing,
|
|
||||||
|
|
||||||
importMsgReqMap: map[string]*pmapi.ImportMsgReq{},
|
importMsgReqMap: map[string]*pmapi.ImportMsgReq{},
|
||||||
importMsgReqSize: 0,
|
importMsgReqSize: 0,
|
||||||
}, nil
|
}
|
||||||
|
|
||||||
|
if addressID != "" {
|
||||||
|
keyRing, err := clientManager.GetClient(userID).KeyRingForAddressID(addressID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to get key ring")
|
||||||
|
}
|
||||||
|
provider.keyRing = keyRing
|
||||||
|
}
|
||||||
|
|
||||||
|
return provider, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PMAPIProvider) client() pmapi.Client {
|
func (p *PMAPIProvider) client() pmapi.Client {
|
||||||
@ -86,7 +90,14 @@ func (p *PMAPIProvider) Mailboxes(includeEmpty, includeAllMail bool) ([]Mailbox,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mailboxes := getSystemMailboxes(includeAllMail)
|
mailboxes := []Mailbox{}
|
||||||
|
for _, mailbox := range getSystemMailboxes(includeAllMail) {
|
||||||
|
if !includeEmpty && emptyLabelsMap[mailbox.ID] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
mailboxes = append(mailboxes, mailbox)
|
||||||
|
}
|
||||||
for _, label := range sortedLabels {
|
for _, label := range sortedLabels {
|
||||||
if !includeEmpty && emptyLabelsMap[label.ID] {
|
if !includeEmpty && emptyLabelsMap[label.ID] {
|
||||||
continue
|
continue
|
||||||
|
|||||||
@ -86,14 +86,15 @@ func (p *PMAPIProvider) transferTo(rule *Rule, progress *Progress, ch chan<- Mes
|
|||||||
progress.callWrap(func() error {
|
progress.callWrap(func() error {
|
||||||
desc := false
|
desc := false
|
||||||
pmapiMessages, count, err := p.listMessages(&pmapi.MessagesFilter{
|
pmapiMessages, count, err := p.listMessages(&pmapi.MessagesFilter{
|
||||||
LabelID: rule.SourceMailbox.ID,
|
AddressID: p.addressID,
|
||||||
Begin: rule.FromTime,
|
LabelID: rule.SourceMailbox.ID,
|
||||||
End: rule.ToTime,
|
Begin: rule.FromTime,
|
||||||
BeginID: nextID,
|
End: rule.ToTime,
|
||||||
PageSize: pmapiListPageSize,
|
BeginID: nextID,
|
||||||
Page: 0,
|
PageSize: pmapiListPageSize,
|
||||||
Sort: "ID",
|
Page: 0,
|
||||||
Desc: &desc,
|
Sort: "ID",
|
||||||
|
Desc: &desc,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@ -22,6 +22,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -42,6 +43,11 @@ type transferRules struct {
|
|||||||
// E.g., every message will be imported into this mailbox.
|
// E.g., every message will be imported into this mailbox.
|
||||||
globalMailbox *Mailbox
|
globalMailbox *Mailbox
|
||||||
|
|
||||||
|
// globalFromTime and globalToTime is applied to every rule right
|
||||||
|
// before the transfer (propagateGlobalTime has to be called).
|
||||||
|
globalFromTime int64
|
||||||
|
globalToTime int64
|
||||||
|
|
||||||
// skipEncryptedMessages determines whether message which cannot
|
// skipEncryptedMessages determines whether message which cannot
|
||||||
// be decrypted should be exported or skipped.
|
// be decrypted should be exported or skipped.
|
||||||
skipEncryptedMessages bool
|
skipEncryptedMessages bool
|
||||||
@ -81,10 +87,18 @@ func (r *transferRules) setGlobalMailbox(mailbox *Mailbox) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *transferRules) setGlobalTimeLimit(fromTime, toTime int64) {
|
func (r *transferRules) setGlobalTimeLimit(fromTime, toTime int64) {
|
||||||
|
r.globalFromTime = fromTime
|
||||||
|
r.globalToTime = toTime
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *transferRules) propagateGlobalTime() {
|
||||||
|
if r.globalFromTime == 0 && r.globalToTime == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
for _, rule := range r.rules {
|
for _, rule := range r.rules {
|
||||||
if !rule.HasTimeLimit() {
|
if !rule.HasTimeLimit() {
|
||||||
rule.FromTime = fromTime
|
rule.FromTime = r.globalFromTime
|
||||||
rule.ToTime = toTime
|
rule.ToTime = r.globalToTime
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -122,8 +136,9 @@ func (r *transferRules) setDefaultRules(sourceMailboxes []Mailbox, targetMailbox
|
|||||||
}
|
}
|
||||||
|
|
||||||
targetMailboxes := sourceMailbox.findMatchingMailboxes(targetMailboxes)
|
targetMailboxes := sourceMailbox.findMatchingMailboxes(targetMailboxes)
|
||||||
if len(targetMailboxes) == 0 {
|
|
||||||
targetMailboxes = defaultCallback(sourceMailbox)
|
if !containsExclusive(targetMailboxes) {
|
||||||
|
targetMailboxes = append(targetMailboxes, defaultCallback(sourceMailbox)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
active := true
|
active := true
|
||||||
@ -147,10 +162,14 @@ func (r *transferRules) setDefaultRules(sourceMailboxes []Mailbox, targetMailbox
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, rule := range r.rules {
|
// There is no point showing rule which has no action (i.e., source mailbox
|
||||||
if !rule.Active {
|
// is not available).
|
||||||
continue
|
// A good reason to keep all rules and only deactivate them would be for
|
||||||
}
|
// multiple imports from different sources with the same or similar enough
|
||||||
|
// mailbox setup to reuse configuration. That is very minor feature which
|
||||||
|
// can be implemented in more reasonable way by allowing users to save and
|
||||||
|
// load configurations.
|
||||||
|
for key, rule := range r.rules {
|
||||||
found := false
|
found := false
|
||||||
for _, sourceMailbox := range sourceMailboxes {
|
for _, sourceMailbox := range sourceMailboxes {
|
||||||
if sourceMailbox.Name == rule.SourceMailbox.Name {
|
if sourceMailbox.Name == rule.SourceMailbox.Name {
|
||||||
@ -158,7 +177,7 @@ func (r *transferRules) setDefaultRules(sourceMailboxes []Mailbox, targetMailbox
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !found {
|
if !found {
|
||||||
rule.Active = false
|
delete(r.rules, key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,6 +235,7 @@ func (r *transferRules) getRules() []*Rule {
|
|||||||
for _, rule := range r.rules {
|
for _, rule := range r.rules {
|
||||||
rules = append(rules, rule)
|
rules = append(rules, rule)
|
||||||
}
|
}
|
||||||
|
sort.Sort(byRuleOrder(rules))
|
||||||
return rules
|
return rules
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -288,3 +308,59 @@ func (r *Rule) TargetMailboxNames() (names []string) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// byRuleOrder implements sort.Interface. Sort order:
|
||||||
|
// * System folders first (as defined in getSystemMailboxes).
|
||||||
|
// * Custom folders by name.
|
||||||
|
// * Custom labels by name.
|
||||||
|
type byRuleOrder []*Rule
|
||||||
|
|
||||||
|
func (a byRuleOrder) Len() int {
|
||||||
|
return len(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a byRuleOrder) Swap(i, j int) {
|
||||||
|
a[i], a[j] = a[j], a[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a byRuleOrder) Less(i, j int) bool {
|
||||||
|
if a[i].SourceMailbox.IsExclusive && !a[j].SourceMailbox.IsExclusive {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if !a[i].SourceMailbox.IsExclusive && a[j].SourceMailbox.IsExclusive {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
iSystemIndex := -1
|
||||||
|
jSystemIndex := -1
|
||||||
|
for index, systemFolders := range getSystemMailboxes(true) {
|
||||||
|
if a[i].SourceMailbox.Name == systemFolders.Name {
|
||||||
|
iSystemIndex = index
|
||||||
|
}
|
||||||
|
if a[j].SourceMailbox.Name == systemFolders.Name {
|
||||||
|
jSystemIndex = index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if iSystemIndex != -1 && jSystemIndex == -1 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if iSystemIndex == -1 && jSystemIndex != -1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if iSystemIndex != -1 && jSystemIndex != -1 {
|
||||||
|
return iSystemIndex < jSystemIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
return a[i].SourceMailbox.Name < a[j].SourceMailbox.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// containsExclusive returns true if there is at least one exclusive mailbox.
|
||||||
|
func containsExclusive(mailboxes []Mailbox) bool {
|
||||||
|
for _, m := range mailboxes {
|
||||||
|
if m.IsExclusive {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|||||||
@ -86,6 +86,7 @@ func TestSetGlobalTimeLimit(t *testing.T) {
|
|||||||
r.NoError(t, rules.setRule(mailboxB, []Mailbox{}, 0, 0))
|
r.NoError(t, rules.setRule(mailboxB, []Mailbox{}, 0, 0))
|
||||||
|
|
||||||
rules.setGlobalTimeLimit(30, 40)
|
rules.setGlobalTimeLimit(30, 40)
|
||||||
|
rules.propagateGlobalTime()
|
||||||
|
|
||||||
r.Equal(t, map[string]*Rule{
|
r.Equal(t, map[string]*Rule{
|
||||||
mailboxA.Hash(): {Active: true, SourceMailbox: mailboxA, TargetMailboxes: []Mailbox{}, FromTime: 10, ToTime: 20},
|
mailboxA.Hash(): {Active: true, SourceMailbox: mailboxA, TargetMailboxes: []Mailbox{}, FromTime: 10, ToTime: 20},
|
||||||
@ -154,7 +155,6 @@ func TestSetDefaultRulesDeactivateMissing(t *testing.T) {
|
|||||||
|
|
||||||
r.Equal(t, map[string]*Rule{
|
r.Equal(t, map[string]*Rule{
|
||||||
mailboxA.Hash(): {Active: true, SourceMailbox: mailboxA, TargetMailboxes: []Mailbox{mailboxB}, FromTime: 0, ToTime: 0},
|
mailboxA.Hash(): {Active: true, SourceMailbox: mailboxA, TargetMailboxes: []Mailbox{mailboxB}, FromTime: 0, ToTime: 0},
|
||||||
mailboxB.Hash(): {Active: false, SourceMailbox: mailboxB, TargetMailboxes: []Mailbox{mailboxB}, FromTime: 0, ToTime: 0},
|
|
||||||
}, rules.rules)
|
}, rules.rules)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -208,3 +208,40 @@ func generateTimeRule(from, to int64) Rule {
|
|||||||
ToTime: to,
|
ToTime: to,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestOrderRules(t *testing.T) {
|
||||||
|
wantMailboxOrder := []Mailbox{
|
||||||
|
{Name: "Inbox", IsExclusive: true},
|
||||||
|
{Name: "Drafts", IsExclusive: true},
|
||||||
|
{Name: "Sent", IsExclusive: true},
|
||||||
|
{Name: "Starred", IsExclusive: true},
|
||||||
|
{Name: "Archive", IsExclusive: true},
|
||||||
|
{Name: "Spam", IsExclusive: true},
|
||||||
|
{Name: "All Mail", IsExclusive: true},
|
||||||
|
{Name: "Folder A", IsExclusive: true},
|
||||||
|
{Name: "Folder B", IsExclusive: true},
|
||||||
|
{Name: "Folder C", IsExclusive: true},
|
||||||
|
{Name: "Label A", IsExclusive: false},
|
||||||
|
{Name: "Label B", IsExclusive: false},
|
||||||
|
{Name: "Label C", IsExclusive: false},
|
||||||
|
}
|
||||||
|
wantMailboxNames := []string{}
|
||||||
|
|
||||||
|
rules := map[string]*Rule{}
|
||||||
|
for _, mailbox := range wantMailboxOrder {
|
||||||
|
wantMailboxNames = append(wantMailboxNames, mailbox.Name)
|
||||||
|
rules[mailbox.Hash()] = &Rule{
|
||||||
|
SourceMailbox: mailbox,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
transferRules := transferRules{
|
||||||
|
rules: rules,
|
||||||
|
}
|
||||||
|
|
||||||
|
gotMailboxNames := []string{}
|
||||||
|
for _, rule := range transferRules.getRules() {
|
||||||
|
gotMailboxNames = append(gotMailboxNames, rule.SourceMailbox.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Equal(t, wantMailboxNames, gotMailboxNames)
|
||||||
|
}
|
||||||
|
|||||||
@ -31,12 +31,14 @@ var log = logrus.WithField("pkg", "transfer") //nolint[gochecknoglobals]
|
|||||||
// Transfer is facade on top of import rules, progress manager and source
|
// Transfer is facade on top of import rules, progress manager and source
|
||||||
// and target providers. This is the main object which should be used.
|
// and target providers. This is the main object which should be used.
|
||||||
type Transfer struct {
|
type Transfer struct {
|
||||||
panicHandler PanicHandler
|
panicHandler PanicHandler
|
||||||
id string
|
id string
|
||||||
dir string
|
dir string
|
||||||
rules transferRules
|
rules transferRules
|
||||||
source SourceProvider
|
source SourceProvider
|
||||||
target TargetProvider
|
target TargetProvider
|
||||||
|
sourceMboxCache []Mailbox
|
||||||
|
targetMboxCache []Mailbox
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates Transfer for specific source and target. Usage:
|
// New creates Transfer for specific source and target. Usage:
|
||||||
@ -127,23 +129,33 @@ func (t *Transfer) GetRules() []*Rule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SourceMailboxes returns mailboxes available at source side.
|
// SourceMailboxes returns mailboxes available at source side.
|
||||||
func (t *Transfer) SourceMailboxes() ([]Mailbox, error) {
|
func (t *Transfer) SourceMailboxes() (m []Mailbox, err error) {
|
||||||
return t.source.Mailboxes(false, true)
|
if t.sourceMboxCache == nil {
|
||||||
|
t.sourceMboxCache, err = t.source.Mailboxes(false, true)
|
||||||
|
}
|
||||||
|
return t.sourceMboxCache, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TargetMailboxes returns mailboxes available at target side.
|
// TargetMailboxes returns mailboxes available at target side.
|
||||||
func (t *Transfer) TargetMailboxes() ([]Mailbox, error) {
|
func (t *Transfer) TargetMailboxes() (m []Mailbox, err error) {
|
||||||
return t.target.Mailboxes(true, false)
|
if t.targetMboxCache == nil {
|
||||||
|
t.targetMboxCache, err = t.target.Mailboxes(true, false)
|
||||||
|
}
|
||||||
|
return t.targetMboxCache, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateTargetMailbox creates mailbox in target provider.
|
// CreateTargetMailbox creates mailbox in target provider.
|
||||||
func (t *Transfer) CreateTargetMailbox(mailbox Mailbox) (Mailbox, error) {
|
func (t *Transfer) CreateTargetMailbox(mailbox Mailbox) (Mailbox, error) {
|
||||||
|
t.targetMboxCache = nil
|
||||||
|
|
||||||
return t.target.CreateMailbox(mailbox)
|
return t.target.CreateMailbox(mailbox)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChangeTarget changes the target. It is safe to change target for export,
|
// ChangeTarget changes the target. It is safe to change target for export,
|
||||||
// must not be changed for import. Do not set after you started transfer.
|
// must not be changed for import. Do not set after you started transfer.
|
||||||
func (t *Transfer) ChangeTarget(target TargetProvider) {
|
func (t *Transfer) ChangeTarget(target TargetProvider) {
|
||||||
|
t.targetMboxCache = nil
|
||||||
|
|
||||||
t.target = target
|
t.target = target
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,6 +163,7 @@ func (t *Transfer) ChangeTarget(target TargetProvider) {
|
|||||||
func (t *Transfer) Start() *Progress {
|
func (t *Transfer) Start() *Progress {
|
||||||
log.Debug("Transfer started")
|
log.Debug("Transfer started")
|
||||||
t.rules.save()
|
t.rules.save()
|
||||||
|
t.rules.propagateGlobalTime()
|
||||||
|
|
||||||
log := log.WithField("id", t.id)
|
log := log.WithField("id", t.id)
|
||||||
reportFile := newFileReport(t.dir, t.id)
|
reportFile := newFileReport(t.dir, t.id)
|
||||||
|
|||||||
@ -5,12 +5,11 @@
|
|||||||
package mocks
|
package mocks
|
||||||
|
|
||||||
import (
|
import (
|
||||||
reflect "reflect"
|
|
||||||
|
|
||||||
store "github.com/ProtonMail/proton-bridge/internal/store"
|
store "github.com/ProtonMail/proton-bridge/internal/store"
|
||||||
credentials "github.com/ProtonMail/proton-bridge/internal/users/credentials"
|
credentials "github.com/ProtonMail/proton-bridge/internal/users/credentials"
|
||||||
pmapi "github.com/ProtonMail/proton-bridge/pkg/pmapi"
|
pmapi "github.com/ProtonMail/proton-bridge/pkg/pmapi"
|
||||||
gomock "github.com/golang/mock/gomock"
|
gomock "github.com/golang/mock/gomock"
|
||||||
|
reflect "reflect"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MockConfiger is a mock of Configer interface
|
// MockConfiger is a mock of Configer interface
|
||||||
|
|||||||
@ -173,26 +173,6 @@ func (c *client) Report(rep ReportReq) (err error) {
|
|||||||
return res.Err()
|
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.
|
// ReportCrash is old. Use sentry instead.
|
||||||
func (c *client) ReportCrash(stacktrace string) (err error) {
|
func (c *client) ReportCrash(stacktrace string) (err error) {
|
||||||
crashReq := ReportReq{
|
crashReq := ReportReq{
|
||||||
|
|||||||
@ -27,19 +27,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
var testBugsReportReq = ReportReq{
|
var testBugReportReq = ReportReq{
|
||||||
OS: "Mac OSX",
|
|
||||||
OSVersion: "10.11.6",
|
|
||||||
Client: "demoapp",
|
|
||||||
ClientVersion: "GoPMAPI_1.0.14",
|
|
||||||
ClientType: 1,
|
|
||||||
Title: "Big Bug",
|
|
||||||
Description: "Cannot fetch new messages",
|
|
||||||
Username: "apple",
|
|
||||||
Email: "apple@gmail.com",
|
|
||||||
}
|
|
||||||
|
|
||||||
var testBugsReportReqWithEmailClient = ReportReq{
|
|
||||||
OS: "Mac OSX",
|
OS: "Mac OSX",
|
||||||
OSVersion: "10.11.6",
|
OSVersion: "10.11.6",
|
||||||
Browser: "AppleMail",
|
Browser: "AppleMail",
|
||||||
@ -67,31 +55,6 @@ const testBugsBody = `{
|
|||||||
|
|
||||||
const testAttachmentJSONZipped = "PK\x03\x04\x14\x00\b\x00\b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\x00\x00\x00last.log\\Rَ\xaaH\x00}ﯨ\xf8r\x1f\xeeܖED;\xe9\ap\x03\x11\x11\x97\x0e8\x99L\xb0(\xa1\xa0\x16\x85b\x91I\xff\xfbD{\x99\xc9}\xab:K\x9d\xa4\xce\xf9\xe7\t\x00\x00z\xf6\xb4\xf7\x02z\xb7a\xe5\xd8\x04*V̭\x8d\xd1lvE}\xd6\xe3\x80\x1f\xd7nX\x9bI[\xa6\xe1a=\xd4a\xa8M\x97\xd9J\xf1F\xeb\x105U\xbd\xb0`XO\xce\xf1hu\x99q\xc3\xfe{\x11ߨ'-\v\x89Z\xa4\x9c5\xaf\xaf\xbd?>R\xd6\x11E\xf7\x1cX\xf0JpF#L\x9eE+\xbe\xe8\x1d\xee\ued2e\u007f\xde]\u06dd\xedo\x97\x87E\xa0V\xf4/$\xc2\xecK\xed\xa0\xdb&\x829\x12\xe5\x9do\xa0\xe9\x1a\xd2\x19\x1e\xf5`\x95гb\xf8\x89\x81\xb7\xa5G\x18\x95\xf3\x9d9\xe8\x93B\x17!\x1a^\xccr\xbb`\xb2\xb4\xb86\x87\xb4h\x0e\xda\xc6u<+\x9e$̓\x95\xccSo\xea\xa4\xdbH!\xe9g\x8b\xd4\b\xb3hܬ\xa6Wk\x14He\xae\x8aPU\xaa\xc1\xee$\xfbH\xb3\xab.I\f<\x89\x06q\xe3-3-\x99\xcdݽ\xe5v\x99\xedn\xac\xadn\xe8Rp=\xb4nJ\xed\xd5\r\x8d\xde\x06Ζ\xf6\xb3\x01\x94\xcb\xf6\xd4\x19r\xe1\xaa$4+\xeaW\xa6F\xfa0\x97\x9cD\f\x8e\xd7\xd6z\v,G\xf3e2\xd4\xe6V\xba\v\xb6\xd9\xe8\xca*\x16\x95V\xa4J\xfbp\xddmF\x8c\x9a\xc6\xc8Č-\xdb\v\xf6\xf5\xf9\x02*\x15e\x874\xc9\xe7\"\xa3\x1an\xabq}ˊq\x957\xd3\xfd\xa91\x82\xe0Lß\\\x17\x8e\x9e_\xed`\t\xe9~5̕\x03\x9a\f\xddN6\xa2\xc4\x17\xdb\xc9V\x1c~\x9e\xea\xbe\xda-xv\xed\x8b\xe2\xc8DŽS\x95E6\xf2\xc3H\x1d:HPx\xc9\x14\xbfɒ\xff\xea\xb4P\x14\xa3\xe2\xfe\xfd\x1f+z\x80\x903\x81\x98\xf8\x15\xa3\x12\x16\xf8\"0g\xf7~B^\xfd \x040T\xa3\x02\x9c\x10\xc1\xa8F\xa0I#\xf1\xa3\x04\x98\x01\x91\xe2\x12\xdc;\x06gL\xd0g\xc0\xe3\xbd\xf6\xd7}&\xa8轀?\xbfяy`X\xf0\x92\x9f\x05\xf0*A8ρ\xac=K\xff\xf3\xfe\xa6Z\xe1\x1a\x017\xc2\x04\f\x94g\xa9\xf7-\xfb\xebqz\u007fz\u007f\xfa7\x00\x00\xff\xffPK\a\b\xf5\\\v\xe5I\x02\x00\x00\r\x03\x00\x00PK\x01\x02\x14\x00\x14\x00\b\x00\b\x00\x00\x00\x00\x00\xf5\\\v\xe5I\x02\x00\x00\r\x03\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00last.logPK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x006\x00\x00\x00\u007f\x02\x00\x00\x00\x00" //nolint[misspell]
|
const testAttachmentJSONZipped = "PK\x03\x04\x14\x00\b\x00\b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\x00\x00\x00last.log\\Rَ\xaaH\x00}ﯨ\xf8r\x1f\xeeܖED;\xe9\ap\x03\x11\x11\x97\x0e8\x99L\xb0(\xa1\xa0\x16\x85b\x91I\xff\xfbD{\x99\xc9}\xab:K\x9d\xa4\xce\xf9\xe7\t\x00\x00z\xf6\xb4\xf7\x02z\xb7a\xe5\xd8\x04*V̭\x8d\xd1lvE}\xd6\xe3\x80\x1f\xd7nX\x9bI[\xa6\xe1a=\xd4a\xa8M\x97\xd9J\xf1F\xeb\x105U\xbd\xb0`XO\xce\xf1hu\x99q\xc3\xfe{\x11ߨ'-\v\x89Z\xa4\x9c5\xaf\xaf\xbd?>R\xd6\x11E\xf7\x1cX\xf0JpF#L\x9eE+\xbe\xe8\x1d\xee\ued2e\u007f\xde]\u06dd\xedo\x97\x87E\xa0V\xf4/$\xc2\xecK\xed\xa0\xdb&\x829\x12\xe5\x9do\xa0\xe9\x1a\xd2\x19\x1e\xf5`\x95гb\xf8\x89\x81\xb7\xa5G\x18\x95\xf3\x9d9\xe8\x93B\x17!\x1a^\xccr\xbb`\xb2\xb4\xb86\x87\xb4h\x0e\xda\xc6u<+\x9e$̓\x95\xccSo\xea\xa4\xdbH!\xe9g\x8b\xd4\b\xb3hܬ\xa6Wk\x14He\xae\x8aPU\xaa\xc1\xee$\xfbH\xb3\xab.I\f<\x89\x06q\xe3-3-\x99\xcdݽ\xe5v\x99\xedn\xac\xadn\xe8Rp=\xb4nJ\xed\xd5\r\x8d\xde\x06Ζ\xf6\xb3\x01\x94\xcb\xf6\xd4\x19r\xe1\xaa$4+\xeaW\xa6F\xfa0\x97\x9cD\f\x8e\xd7\xd6z\v,G\xf3e2\xd4\xe6V\xba\v\xb6\xd9\xe8\xca*\x16\x95V\xa4J\xfbp\xddmF\x8c\x9a\xc6\xc8Č-\xdb\v\xf6\xf5\xf9\x02*\x15e\x874\xc9\xe7\"\xa3\x1an\xabq}ˊq\x957\xd3\xfd\xa91\x82\xe0Lß\\\x17\x8e\x9e_\xed`\t\xe9~5̕\x03\x9a\f\xddN6\xa2\xc4\x17\xdb\xc9V\x1c~\x9e\xea\xbe\xda-xv\xed\x8b\xe2\xc8DŽS\x95E6\xf2\xc3H\x1d:HPx\xc9\x14\xbfɒ\xff\xea\xb4P\x14\xa3\xe2\xfe\xfd\x1f+z\x80\x903\x81\x98\xf8\x15\xa3\x12\x16\xf8\"0g\xf7~B^\xfd \x040T\xa3\x02\x9c\x10\xc1\xa8F\xa0I#\xf1\xa3\x04\x98\x01\x91\xe2\x12\xdc;\x06gL\xd0g\xc0\xe3\xbd\xf6\xd7}&\xa8轀?\xbfяy`X\xf0\x92\x9f\x05\xf0*A8ρ\xac=K\xff\xf3\xfe\xa6Z\xe1\x1a\x017\xc2\x04\f\x94g\xa9\xf7-\xfb\xebqz\u007fz\u007f\xfa7\x00\x00\xff\xffPK\a\b\xf5\\\v\xe5I\x02\x00\x00\r\x03\x00\x00PK\x01\x02\x14\x00\x14\x00\b\x00\b\x00\x00\x00\x00\x00\xf5\\\v\xe5I\x02\x00\x00\r\x03\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00last.logPK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x006\x00\x00\x00\u007f\x02\x00\x00\x00\x00" //nolint[misspell]
|
||||||
|
|
||||||
func TestClient_BugReport(t *testing.T) {
|
|
||||||
s, c := newTestServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
Ok(t, checkMethodAndPath(r, "POST", "/reports/bug"))
|
|
||||||
Ok(t, isAuthReq(r, testUID, testAccessToken))
|
|
||||||
|
|
||||||
var bugsReportReq ReportReq
|
|
||||||
Ok(t, json.NewDecoder(r.Body).Decode(&bugsReportReq))
|
|
||||||
Equals(t, testBugsReportReq, bugsReportReq)
|
|
||||||
|
|
||||||
fmt.Fprint(w, testBugsBody)
|
|
||||||
}))
|
|
||||||
defer s.Close()
|
|
||||||
c.uid = testUID
|
|
||||||
c.accessToken = testAccessToken
|
|
||||||
|
|
||||||
Ok(t, c.ReportBug(
|
|
||||||
testBugsReportReq.OS,
|
|
||||||
testBugsReportReq.OSVersion,
|
|
||||||
testBugsReportReq.Title,
|
|
||||||
testBugsReportReq.Description,
|
|
||||||
testBugsReportReq.Username,
|
|
||||||
testBugsReportReq.Email,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestClient_BugReportWithAttachment(t *testing.T) {
|
func TestClient_BugReportWithAttachment(t *testing.T) {
|
||||||
s, c := newTestServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
s, c := newTestServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
Ok(t, checkMethodAndPath(r, "POST", "/reports/bug"))
|
Ok(t, checkMethodAndPath(r, "POST", "/reports/bug"))
|
||||||
@ -100,15 +63,15 @@ func TestClient_BugReportWithAttachment(t *testing.T) {
|
|||||||
Ok(t, r.ParseMultipartForm(10*1024))
|
Ok(t, r.ParseMultipartForm(10*1024))
|
||||||
|
|
||||||
for field, expected := range map[string]string{
|
for field, expected := range map[string]string{
|
||||||
"OS": testBugsReportReq.OS,
|
"OS": testBugReportReq.OS,
|
||||||
"OSVersion": testBugsReportReq.OSVersion,
|
"OSVersion": testBugReportReq.OSVersion,
|
||||||
"Client": testBugsReportReq.Client,
|
"Client": testBugReportReq.Client,
|
||||||
"ClientVersion": testBugsReportReq.ClientVersion,
|
"ClientVersion": testBugReportReq.ClientVersion,
|
||||||
"ClientType": fmt.Sprintf("%d", testBugsReportReq.ClientType),
|
"ClientType": fmt.Sprintf("%d", testBugReportReq.ClientType),
|
||||||
"Title": testBugsReportReq.Title,
|
"Title": testBugReportReq.Title,
|
||||||
"Description": testBugsReportReq.Description,
|
"Description": testBugReportReq.Description,
|
||||||
"Username": testBugsReportReq.Username,
|
"Username": testBugReportReq.Username,
|
||||||
"Email": testBugsReportReq.Email,
|
"Email": testBugReportReq.Email,
|
||||||
} {
|
} {
|
||||||
if r.PostFormValue(field) != expected {
|
if r.PostFormValue(field) != expected {
|
||||||
t.Errorf("Field %q has %q but expected %q", field, r.PostFormValue(field), expected)
|
t.Errorf("Field %q has %q but expected %q", field, r.PostFormValue(field), expected)
|
||||||
@ -129,20 +92,20 @@ func TestClient_BugReportWithAttachment(t *testing.T) {
|
|||||||
c.uid = testUID
|
c.uid = testUID
|
||||||
c.accessToken = testAccessToken
|
c.accessToken = testAccessToken
|
||||||
|
|
||||||
rep := testBugsReportReq
|
rep := testBugReportReq
|
||||||
rep.AddAttachment("log", "last.log", strings.NewReader(testAttachmentJSON))
|
rep.AddAttachment("log", "last.log", strings.NewReader(testAttachmentJSON))
|
||||||
|
|
||||||
Ok(t, c.Report(rep))
|
Ok(t, c.Report(rep))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestClient_BugReportWithEmailClient(t *testing.T) {
|
func TestClient_BugReport(t *testing.T) {
|
||||||
s, c := newTestServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
s, c := newTestServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
Ok(t, checkMethodAndPath(r, "POST", "/reports/bug"))
|
Ok(t, checkMethodAndPath(r, "POST", "/reports/bug"))
|
||||||
Ok(t, isAuthReq(r, testUID, testAccessToken))
|
Ok(t, isAuthReq(r, testUID, testAccessToken))
|
||||||
|
|
||||||
var bugsReportReq ReportReq
|
var bugsReportReq ReportReq
|
||||||
Ok(t, json.NewDecoder(r.Body).Decode(&bugsReportReq))
|
Ok(t, json.NewDecoder(r.Body).Decode(&bugsReportReq))
|
||||||
Equals(t, testBugsReportReqWithEmailClient, bugsReportReq)
|
Equals(t, testBugReportReq, bugsReportReq)
|
||||||
|
|
||||||
fmt.Fprint(w, testBugsBody)
|
fmt.Fprint(w, testBugsBody)
|
||||||
}))
|
}))
|
||||||
@ -150,15 +113,17 @@ func TestClient_BugReportWithEmailClient(t *testing.T) {
|
|||||||
c.uid = testUID
|
c.uid = testUID
|
||||||
c.accessToken = testAccessToken
|
c.accessToken = testAccessToken
|
||||||
|
|
||||||
Ok(t, c.ReportBugWithEmailClient(
|
r := ReportReq{
|
||||||
testBugsReportReqWithEmailClient.OS,
|
OS: testBugReportReq.OS,
|
||||||
testBugsReportReqWithEmailClient.OSVersion,
|
OSVersion: testBugReportReq.OSVersion,
|
||||||
testBugsReportReqWithEmailClient.Title,
|
Browser: testBugReportReq.Browser,
|
||||||
testBugsReportReqWithEmailClient.Description,
|
Title: testBugReportReq.Title,
|
||||||
testBugsReportReqWithEmailClient.Username,
|
Description: testBugReportReq.Description,
|
||||||
testBugsReportReqWithEmailClient.Email,
|
Username: testBugReportReq.Username,
|
||||||
testBugsReportReqWithEmailClient.Browser,
|
Email: testBugReportReq.Email,
|
||||||
))
|
}
|
||||||
|
|
||||||
|
Ok(t, c.Report(r))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestClient_BugsCrash(t *testing.T) {
|
func TestClient_BugsCrash(t *testing.T) {
|
||||||
|
|||||||
@ -67,7 +67,7 @@ type Client interface {
|
|||||||
DeleteLabel(labelID string) error
|
DeleteLabel(labelID string) error
|
||||||
EmptyFolder(labelID string, addressID string) error
|
EmptyFolder(labelID string, addressID string) error
|
||||||
|
|
||||||
ReportBugWithEmailClient(os, osVersion, title, description, username, email, emailClient string) error
|
Report(report ReportReq) error
|
||||||
SendSimpleMetric(category, action, label string) error
|
SendSimpleMetric(category, action, label string) error
|
||||||
|
|
||||||
GetMailSettings() (MailSettings, error)
|
GetMailSettings() (MailSettings, error)
|
||||||
|
|||||||
@ -5,12 +5,11 @@
|
|||||||
package mocks
|
package mocks
|
||||||
|
|
||||||
import (
|
import (
|
||||||
io "io"
|
|
||||||
reflect "reflect"
|
|
||||||
|
|
||||||
crypto "github.com/ProtonMail/gopenpgp/v2/crypto"
|
crypto "github.com/ProtonMail/gopenpgp/v2/crypto"
|
||||||
pmapi "github.com/ProtonMail/proton-bridge/pkg/pmapi"
|
pmapi "github.com/ProtonMail/proton-bridge/pkg/pmapi"
|
||||||
gomock "github.com/golang/mock/gomock"
|
gomock "github.com/golang/mock/gomock"
|
||||||
|
io "io"
|
||||||
|
reflect "reflect"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MockClient is a mock of Client interface
|
// MockClient is a mock of Client interface
|
||||||
@ -601,18 +600,18 @@ func (mr *MockClientMockRecorder) ReorderAddresses(arg0 interface{}) *gomock.Cal
|
|||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReorderAddresses", reflect.TypeOf((*MockClient)(nil).ReorderAddresses), arg0)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReorderAddresses", reflect.TypeOf((*MockClient)(nil).ReorderAddresses), arg0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReportBugWithEmailClient mocks base method
|
// Report mocks base method
|
||||||
func (m *MockClient) ReportBugWithEmailClient(arg0, arg1, arg2, arg3, arg4, arg5, arg6 string) error {
|
func (m *MockClient) Report(arg0 pmapi.ReportReq) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "ReportBugWithEmailClient", arg0, arg1, arg2, arg3, arg4, arg5, arg6)
|
ret := m.ctrl.Call(m, "Report", arg0)
|
||||||
ret0, _ := ret[0].(error)
|
ret0, _ := ret[0].(error)
|
||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReportBugWithEmailClient indicates an expected call of ReportBugWithEmailClient
|
// Report indicates an expected call of Report
|
||||||
func (mr *MockClientMockRecorder) ReportBugWithEmailClient(arg0, arg1, arg2, arg3, arg4, arg5, arg6 interface{}) *gomock.Call {
|
func (mr *MockClientMockRecorder) Report(arg0 interface{}) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReportBugWithEmailClient", reflect.TypeOf((*MockClient)(nil).ReportBugWithEmailClient), arg0, arg1, arg2, arg3, arg4, arg5, arg6)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Report", reflect.TypeOf((*MockClient)(nil).Report), arg0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendMessage mocks base method
|
// SendMessage mocks base method
|
||||||
|
|||||||
@ -23,16 +23,8 @@ import (
|
|||||||
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
|
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (api *FakePMAPI) ReportBugWithEmailClient(os, osVersion, title, description, username, email, emailClient string) error {
|
func (api *FakePMAPI) Report(report pmapi.ReportReq) error {
|
||||||
return api.checkInternetAndRecordCall(POST, "/reports/bug", &pmapi.ReportReq{
|
return api.checkInternetAndRecordCall(POST, "/reports/bug", report)
|
||||||
OS: os,
|
|
||||||
OSVersion: osVersion,
|
|
||||||
Title: title,
|
|
||||||
Description: description,
|
|
||||||
Username: username,
|
|
||||||
Email: email,
|
|
||||||
Browser: emailClient,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *FakePMAPI) SendSimpleMetric(category, action, label string) error {
|
func (api *FakePMAPI) SendSimpleMetric(category, action, label string) error {
|
||||||
|
|||||||
Reference in New Issue
Block a user