Skip to content

Commit

Permalink
Use user agent provided by provisioning API
Browse files Browse the repository at this point in the history
  • Loading branch information
tulir committed Jun 15, 2023
1 parent 90bf422 commit 8e1baa6
Show file tree
Hide file tree
Showing 8 changed files with 80 additions and 14 deletions.
10 changes: 8 additions & 2 deletions maugclib/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,11 @@ class Client:
_listen_future: asyncio.Future | None

def __init__(
self, cookies: http_utils.Cookies, max_retries: int = 5, retry_backoff_base: int = 2
self,
cookies: http_utils.Cookies,
user_agent: str | None = None,
max_retries: int = 5,
retry_backoff_base: int = 2,
) -> None:
self._max_retries = max_retries
self._retry_backoff_base = retry_backoff_base
Expand Down Expand Up @@ -80,7 +84,9 @@ def __init__(
state_update: A ``StateUpdate`` message.
"""

self._session = http_utils.Session(cookies, proxy=os.environ.get("HTTP_PROXY"))
self._session = http_utils.Session(
cookies, user_agent=user_agent, proxy=os.environ.get("HTTP_PROXY")
)

# channel.Channel instance (populated by .connect()):
self._channel = None
Expand Down
24 changes: 21 additions & 3 deletions maugclib/http_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from http.cookies import Morsel, SimpleCookie
import asyncio
import logging
import re

from yarl import URL
import aiohttp
Expand All @@ -19,7 +20,14 @@
MAX_RETRIES = 3
ORIGIN_URL = "https://chat.google.com"

USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"
LATEST_CHROME_VERSION = "114"
LATEST_FIREFOX_VERSION = "114"
DEFAULT_USER_AGENT = (
f"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
f"Chrome/{LATEST_CHROME_VERSION}.0.0.0 Safari/537.36"
)
chrome_version_regex = re.compile(r"Chrome/\d+\.\d+\.\d+\.\d+")
firefox_version_regex = re.compile(r"Firefox/\d+.\d+")


class FetchResponse(NamedTuple):
Expand Down Expand Up @@ -47,7 +55,7 @@ class Session:
proxy (str): (optional) HTTP proxy URL to use for requests.
"""

def __init__(self, cookies: Cookies, proxy: str = None) -> None:
def __init__(self, cookies: Cookies, proxy: str = None, user_agent: str = None) -> None:
self._proxy = proxy

# The server does not support quoting cookie values (see #498).
Expand All @@ -58,12 +66,22 @@ def __init__(self, cookies: Cookies, proxy: str = None) -> None:
cookie[key.upper()].update({"domain": "chat.google.com", "path": "/"})
self._cookie_jar.update_cookies(cookie, chat_google_com)

if user_agent:
user_agent = chrome_version_regex.sub(
f"Chrome/{LATEST_CHROME_VERSION}.0.0.0", user_agent
)
user_agent = firefox_version_regex.sub(
f"Firefox/{LATEST_FIREFOX_VERSION}.0", user_agent
)
else:
user_agent = DEFAULT_USER_AGENT

timeout = aiohttp.ClientTimeout(connect=CONNECT_TIMEOUT)
self._session = aiohttp.ClientSession(
cookie_jar=self._cookie_jar,
timeout=timeout,
trust_env=True,
headers={"User-Agent": USER_AGENT},
headers={"User-Agent": user_agent},
)

def get_auth_cookies(self) -> Cookies:
Expand Down
1 change: 1 addition & 0 deletions mautrix_googlechat/db/upgrade/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@
v06_space_description,
v07_puppet_contact_info_set,
v08_web_app_auth,
v09_web_app_ua,
)
3 changes: 2 additions & 1 deletion mautrix_googlechat/db/upgrade/v00_latest_revision.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@
from . import upgrade_table


@upgrade_table.register(description="Latest revision", upgrades_to=8)
@upgrade_table.register(description="Latest revision", upgrades_to=9)
async def upgrade_latest(conn: Connection) -> None:
await conn.execute(
"""CREATE TABLE "user" (
mxid TEXT PRIMARY KEY,
gcid TEXT UNIQUE,
cookies TEXT,
user_agent TEXT,
notice_room TEXT,
revision BIGINT
)"""
Expand Down
23 changes: 23 additions & 0 deletions mautrix_googlechat/db/upgrade/v09_web_app_ua.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# mautrix-googlechat - A Matrix-Google Chat puppeting bridge
# Copyright (C) 2023 Tulir Asokan, Sumner Evans
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
from mautrix.util.async_db import Connection

from . import upgrade_table


@upgrade_table.register(description="Store user agent for users")
async def upgrade_v09(conn: Connection) -> None:
await conn.execute('ALTER TABLE "user" ADD COLUMN user_agent TEXT')
21 changes: 15 additions & 6 deletions mautrix_googlechat/db/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class User:
mxid: UserID
gcid: str | None
cookies: Cookies | None
user_agent: str | None
notice_room: RoomID | None
revision: int | None

Expand All @@ -50,21 +51,25 @@ def _from_row(cls, row: Record | None) -> User | None:
@classmethod
async def all_logged_in(cls) -> list[User]:
q = (
'SELECT mxid, gcid, cookies, notice_room, revision FROM "user" '
'SELECT mxid, gcid, cookies, user_agent, notice_room, revision FROM "user" '
"WHERE cookies IS NOT NULL"
)
rows = await cls.db.fetch(q)
return [cls._from_row(row) for row in rows]

@classmethod
async def get_by_gcid(cls, gcid: str) -> User | None:
q = 'SELECT mxid, gcid, cookies, notice_room, revision FROM "user" WHERE gcid=$1'
q = """
SELECT mxid, gcid, cookies, user_agent, notice_room, revision FROM "user" WHERE gcid=$1
"""
row = await cls.db.fetchrow(q, gcid)
return cls._from_row(row)

@classmethod
async def get_by_mxid(cls, mxid: UserID) -> User | None:
q = 'SELECT mxid, gcid, cookies, notice_room, revision FROM "user" WHERE mxid=$1'
q = """
SELECT mxid, gcid, cookies, user_agent, notice_room, revision FROM "user" WHERE mxid=$1
"""
row = await cls.db.fetchrow(q, mxid)
return cls._from_row(row)

Expand All @@ -74,22 +79,26 @@ def _values(self):
self.mxid,
self.gcid,
json.dumps(self.cookies._asdict()) if self.cookies else None,
self.user_agent,
self.notice_room,
self.revision,
)

async def insert(self) -> None:
q = (
'INSERT INTO "user" (mxid, gcid, cookies, notice_room, revision) '
"VALUES ($1, $2, $3, $4, $5)"
'INSERT INTO "user" (mxid, gcid, cookies, user_agent, notice_room, revision) '
"VALUES ($1, $2, $3, $4, $5, $6)"
)
await self.db.execute(q, *self._values)

async def delete(self) -> None:
await self.db.execute('DELETE FROM "user" WHERE mxid=$1', self.mxid)

async def save(self) -> None:
q = 'UPDATE "user" SET gcid=$2, cookies=$3, notice_room=$4, revision=$5 ' "WHERE mxid=$1"
q = (
'UPDATE "user" SET gcid=$2, cookies=$3, user_agent=$4, notice_room=$5, revision=$6 '
"WHERE mxid=$1"
)
await self.db.execute(q, *self._values)

async def set_revision(self, revision: int) -> None:
Expand Down
10 changes: 9 additions & 1 deletion mautrix_googlechat/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,14 @@ def __init__(
gcid: str | None = None,
revision: int | None = None,
cookies: Cookies | None = None,
user_agent: str | None = None,
notice_room: RoomID | None = None,
) -> None:
super().__init__(
mxid=mxid,
gcid=gcid,
cookies=cookies,
user_agent=user_agent,
revision=revision,
notice_room=notice_room,
)
Expand Down Expand Up @@ -252,7 +254,12 @@ async def _try_init(self) -> None:

async def connect(self, cookies: Cookies | None = None, get_self: bool = False) -> bool:
self.log.debug("Running post-login actions")
self.client = Client(cookies or self.cookies, max_retries=3, retry_backoff_base=2)
self.client = Client(
cookies or self.cookies,
user_agent=self.user_agent,
max_retries=3,
retry_backoff_base=2,
)
await self.client.refresh_tokens()
if get_self or not self.gcid:
self.log.debug("Fetching own user ID before connecting")
Expand Down Expand Up @@ -403,6 +410,7 @@ async def logout(self, is_manual: bool, error: UnexpectedStatusError | None = No
self.by_gcid.pop(self.gcid, None)
self.gcid = None
self.cookies = None
self.user_agent = None
await self.stop()
self.client = None
self.connected = False
Expand Down
2 changes: 1 addition & 1 deletion mautrix_googlechat/web/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ async def login(self, request: web.Request) -> web.Response:
raise ErrorResponse(
400, "Request body did not contain the required fields", "M_BAD_REQUEST"
)
# TODO use user_agent from request body instead of hardcoding?
user.user_agent = data.get("user_agent", None)

user.log.debug("Trying to log in with cookies")
try:
Expand Down

0 comments on commit 8e1baa6

Please sign in to comment.