diff --git a/go.mod b/go.mod index 744fa3b..10b991e 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/zyedidia/clipboard v1.0.4 go.mau.fi/libsignal v0.1.0 go.mau.fi/util v0.3.1-0.20240209114727-da0b16df0446 - go.mau.fi/whatsmeow v0.0.0-20240209184912-c3e911dd6cfe + go.mau.fi/whatsmeow v0.0.0-20240212120533-ccac3f06df31 golang.org/x/crypto v0.19.0 golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3 golang.org/x/image v0.15.0 diff --git a/go.sum b/go.sum index 404aefb..26007e4 100644 --- a/go.sum +++ b/go.sum @@ -55,8 +55,8 @@ go.mau.fi/libsignal v0.1.0 h1:vAKI/nJ5tMhdzke4cTK1fb0idJzz1JuEIpmjprueC+c= go.mau.fi/libsignal v0.1.0/go.mod h1:R8ovrTezxtUNzCQE5PH30StOQWWeBskBsWE55vMfY9I= go.mau.fi/util v0.3.1-0.20240209114727-da0b16df0446 h1:goGC5wdUKIAdD/rMcA282viTTwUzvLwI/94cxYaKAjI= go.mau.fi/util v0.3.1-0.20240209114727-da0b16df0446/go.mod h1:rRypwgXVEPILomtFPyQcnbOeuRqf+nRN84vh/CICq4w= -go.mau.fi/whatsmeow v0.0.0-20240209184912-c3e911dd6cfe h1:ZosSMvW46/bX5tEdXXgF+weMIPj+r8QG5ry7JSdDWt8= -go.mau.fi/whatsmeow v0.0.0-20240209184912-c3e911dd6cfe/go.mod h1:lQHbhaG/fI+6hfGqz5Vzn2OBJBEZ05H0kCP6iJXriN4= +go.mau.fi/whatsmeow v0.0.0-20240212120533-ccac3f06df31 h1:pLQs6ZAx6vfiQVgb0ECrhsmgkpG0oyYryLylCHz3xjs= +go.mau.fi/whatsmeow v0.0.0-20240212120533-ccac3f06df31/go.mod h1:lQHbhaG/fI+6hfGqz5Vzn2OBJBEZ05H0kCP6iJXriN4= go.mau.fi/zeroconfig v0.1.2 h1:DKOydWnhPMn65GbXZOafgkPm11BvFashZWLct0dGFto= go.mau.fi/zeroconfig v0.1.2/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= diff --git a/messagix/client.go b/messagix/client.go index 8e87374..bcbbbf8 100644 --- a/messagix/client.go +++ b/messagix/client.go @@ -71,6 +71,9 @@ type Client struct { taskMutex *sync.Mutex activeTasks []int + catRefreshLock sync.Mutex + unnecessaryCATRequests int + stopCurrentConnection atomic.Pointer[context.CancelFunc] } diff --git a/messagix/e2ee-client.go b/messagix/e2ee-client.go index 4e31741..1572e15 100644 --- a/messagix/e2ee-client.go +++ b/messagix/e2ee-client.go @@ -17,13 +17,19 @@ package messagix import ( + "encoding/base64" + "encoding/json" + "fmt" "strconv" + "time" "google.golang.org/protobuf/proto" "go.mau.fi/whatsmeow" waProto "go.mau.fi/whatsmeow/binary/proto" waLog "go.mau.fi/whatsmeow/util/log" + + "go.mau.fi/mautrix-meta/messagix/types" ) func (c *Client) PrepareE2EEClient() *whatsmeow.Client { @@ -36,9 +42,61 @@ func (c *Client) PrepareE2EEClient() *whatsmeow.Client { UserAgent: UserAgent, BaseURL: c.getEndpoint("base_url"), } + e2eeClient.RefreshCAT = c.refreshCAT return e2eeClient } +type refreshCATResponseGraphQL struct { + Data struct { + SecureMessageOverWACATQuery types.CryptoAuthToken `json:"secure_message_over_wa_cat_query"` + } `json:"data"` + Extensions struct { + IsFinal bool `json:"is_final"` + } `json:"extensions"` +} + +func (c *Client) refreshCAT() error { + c.catRefreshLock.Lock() + defer c.catRefreshLock.Unlock() + currentExpiration := time.Unix(c.configs.browserConfigTable.MessengerWebInitData.CryptoAuthToken.ExpirationTimeInSeconds, 0) + if time.Until(currentExpiration) > 23*time.Hour { + c.unnecessaryCATRequests++ + logEvt := c.Logger.Warn().Time("expiration", currentExpiration).Int("unnecessary_requests", c.unnecessaryCATRequests) + switch c.unnecessaryCATRequests { + case 1, 3: + // Don't throw an error if it's just called again times accidentally + logEvt.Msg("Not refreshing CAT") + return nil + case 2: + // If it's called twice for no reason, there might actually be a reason, so refresh it again to be safe + logEvt.Msg("Refreshing CAT unnecessarily") + default: + logEvt.Msg("Failing CAT refresh call due to repeated requests") + // If it's called more than 4 times in total, fail + return fmt.Errorf("repeated unnecessary CAT refreshes") + } + } else { + c.unnecessaryCATRequests = 0 + } + c.Logger.Info().Time("prev_expiration", currentExpiration).Msg("Refreshing crypto auth token") + _, respData, err := c.makeGraphQLRequest("MAWCatQuery", struct{}{}) + if err != nil { + return fmt.Errorf("failed to send request: %w", err) + } + var parsedResp refreshCATResponseGraphQL + err = json.Unmarshal(respData, &parsedResp) + if err != nil { + c.Logger.Debug().Str("resp_data", base64.StdEncoding.EncodeToString(respData)).Msg("Response data for failed CAT query") + return fmt.Errorf("failed to parse response: %w", err) + } else if len(parsedResp.Data.SecureMessageOverWACATQuery.EncryptedSerializedCat) == 0 { + c.Logger.Debug().RawJSON("resp_data", respData).Msg("Response data for failed CAT query") + return fmt.Errorf("didn't get CAT in response") + } + c.Logger.Info().Msg("Successfully refreshed crypto auth token") + c.configs.browserConfigTable.MessengerWebInitData.CryptoAuthToken = parsedResp.Data.SecureMessageOverWACATQuery + return nil +} + func (c *Client) getClientPayload() *waProto.ClientPayload { userID, _ := strconv.ParseUint(c.device.ID.User, 10, 64) return &waProto.ClientPayload{ diff --git a/messagix/graphql.go b/messagix/graphql.go index 9c00d2f..1c5b129 100644 --- a/messagix/graphql.go +++ b/messagix/graphql.go @@ -10,6 +10,7 @@ import ( "github.com/google/go-querystring/query" + "go.mau.fi/mautrix-meta/messagix/cookies" "go.mau.fi/mautrix-meta/messagix/graphql" "go.mau.fi/mautrix-meta/messagix/lightspeed" "go.mau.fi/mautrix-meta/messagix/table" @@ -33,6 +34,7 @@ func (c *Client) makeGraphQLRequest(name string, variables interface{}) (*http.R payload.Variables = string(vBytes) payload.ServerTimestamps = "true" payload.DocID = graphQLDoc.DocId + payload.Jssesw = graphQLDoc.Jsessw form, err := query.Values(payload) if err != nil { @@ -51,7 +53,11 @@ func (c *Client) makeGraphQLRequest(name string, variables interface{}) (*http.R reqUrl := c.getEndpoint("graphql") //c.Logger.Info().Any("url", reqUrl).Any("payload", string(payloadBytes)).Any("headers", headers).Msg("Sending graphQL request.") - return c.MakeRequest(reqUrl, "POST", headers, payloadBytes, types.FORM) + resp, respData, err := c.MakeRequest(reqUrl, "POST", headers, payloadBytes, types.FORM) + if err == nil && resp != nil { + cookies.UpdateFromResponse(c.cookies, resp.Header) + } + return resp, respData, err } type LSErrorResponse struct { diff --git a/messagix/graphql/docs.go b/messagix/graphql/docs.go index 5b9d6c6..e7498b2 100644 --- a/messagix/graphql/docs.go +++ b/messagix/graphql/docs.go @@ -4,6 +4,8 @@ type GraphQLDoc struct { DocId string CallerClass string FriendlyName string + + Jsessw string } var GraphQLDocs = map[string]GraphQLDoc{ @@ -17,6 +19,12 @@ var GraphQLDocs = map[string]GraphQLDoc{ CallerClass: "RelayModern", FriendlyName: "LSPlatformGraphQLLightspeedRequestForIGDQuery", }, + "MAWCatQuery": { + DocId: "23999698219677129", + CallerClass: "RelayModern", + FriendlyName: "MAWCatQuery", + Jsessw: "1", + }, } type LSPlatformGraphQLLightspeedRequestPayload struct { diff --git a/messagix/http.go b/messagix/http.go index d2d33ca..68c21ff 100644 --- a/messagix/http.go +++ b/messagix/http.go @@ -33,6 +33,7 @@ type HttpQuery struct { SpinR string `url:"__spin_r,omitempty"` // not required SpinB string `url:"__spin_b,omitempty"` // not required SpinT string `url:"__spin_t,omitempty"` // not required + Jssesw string `url:"__jssesw,omitempty"` // not required FbAPICallerClass string `url:"fb_api_caller_class,omitempty"` // not required FbAPIReqFriendlyName string `url:"fb_api_req_friendly_name,omitempty"` // not required Variables string `url:"variables,omitempty"` diff --git a/messagix/types/account.go b/messagix/types/account.go index 25dd5fc..be9e09e 100644 --- a/messagix/types/account.go +++ b/messagix/types/account.go @@ -51,7 +51,7 @@ type MessengerWebInitData struct { type CryptoAuthToken struct { EncryptedSerializedCat string `json:"encrypted_serialized_cat,omitempty"` - ExpirationTimeInSeconds int `json:"expiration_time_in_seconds,omitempty"` + ExpirationTimeInSeconds int64 `json:"expiration_time_in_seconds,omitempty"` } type LSD struct {