1package chat
2
3import (
4	"errors"
5	"fmt"
6	"net"
7	"time"
8
9	"github.com/keybase/client/go/chat/types"
10	"github.com/keybase/client/go/ephemeral"
11	"github.com/keybase/client/go/libkb"
12	"github.com/keybase/client/go/protocol/chat1"
13	"github.com/keybase/client/go/protocol/gregor1"
14	"github.com/keybase/client/go/protocol/keybase1"
15	"github.com/keybase/client/go/teams"
16	"github.com/keybase/go-framed-msgpack-rpc/rpc"
17	"golang.org/x/net/context"
18)
19
20var ErrChatServerTimeout = errors.New("timeout calling chat server")
21var ErrDuplicateConnection = errors.New("error calling chat server")
22var ErrKeyServerTimeout = errors.New("timeout calling into key server")
23
24func NewPermanentUnboxingError(inner error) types.UnboxingError {
25	return PermanentUnboxingError{inner}
26}
27
28type PermanentUnboxingError struct{ inner error }
29
30func (e PermanentUnboxingError) Error() string {
31	switch err := e.inner.(type) {
32	case EphemeralUnboxingError, NotAuthenticatedForThisDeviceError:
33		return err.Error()
34	default:
35		return fmt.Sprintf("Unable to decrypt chat message: %s", err.Error())
36	}
37}
38
39func (e PermanentUnboxingError) IsPermanent() bool { return true }
40
41func (e PermanentUnboxingError) Inner() error { return e.inner }
42
43func (e PermanentUnboxingError) ExportType() chat1.MessageUnboxedErrorType {
44	switch err := e.inner.(type) {
45	case VersionError:
46		return err.ExportType()
47	case EphemeralUnboxingError:
48		return chat1.MessageUnboxedErrorType_EPHEMERAL
49	case NotAuthenticatedForThisDeviceError, InvalidMACError:
50		return chat1.MessageUnboxedErrorType_PAIRWISE_MISSING
51	default:
52		return chat1.MessageUnboxedErrorType_MISC
53	}
54}
55
56func (e PermanentUnboxingError) VersionKind() chat1.VersionKind {
57	switch err := e.inner.(type) {
58	case VersionError:
59		return err.VersionKind()
60	default:
61		return ""
62	}
63}
64
65func (e PermanentUnboxingError) VersionNumber() int {
66	switch err := e.inner.(type) {
67	case VersionError:
68		return err.VersionNumber()
69	default:
70		return 0
71	}
72}
73
74func (e PermanentUnboxingError) IsCritical() bool {
75	switch err := e.inner.(type) {
76	case VersionError:
77		return err.IsCritical()
78	default:
79		return false
80	}
81}
82
83func (e PermanentUnboxingError) InternalError() string {
84	switch err := e.Inner().(type) {
85	case types.InternalError:
86		return err.InternalError()
87	default:
88		return err.Error()
89	}
90}
91
92func (e PermanentUnboxingError) ToStatus() (status keybase1.Status) {
93	if ee, ok := e.inner.(libkb.ExportableError); ok {
94		status = ee.ToStatus()
95		status.Desc = e.Error()
96	} else {
97		status = keybase1.Status{
98			Name: "GENERIC",
99			Code: libkb.SCGeneric,
100			Desc: e.Error(),
101		}
102	}
103	return status
104}
105
106//=============================================================================
107
108func NewTransientUnboxingError(inner error) types.UnboxingError {
109	return TransientUnboxingError{inner}
110}
111
112type TransientUnboxingError struct{ inner error }
113
114func (e TransientUnboxingError) Error() string {
115	return fmt.Sprintf("Unable to decrypt chat message (transient): %s", e.inner.Error())
116}
117
118func (e TransientUnboxingError) IsPermanent() bool { return false }
119
120func (e TransientUnboxingError) Inner() error { return e.inner }
121
122func (e TransientUnboxingError) ExportType() chat1.MessageUnboxedErrorType {
123	return chat1.MessageUnboxedErrorType_MISC
124}
125
126func (e TransientUnboxingError) VersionKind() chat1.VersionKind {
127	return ""
128}
129
130func (e TransientUnboxingError) VersionNumber() int {
131	return 0
132}
133
134func (e TransientUnboxingError) IsCritical() bool {
135	return false
136}
137
138func (e TransientUnboxingError) InternalError() string {
139	switch err := e.Inner().(type) {
140	case types.InternalError:
141		return err.InternalError()
142	default:
143		return err.Error()
144	}
145}
146
147func (e TransientUnboxingError) ToStatus() (status keybase1.Status) {
148	if ee, ok := e.inner.(libkb.ExportableError); ok {
149		status = ee.ToStatus()
150		status.Desc = e.Error()
151	} else {
152		status = keybase1.Status{
153			Name: "GENERIC",
154			Code: libkb.SCGeneric,
155			Desc: e.Error(),
156		}
157	}
158	return status
159}
160
161//=============================================================================
162
163type EphemeralAlreadyExpiredError struct{}
164
165func NewEphemeralAlreadyExpiredError() EphemeralAlreadyExpiredError {
166	return EphemeralAlreadyExpiredError{}
167}
168
169func (e EphemeralAlreadyExpiredError) Error() string {
170	return "Exploding message is expired"
171}
172
173func (e EphemeralAlreadyExpiredError) InternalError() string {
174	return e.Error()
175}
176
177//=============================================================================
178
179type EphemeralUnboxingError struct {
180	inner ephemeral.EphemeralKeyError
181}
182
183func NewEphemeralUnboxingError(inner ephemeral.EphemeralKeyError) EphemeralUnboxingError {
184	return EphemeralUnboxingError{inner}
185}
186
187func (e EphemeralUnboxingError) Error() string {
188	return e.inner.HumanError()
189}
190
191func (e EphemeralUnboxingError) InternalError() string {
192	return e.inner.Error()
193}
194
195//=============================================================================
196
197type PublicTeamEphemeralKeyError struct{}
198
199func NewPublicTeamEphemeralKeyError() PublicTeamEphemeralKeyError {
200	return PublicTeamEphemeralKeyError{}
201}
202
203func (e PublicTeamEphemeralKeyError) Error() string {
204	return "Cannot use exploding messages for a public team."
205}
206
207//=============================================================================
208
209type NotAuthenticatedForThisDeviceError struct{ inner ephemeral.EphemeralKeyError }
210
211func NewNotAuthenticatedForThisDeviceError(mctx libkb.MetaContext, memberCtime *keybase1.Time,
212	contentCtime gregor1.Time) NotAuthenticatedForThisDeviceError {
213	inner := ephemeral.NewNotAuthenticatedForThisDeviceError(mctx, memberCtime, contentCtime)
214	return NotAuthenticatedForThisDeviceError{inner: inner}
215}
216
217func (e NotAuthenticatedForThisDeviceError) Error() string {
218	return e.inner.HumanError()
219}
220
221func (e NotAuthenticatedForThisDeviceError) InternalError() string {
222	return e.inner.Error()
223}
224
225//=============================================================================
226
227type InvalidMACError struct{}
228
229func NewInvalidMACError() InvalidMACError {
230	return InvalidMACError{}
231}
232
233func (e InvalidMACError) Error() string {
234	return "invalid MAC"
235}
236
237//=============================================================================
238
239type ConsistencyErrorCode int
240
241const (
242	DuplicateID ConsistencyErrorCode = iota
243	OutOfOrderID
244	InconsistentHash
245	IncorrectHash
246)
247
248type ChatThreadConsistencyError interface {
249	error
250	Code() ConsistencyErrorCode
251}
252
253type chatThreadConsistencyErrorImpl struct {
254	msg  string
255	code ConsistencyErrorCode
256}
257
258func (e chatThreadConsistencyErrorImpl) Error() string {
259	return e.msg
260}
261
262func (e chatThreadConsistencyErrorImpl) Code() ConsistencyErrorCode {
263	return e.code
264}
265
266func NewChatThreadConsistencyError(code ConsistencyErrorCode, msg string, formatArgs ...interface{}) ChatThreadConsistencyError {
267	return &chatThreadConsistencyErrorImpl{
268		code: code,
269		msg:  fmt.Sprintf(msg, formatArgs...),
270	}
271}
272
273//=============================================================================
274
275type BoxingError struct {
276	Msg  string
277	Perm bool
278}
279
280func NewBoxingError(msg string, perm bool) BoxingError {
281	return BoxingError{
282		Msg:  msg,
283		Perm: perm,
284	}
285}
286
287func (e BoxingError) Error() string {
288	return fmt.Sprintf("encryption error: %s perm: %v", e.Msg, e.Perm)
289}
290
291func (e BoxingError) IsImmediateFail() (chat1.OutboxErrorType, bool) {
292	if e.Perm {
293		return chat1.OutboxErrorType_MISC, true
294	}
295	return 0, false
296}
297
298//=============================================================================
299
300type RestrictedBotChannelError struct{}
301
302func NewRestrictedBotChannelError() RestrictedBotChannelError {
303	return RestrictedBotChannelError{}
304}
305
306func (e RestrictedBotChannelError) Error() string {
307	return "bot restricted from sending to this channel"
308}
309
310func (e RestrictedBotChannelError) IsImmediateFail() (chat1.OutboxErrorType, bool) {
311	return chat1.OutboxErrorType_RESTRICTEDBOT, true
312}
313
314//=============================================================================
315
316type BoxingCryptKeysError struct {
317	Err error
318}
319
320// Cause implements the pkg/errors Cause() method, also cloned in libkb via HumanError,
321// so that we know which error to show to the human being using keybase (rather than
322// for our own internal uses).
323func (e BoxingCryptKeysError) Cause() error {
324	return e.Err
325}
326
327func NewBoxingCryptKeysError(err error) BoxingCryptKeysError {
328	return BoxingCryptKeysError{
329		Err: err,
330	}
331}
332
333func (e BoxingCryptKeysError) Error() string {
334	return fmt.Sprintf("boxing error: unable to get crypt keys: %s", e.Err.Error())
335}
336
337func (e BoxingCryptKeysError) Inner() error {
338	return e.Err
339}
340
341func (e BoxingCryptKeysError) IsImmediateFail() (chat1.OutboxErrorType, bool) {
342	if _, ok := e.Err.(libkb.IdentifySummaryError); ok {
343		return chat1.OutboxErrorType_IDENTIFY, true
344	}
345	return 0, false
346}
347
348//=============================================================================
349
350type BodyHashInvalid struct{}
351
352func (e BodyHashInvalid) Error() string {
353	return "chat body hash invalid"
354}
355
356type VersionError struct {
357	Kind     string
358	Version  int
359	Critical bool
360}
361
362func (e VersionError) Error() string {
363	return fmt.Sprintf("Unable to decrypt because current client is out of date. Please update your version of Keybase! Chat version error: [ unhandled: %s version: %d critical: %v ]", e.Kind, e.Version, e.Critical)
364}
365
366func (e VersionError) ExportType() chat1.MessageUnboxedErrorType {
367	if e.Critical {
368		return chat1.MessageUnboxedErrorType_BADVERSION_CRITICAL
369	}
370	return chat1.MessageUnboxedErrorType_BADVERSION
371}
372
373func (e VersionError) VersionKind() chat1.VersionKind {
374	return chat1.VersionKind(e.Kind)
375}
376
377func (e VersionError) VersionNumber() int {
378	return e.Version
379}
380
381func (e VersionError) IsCritical() bool {
382	return e.Critical
383}
384
385func NewMessageBoxedVersionError(version chat1.MessageBoxedVersion) VersionError {
386	return VersionError{
387		Kind:     string(chat1.VersionErrorMessageBoxed),
388		Version:  int(version),
389		Critical: true,
390	}
391}
392
393func NewHeaderVersionError(version chat1.HeaderPlaintextVersion,
394	defaultHeader chat1.HeaderPlaintextUnsupported) VersionError {
395	return VersionError{
396		Kind:     string(chat1.VersionErrorHeader),
397		Version:  int(version),
398		Critical: defaultHeader.Mi.Crit,
399	}
400}
401
402func NewBodyVersionError(version chat1.BodyPlaintextVersion, defaultBody chat1.BodyPlaintextUnsupported) VersionError {
403	return VersionError{
404		Kind:     string(chat1.VersionErrorBody),
405		Version:  int(version),
406		Critical: defaultBody.Mi.Crit,
407	}
408}
409
410//=============================================================================
411
412type HeaderMismatchError struct {
413	Field string
414}
415
416var _ error = (*HeaderMismatchError)(nil)
417
418func (e HeaderMismatchError) Error() string {
419	return fmt.Sprintf("chat header mismatch on %q", e.Field)
420}
421
422func NewHeaderMismatchError(field string) HeaderMismatchError {
423	return HeaderMismatchError{Field: field}
424}
425
426//=============================================================================
427
428type OfflineError struct {
429}
430
431func (e OfflineError) Error() string {
432	return "operation failed: no connection to chat server"
433}
434
435type OfflineClient struct {
436}
437
438func (e OfflineClient) Call(ctx context.Context, method string, arg interface{},
439	res interface{}, timeout time.Duration) error {
440	return OfflineError{}
441}
442
443func (e OfflineClient) CallCompressed(ctx context.Context, method string, arg interface{},
444	res interface{}, ctype rpc.CompressionType, timeout time.Duration) error {
445	return OfflineError{}
446}
447
448func (e OfflineClient) Notify(ctx context.Context, method string, arg interface{}, timeout time.Duration) error {
449	return OfflineError{}
450}
451
452//=============================================================================
453
454type DuplicateTopicNameError struct {
455	Conv chat1.ConversationLocal
456}
457
458func (e DuplicateTopicNameError) Error() string {
459	return fmt.Sprintf("channel name %s is already in use in %v",
460		e.Conv.GetTopicName(), e.Conv.Info.TlfName)
461}
462
463//=============================================================================
464
465type ImpteamBadteamError struct {
466	Msg string
467}
468
469func (e ImpteamBadteamError) Error() string {
470	return fmt.Sprintf("bad iteam found in conv: %s", e.Msg)
471}
472
473//=============================================================================
474
475type UnknownTLFNameError struct {
476	tlfName string
477}
478
479func NewUnknownTLFNameError(name string) UnknownTLFNameError {
480	return UnknownTLFNameError{
481		tlfName: name,
482	}
483}
484
485func (e UnknownTLFNameError) Error() string {
486	return fmt.Sprintf("unknown conversation name: %s", e.tlfName)
487}
488
489//=============================================================================
490
491type AttachmentUploadError struct {
492	Msg  string
493	Perm bool
494}
495
496func NewAttachmentUploadError(msg string, perm bool) AttachmentUploadError {
497	return AttachmentUploadError{
498		Msg:  msg,
499		Perm: perm,
500	}
501}
502
503func (e AttachmentUploadError) Error() string {
504	return fmt.Sprintf("attachment failed to upload; %s", e.Msg)
505}
506
507func (e AttachmentUploadError) IsImmediateFail() (chat1.OutboxErrorType, bool) {
508	return chat1.OutboxErrorType_MISC, e.Perm
509}
510
511//=============================================================================
512
513type SenderTestImmediateFailError struct {
514}
515
516func (e SenderTestImmediateFailError) Error() string {
517	return "sender test immediate fail error"
518}
519
520func (e SenderTestImmediateFailError) IsImmediateFail() (chat1.OutboxErrorType, bool) {
521	return chat1.OutboxErrorType_MISC, true
522}
523
524//=============================================================================
525
526type DecryptionKeyNotFoundError struct {
527	generation            int
528	kbfsEncrypted, public bool
529}
530
531func NewDecryptionKeyNotFoundError(generation int, public, kbfsEncrypted bool) DecryptionKeyNotFoundError {
532	return DecryptionKeyNotFoundError{
533		generation:    generation,
534		kbfsEncrypted: kbfsEncrypted,
535		public:        public,
536	}
537}
538
539func (e DecryptionKeyNotFoundError) Error() string {
540	return fmt.Sprintf("decryption key not found for generation: %v kbfsEncrypted: %v public: %v",
541		e.generation, e.kbfsEncrypted, e.public)
542}
543
544//=============================================================================
545
546type OfflineErrorKind int
547
548const (
549	OfflineErrorKindOnline OfflineErrorKind = iota
550	OfflineErrorKindOfflineBasic
551	OfflineErrorKindOfflineReconnect
552)
553
554func IsOfflineError(err error) OfflineErrorKind {
555	// Check type
556	switch terr := err.(type) {
557	case net.Error:
558		return OfflineErrorKindOfflineReconnect
559	case libkb.APINetError:
560		return OfflineErrorKindOfflineBasic
561	case OfflineError:
562		return OfflineErrorKindOfflineBasic
563	case TransientUnboxingError:
564		return IsOfflineError(terr.Inner())
565	}
566	// Check error itself
567	switch err {
568	case context.DeadlineExceeded:
569		fallthrough
570	case ErrChatServerTimeout:
571		return OfflineErrorKindOfflineReconnect
572	case ErrDuplicateConnection:
573		return OfflineErrorKindOfflineBasic
574	}
575
576	// Unfortunately, Go throws these without a type and they can occasionally
577	// propagate up. The strings were copied from
578	// https://golang.org/src/crypto/tls/conn.go
579	switch err.Error() {
580	case "tls: use of closed connection",
581		"tls: protocol is shutdown":
582		return OfflineErrorKindOfflineReconnect
583	}
584	return OfflineErrorKindOnline
585}
586
587func IsRekeyError(err error) (typ chat1.ConversationErrorType, ok bool) {
588	switch err := err.(type) {
589	case types.UnboxingError:
590		return IsRekeyError(err.Inner())
591	case libkb.NeedSelfRekeyError:
592		return chat1.ConversationErrorType_SELFREKEYNEEDED, true
593	case libkb.NeedOtherRekeyError:
594		return chat1.ConversationErrorType_OTHERREKEYNEEDED, true
595	default:
596		if teams.IsTeamReadError(err) {
597			return chat1.ConversationErrorType_OTHERREKEYNEEDED, true
598		}
599	}
600	return chat1.ConversationErrorType_NONE, false
601}
602
603//=============================================================================
604
605type FTLError struct {
606	msg string
607}
608
609func NewFTLError(s string) error {
610	return &FTLError{msg: s}
611}
612
613func (f FTLError) Error() string {
614	return fmt.Sprintf("FTL Error: %s", f.msg)
615}
616
617//=============================================================================
618
619type DevStoragePermissionDeniedError struct {
620	role keybase1.TeamRole
621}
622
623func NewDevStoragePermissionDeniedError(role keybase1.TeamRole) error {
624	return &DevStoragePermissionDeniedError{role: role}
625}
626
627func (e *DevStoragePermissionDeniedError) Error() string {
628	return fmt.Sprintf("role %q is not high enough", e.role)
629}
630
631//=============================================================================
632
633type DevStorageAdminOnlyError struct {
634	msg string
635}
636
637func NewDevStorageAdminOnlyError(msg string) error {
638	return &DevStorageAdminOnlyError{msg: msg}
639}
640
641func (e *DevStorageAdminOnlyError) Error() string {
642	return fmt.Sprintf("found a conversation and a message, but role checking failed: %s", e.msg)
643}
644