errors.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. // Copyright (c) 2021 Tulir Asokan
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this
  5. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6. package whatsmeow
  7. import (
  8. "errors"
  9. "fmt"
  10. "net/http"
  11. waBinary "go.mau.fi/whatsmeow/binary"
  12. )
  13. // Miscellaneous errors
  14. var (
  15. ErrClientIsNil = errors.New("client is nil")
  16. ErrNoSession = errors.New("can't encrypt message for device: no signal session established")
  17. ErrIQTimedOut = errors.New("info query timed out")
  18. ErrNotConnected = errors.New("websocket not connected")
  19. ErrNotLoggedIn = errors.New("the store doesn't contain a device JID")
  20. ErrMessageTimedOut = errors.New("timed out waiting for message send response")
  21. ErrAlreadyConnected = errors.New("websocket is already connected")
  22. ErrPhoneNumberTooShort = errors.New("phone number too short")
  23. ErrPhoneNumberIsNotInternational = errors.New("international phone number required (must not start with 0)")
  24. ErrQRAlreadyConnected = errors.New("GetQRChannel must be called before connecting")
  25. ErrQRStoreContainsID = errors.New("GetQRChannel can only be called when there's no user ID in the client's Store")
  26. ErrNoPushName = errors.New("can't send presence without PushName set")
  27. ErrNoPrivacyToken = errors.New("no privacy token stored")
  28. ErrAppStateUpdate = errors.New("server returned error updating app state")
  29. )
  30. // Errors that happen while confirming device pairing
  31. var (
  32. ErrPairInvalidDeviceIdentityHMAC = errors.New("invalid device identity HMAC in pair success message")
  33. ErrPairInvalidDeviceSignature = errors.New("invalid device signature in pair success message")
  34. ErrPairRejectedLocally = errors.New("local PrePairCallback rejected pairing")
  35. )
  36. // PairProtoError is included in an events.PairError if the pairing failed due to a protobuf error.
  37. type PairProtoError struct {
  38. Message string
  39. ProtoErr error
  40. }
  41. func (err *PairProtoError) Error() string {
  42. return fmt.Sprintf("%s: %v", err.Message, err.ProtoErr)
  43. }
  44. func (err *PairProtoError) Unwrap() error {
  45. return err.ProtoErr
  46. }
  47. // PairDatabaseError is included in an events.PairError if the pairing failed due to being unable to save the credentials to the device store.
  48. type PairDatabaseError struct {
  49. Message string
  50. DBErr error
  51. }
  52. func (err *PairDatabaseError) Error() string {
  53. return fmt.Sprintf("%s: %v", err.Message, err.DBErr)
  54. }
  55. func (err *PairDatabaseError) Unwrap() error {
  56. return err.DBErr
  57. }
  58. var (
  59. // ErrProfilePictureUnauthorized is returned by GetProfilePictureInfo when trying to get the profile picture of a user
  60. // whose privacy settings prevent you from seeing their profile picture (status code 401).
  61. ErrProfilePictureUnauthorized = errors.New("the user has hidden their profile picture from you")
  62. // ErrProfilePictureNotSet is returned by GetProfilePictureInfo when the given user or group doesn't have a profile
  63. // picture (status code 404).
  64. ErrProfilePictureNotSet = errors.New("that user or group does not have a profile picture")
  65. // ErrGroupInviteLinkUnauthorized is returned by GetGroupInviteLink if you don't have the permission to get the link (status code 401).
  66. ErrGroupInviteLinkUnauthorized = errors.New("you don't have the permission to get the group's invite link")
  67. // ErrNotInGroup is returned by group info getting methods if you're not in the group (status code 403).
  68. ErrNotInGroup = errors.New("you're not participating in that group")
  69. // ErrGroupNotFound is returned by group info getting methods if the group doesn't exist (status code 404).
  70. ErrGroupNotFound = errors.New("that group does not exist")
  71. // ErrInviteLinkInvalid is returned by methods that use group invite links if the invite link is malformed.
  72. ErrInviteLinkInvalid = errors.New("that group invite link is not valid")
  73. // ErrInviteLinkRevoked is returned by methods that use group invite links if the invite link was valid, but has been revoked and can no longer be used.
  74. ErrInviteLinkRevoked = errors.New("that group invite link has been revoked")
  75. // ErrBusinessMessageLinkNotFound is returned by ResolveBusinessMessageLink if the link doesn't exist or has been revoked.
  76. ErrBusinessMessageLinkNotFound = errors.New("that business message link does not exist or has been revoked")
  77. // ErrContactQRLinkNotFound is returned by ResolveContactQRLink if the link doesn't exist or has been revoked.
  78. ErrContactQRLinkNotFound = errors.New("that contact QR link does not exist or has been revoked")
  79. // ErrInvalidImageFormat is returned by SetGroupPhoto if the given photo is not in the correct format.
  80. ErrInvalidImageFormat = errors.New("the given data is not a valid image")
  81. // ErrMediaNotAvailableOnPhone is returned by DecryptMediaRetryNotification if the given event contains error code 2.
  82. ErrMediaNotAvailableOnPhone = errors.New("media no longer available on phone")
  83. // ErrUnknownMediaRetryError is returned by DecryptMediaRetryNotification if the given event contains an unknown error code.
  84. ErrUnknownMediaRetryError = errors.New("unknown media retry error")
  85. // ErrInvalidDisappearingTimer is returned by SetDisappearingTimer if the given timer is not one of the allowed values.
  86. ErrInvalidDisappearingTimer = errors.New("invalid disappearing timer provided")
  87. )
  88. // Some errors that Client.SendMessage can return
  89. var (
  90. ErrBroadcastListUnsupported = errors.New("sending to non-status broadcast lists is not yet supported")
  91. ErrUnknownServer = errors.New("can't send message to unknown server")
  92. ErrRecipientADJID = errors.New("message recipient must be a user JID with no device part")
  93. ErrServerReturnedError = errors.New("server returned error")
  94. ErrInvalidInlineBotID = errors.New("invalid inline bot ID")
  95. )
  96. type DownloadHTTPError struct {
  97. *http.Response
  98. }
  99. func (dhe DownloadHTTPError) Error() string {
  100. return fmt.Sprintf("download failed with status code %d", dhe.StatusCode)
  101. }
  102. func (dhe DownloadHTTPError) Is(other error) bool {
  103. var otherDHE DownloadHTTPError
  104. return errors.As(other, &otherDHE) && dhe.StatusCode == otherDHE.StatusCode
  105. }
  106. // Some errors that Client.Download can return
  107. var (
  108. ErrMediaDownloadFailedWith403 = DownloadHTTPError{Response: &http.Response{StatusCode: 403}}
  109. ErrMediaDownloadFailedWith404 = DownloadHTTPError{Response: &http.Response{StatusCode: 404}}
  110. ErrMediaDownloadFailedWith410 = DownloadHTTPError{Response: &http.Response{StatusCode: 410}}
  111. ErrNoURLPresent = errors.New("no url present")
  112. ErrFileLengthMismatch = errors.New("file length does not match")
  113. ErrTooShortFile = errors.New("file too short")
  114. ErrInvalidMediaHMAC = errors.New("invalid media hmac")
  115. ErrInvalidMediaEncSHA256 = errors.New("hash of media ciphertext doesn't match")
  116. ErrInvalidMediaSHA256 = errors.New("hash of media plaintext doesn't match")
  117. ErrUnknownMediaType = errors.New("unknown media type")
  118. ErrNothingDownloadableFound = errors.New("didn't find any attachments in message")
  119. )
  120. var (
  121. ErrOriginalMessageSecretNotFound = errors.New("original message secret key not found")
  122. ErrNotEncryptedReactionMessage = errors.New("given message isn't an encrypted reaction message")
  123. ErrNotEncryptedCommentMessage = errors.New("given message isn't an encrypted comment message")
  124. ErrNotSecretEncryptedMessage = errors.New("given message isn't a secret encrypted message")
  125. ErrNotPollUpdateMessage = errors.New("given message isn't a poll update message")
  126. )
  127. type wrappedIQError struct {
  128. HumanError error
  129. IQError error
  130. }
  131. func (err *wrappedIQError) Error() string {
  132. return err.HumanError.Error()
  133. }
  134. func (err *wrappedIQError) Is(other error) bool {
  135. return errors.Is(other, err.HumanError)
  136. }
  137. func (err *wrappedIQError) Unwrap() error {
  138. return err.IQError
  139. }
  140. func wrapIQError(human, iq error) error {
  141. return &wrappedIQError{human, iq}
  142. }
  143. // IQError is a generic error container for info queries
  144. type IQError struct {
  145. Code int
  146. Text string
  147. ErrorNode *waBinary.Node
  148. RawNode *waBinary.Node
  149. }
  150. // Common errors returned by info queries for use with errors.Is
  151. var (
  152. ErrIQBadRequest error = &IQError{Code: 400, Text: "bad-request"}
  153. ErrIQNotAuthorized error = &IQError{Code: 401, Text: "not-authorized"}
  154. ErrIQForbidden error = &IQError{Code: 403, Text: "forbidden"}
  155. ErrIQNotFound error = &IQError{Code: 404, Text: "item-not-found"}
  156. ErrIQNotAllowed error = &IQError{Code: 405, Text: "not-allowed"}
  157. ErrIQNotAcceptable error = &IQError{Code: 406, Text: "not-acceptable"}
  158. ErrIQGone error = &IQError{Code: 410, Text: "gone"}
  159. ErrIQResourceLimit error = &IQError{Code: 419, Text: "resource-limit"}
  160. ErrIQLocked error = &IQError{Code: 423, Text: "locked"}
  161. ErrIQRateOverLimit error = &IQError{Code: 429, Text: "rate-overlimit"}
  162. ErrIQInternalServerError error = &IQError{Code: 500, Text: "internal-server-error"}
  163. ErrIQServiceUnavailable error = &IQError{Code: 503, Text: "service-unavailable"}
  164. ErrIQPartialServerError error = &IQError{Code: 530, Text: "partial-server-error"}
  165. )
  166. func parseIQError(node *waBinary.Node) error {
  167. var err IQError
  168. err.RawNode = node
  169. val, ok := node.GetOptionalChildByTag("error")
  170. if ok {
  171. err.ErrorNode = &val
  172. ag := val.AttrGetter()
  173. err.Code = ag.OptionalInt("code")
  174. err.Text = ag.OptionalString("text")
  175. }
  176. return &err
  177. }
  178. func (iqe *IQError) Error() string {
  179. if iqe.Code == 0 {
  180. if iqe.ErrorNode != nil {
  181. return fmt.Sprintf("info query returned unknown error: %s", iqe.ErrorNode.XMLString())
  182. } else if iqe.RawNode != nil {
  183. return fmt.Sprintf("info query returned unexpected response: %s", iqe.RawNode.XMLString())
  184. } else {
  185. return "unknown info query error"
  186. }
  187. }
  188. return fmt.Sprintf("info query returned status %d: %s", iqe.Code, iqe.Text)
  189. }
  190. func (iqe *IQError) Is(other error) bool {
  191. otherIQE, ok := other.(*IQError)
  192. if !ok {
  193. return false
  194. } else if iqe.Code != 0 && otherIQE.Code != 0 {
  195. return otherIQE.Code == iqe.Code && otherIQE.Text == iqe.Text
  196. } else if iqe.ErrorNode != nil && otherIQE.ErrorNode != nil {
  197. return iqe.ErrorNode.XMLString() == otherIQE.ErrorNode.XMLString()
  198. } else {
  199. return false
  200. }
  201. }
  202. // ElementMissingError is returned by various functions that parse XML elements when a required element is missing.
  203. type ElementMissingError struct {
  204. Tag string
  205. In string
  206. }
  207. func (eme *ElementMissingError) Error() string {
  208. return fmt.Sprintf("missing <%s> element in %s", eme.Tag, eme.In)
  209. }
  210. var ErrIQDisconnected = &DisconnectedError{Action: "info query"}
  211. // DisconnectedError is returned if the websocket disconnects before an info query or other request gets a response.
  212. type DisconnectedError struct {
  213. Action string
  214. Node *waBinary.Node
  215. }
  216. func (err *DisconnectedError) Error() string {
  217. return fmt.Sprintf("websocket disconnected before %s returned response", err.Action)
  218. }
  219. func (err *DisconnectedError) Is(other error) bool {
  220. otherDisc, ok := other.(*DisconnectedError)
  221. if !ok {
  222. return false
  223. }
  224. return otherDisc.Action == err.Action
  225. }