From f3c2772ad46b315cc0b8af07e21f45383b4a17df Mon Sep 17 00:00:00 2001 From: "Andres G. Aragoneses" Date: Tue, 15 Sep 2020 00:54:43 +0800 Subject: [PATCH 1/2] Core: refactor around Session vs SessionStore --- src/TgSharp.Core/DataCenter.cs | 7 ++- src/TgSharp.Core/Network/MtProtoSender.cs | 6 ++- src/TgSharp.Core/Session.cs | 58 ++++++++++------------- src/TgSharp.Core/TelegramClient.cs | 38 +++++++-------- 4 files changed, 52 insertions(+), 57 deletions(-) diff --git a/src/TgSharp.Core/DataCenter.cs b/src/TgSharp.Core/DataCenter.cs index 6ef338fd..bfa211a5 100644 --- a/src/TgSharp.Core/DataCenter.cs +++ b/src/TgSharp.Core/DataCenter.cs @@ -2,14 +2,17 @@ namespace TgSharp.Core { internal class DataCenter { - internal DataCenter (int? dcId, string address, int port) + private const string defaultConnectionAddress = "149.154.175.100";//"149.154.167.50"; + private const int defaultConnectionPort = 443; + + internal DataCenter (int? dcId, string address = defaultConnectionAddress, int port = defaultConnectionPort) { DataCenterId = dcId; Address = address; Port = port; } - internal DataCenter (string address, int port) : this (null, address, port) + internal DataCenter (string address = defaultConnectionAddress, int port = defaultConnectionPort) : this (null, address, port) { } diff --git a/src/TgSharp.Core/Network/MtProtoSender.cs b/src/TgSharp.Core/Network/MtProtoSender.cs index 4a27da5a..b1699ce2 100644 --- a/src/TgSharp.Core/Network/MtProtoSender.cs +++ b/src/TgSharp.Core/Network/MtProtoSender.cs @@ -23,13 +23,15 @@ public class MtProtoSender //private ulong sessionId = GenerateRandomUlong(); private readonly TcpTransport transport; + private readonly ISessionStore sessionStore; private readonly Session session; public readonly List needConfirmation = new List(); - public MtProtoSender(TcpTransport transport, Session session) + public MtProtoSender(TcpTransport transport, ISessionStore sessionStore, Session session) { this.transport = transport; + this.sessionStore = sessionStore; this.session = session; } @@ -63,7 +65,7 @@ private int GenerateSequence(bool confirmed) await Send(memory.ToArray(), request, token).ConfigureAwait(false); } - session.Save(); + sessionStore.Save (session); } public async Task Send(byte[] packet, TLMethod request, CancellationToken token = default(CancellationToken)) diff --git a/src/TgSharp.Core/Session.cs b/src/TgSharp.Core/Session.cs index df1bf345..a18d0a0e 100644 --- a/src/TgSharp.Core/Session.cs +++ b/src/TgSharp.Core/Session.cs @@ -71,11 +71,32 @@ public Session Load(string sessionUserId) } } - public class Session + internal static class SessionFactory { - private const string defaultConnectionAddress = "149.154.175.100";//"149.154.167.50"; - private const int defaultConnectionPort = 443; + internal static Session TryLoadOrCreateNew (ISessionStore store, string sessionUserId) + { + var session = store.Load (sessionUserId); + if (null == session) { + var defaultDataCenter = new DataCenter (); + session = new Session { + Id = GenerateRandomUlong (), + SessionUserId = sessionUserId, + DataCenter = defaultDataCenter, + }; + } + return session; + } + private static ulong GenerateRandomUlong () + { + var random = new Random (); + ulong rand = (((ulong)random.Next ()) << 32) | ((ulong)random.Next ()); + return rand; + } + } + + public class Session + { public int Sequence { get; set; } #if CI // see the same CI-wrapped assignment in .FromBytes(), but this one will become useful @@ -100,12 +121,9 @@ private static int CurrentTime () public TLUser TLUser { get; set; } private Random random; - private ISessionStore store; - - public Session(ISessionStore store) + public Session() { random = new Random(); - this.store = store; } public byte[] ToBytes() @@ -170,7 +188,7 @@ public static Session FromBytes(byte[] buffer, ISessionStore store, string sessi var authData = Serializers.Bytes.Read(reader); var defaultDataCenter = new DataCenter (serverAddress, port); - return new Session(store) + return new Session() { AuthKey = new AuthKey(authData), Id = id, @@ -186,30 +204,6 @@ public static Session FromBytes(byte[] buffer, ISessionStore store, string sessi } } - public void Save() - { - store.Save(this); - } - - public static Session TryLoadOrCreateNew(ISessionStore store, string sessionUserId) - { - var defaultDataCenter = new DataCenter (defaultConnectionAddress, defaultConnectionPort); - - return store.Load(sessionUserId) ?? new Session(store) - { - Id = GenerateRandomUlong(), - SessionUserId = sessionUserId, - DataCenter = defaultDataCenter, - }; - } - - private static ulong GenerateRandomUlong() - { - var random = new Random(); - ulong rand = (((ulong)random.Next()) << 32) | ((ulong)random.Next()); - return rand; - } - public long GetNewMessageId() { long time = Convert.ToInt64((DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalMilliseconds); diff --git a/src/TgSharp.Core/TelegramClient.cs b/src/TgSharp.Core/TelegramClient.cs index 8858134a..ceae05e0 100644 --- a/src/TgSharp.Core/TelegramClient.cs +++ b/src/TgSharp.Core/TelegramClient.cs @@ -31,15 +31,11 @@ public class TelegramClient : IDisposable private readonly int apiId; private readonly string sessionUserId; private readonly ISessionStore store; - private Session session; private List dcOptions; private readonly TcpClientConnectionHandler handler; private readonly DataCenterIPVersion dcIpVersion; - public Session Session - { - get { return session; } - } + public Session Session { get; private set; } /// /// Creates a new TelegramClient @@ -83,17 +79,17 @@ public TelegramClient(int apiId, string apiHash, { token.ThrowIfCancellationRequested(); - session = Session.TryLoadOrCreateNew (store, sessionUserId); - transport = new TcpTransport (session.DataCenter.Address, session.DataCenter.Port, this.handler); + Session = SessionFactory.TryLoadOrCreateNew (store, sessionUserId); + transport = new TcpTransport (Session.DataCenter.Address, Session.DataCenter.Port, this.handler); - if (session.AuthKey == null || reconnect) + if (Session.AuthKey == null || reconnect) { var result = await Authenticator.DoAuthentication(transport, token).ConfigureAwait(false); - session.AuthKey = result.AuthKey; - session.TimeOffset = result.TimeOffset; + Session.AuthKey = result.AuthKey; + Session.TimeOffset = result.TimeOffset; } - sender = new MtProtoSender(transport, session); + sender = new MtProtoSender(transport, store, Session); //set-up layer var config = new TLRequestGetConfig(); @@ -123,7 +119,7 @@ public TelegramClient(int apiId, string apiHash, throw new InvalidOperationException($"Can't reconnect. Establish initial connection first."); TLExportedAuthorization exported = null; - if (session.TLUser != null) + if (Session.TLUser != null) { TLRequestExportAuthorization exportAuthorization = new TLRequestExportAuthorization() { DcId = dcId }; exported = await SendRequestAsync(exportAuthorization, token).ConfigureAwait(false); @@ -151,12 +147,12 @@ public TelegramClient(int apiId, string apiHash, dc = dcs.First(); var dataCenter = new DataCenter (dcId, dc.IpAddress, dc.Port); - session.DataCenter = dataCenter; - session.Save(); + Session.DataCenter = dataCenter; + this.store.Save (Session); await ConnectInternalAsync(true, token).ConfigureAwait(false); - if (session.TLUser != null) + if (Session.TLUser != null) { TLRequestImportAuthorization importAuthorization = new TLRequestImportAuthorization() { Id = exported.Id, Bytes = exported.Bytes }; var imported = await SendRequestAsync(importAuthorization, token).ConfigureAwait(false); @@ -180,8 +176,8 @@ public TelegramClient(int apiId, string apiHash, } catch (DataCenterMigrationException e) { - if (session.DataCenter.DataCenterId.HasValue && - session.DataCenter.DataCenterId.Value == e.DC) + if (Session.DataCenter.DataCenterId.HasValue && + Session.DataCenter.DataCenterId.Value == e.DC) { throw new Exception($"Telegram server replied requesting a migration to DataCenter {e.DC} when this connection was already using this DataCenter", e); } @@ -195,7 +191,7 @@ public TelegramClient(int apiId, string apiHash, public bool IsUserAuthorized() { - return session.TLUser != null; + return Session.TLUser != null; } public async Task SendCodeRequestAsync(string phoneNumber, CancellationToken token = default(CancellationToken)) @@ -427,10 +423,10 @@ await sender.SendPingAsync(token) private void OnUserAuthenticated(TLUser TLUser) { - session.TLUser = TLUser; - session.SessionExpires = int.MaxValue; + Session.TLUser = TLUser; + Session.SessionExpires = int.MaxValue; - session.Save(); + this.store.Save (Session); } public bool IsConnected From 056139ed93e86899805b35f30fb8e88d080e22b9 Mon Sep 17 00:00:00 2001 From: Matt <33204743+Laiteux@users.noreply.github.com> Date: Tue, 13 Oct 2020 01:42:05 +0200 Subject: [PATCH 2/2] Use JSON as default file session store It's always better to have a human-readable JSON file than an unreadable DAT one containing bytes. You can see what the session file contains, what it is all about. Also, it prevents the file from getting corrupted. Happened to me a few times in the past using FileSessionStore, had to delete the session file and reconnect/auth. And it's just way better in general for storing data. For example, I currently store a lot of sessions in a SQL database, and doing it with bytes would have been a pain. JSON session file is just better in general. --- src/TgSharp.Core/DataCenter.cs | 19 +++-- src/TgSharp.Core/MTProto/Crypto/AuthKey.cs | 14 ++++ src/TgSharp.Core/Session.cs | 93 ++++++++++++++++++---- src/TgSharp.Core/TelegramClient.cs | 18 +++-- src/TgSharp.Core/TgSharp.Core.csproj | 3 + src/TgSharp.Core/packages.config | 1 + 6 files changed, 117 insertions(+), 31 deletions(-) diff --git a/src/TgSharp.Core/DataCenter.cs b/src/TgSharp.Core/DataCenter.cs index bfa211a5..cce2b67e 100644 --- a/src/TgSharp.Core/DataCenter.cs +++ b/src/TgSharp.Core/DataCenter.cs @@ -1,10 +1,17 @@ +using System; + namespace TgSharp.Core { - internal class DataCenter + public class DataCenter { private const string defaultConnectionAddress = "149.154.175.100";//"149.154.167.50"; private const int defaultConnectionPort = 443; + [Obsolete("Do not use, this ctor is public only for serialization")] + public DataCenter() + { + } + internal DataCenter (int? dcId, string address = defaultConnectionAddress, int port = defaultConnectionPort) { DataCenterId = dcId; @@ -12,12 +19,8 @@ internal DataCenter (int? dcId, string address = defaultConnectionAddress, int p Port = port; } - internal DataCenter (string address = defaultConnectionAddress, int port = defaultConnectionPort) : this (null, address, port) - { - } - - internal int? DataCenterId { get; } - internal string Address { get; } - internal int Port { get; } + public int? DataCenterId { get; set; } + public string Address { get; set; } + public int Port { get; set; } } } \ No newline at end of file diff --git a/src/TgSharp.Core/MTProto/Crypto/AuthKey.cs b/src/TgSharp.Core/MTProto/Crypto/AuthKey.cs index 1802cae1..c4e15407 100644 --- a/src/TgSharp.Core/MTProto/Crypto/AuthKey.cs +++ b/src/TgSharp.Core/MTProto/Crypto/AuthKey.cs @@ -9,6 +9,12 @@ public class AuthKey private byte[] key; private ulong keyId; private ulong auxHash; + + [Obsolete("Do not use, this ctor is public only for serialization")] + public AuthKey() + { + } + public AuthKey(BigInteger gab) { key = gab.ToByteArrayUnsigned(); @@ -69,6 +75,10 @@ public byte[] Data { return key; } + set + { + key = value; + } } public ulong Id @@ -77,6 +87,10 @@ public ulong Id { return keyId; } + set + { + keyId = value; + } } public override string ToString() diff --git a/src/TgSharp.Core/Session.cs b/src/TgSharp.Core/Session.cs index a18d0a0e..b6f8023c 100644 --- a/src/TgSharp.Core/Session.cs +++ b/src/TgSharp.Core/Session.cs @@ -1,6 +1,6 @@ using System; using System.IO; - +using Newtonsoft.Json; using TgSharp.TL; using TgSharp.Core.MTProto; using TgSharp.Core.MTProto.Crypto; @@ -13,11 +13,16 @@ public interface ISessionStore Session Load(string sessionUserId); } - public class FileSessionStore : ISessionStore + [Obsolete("Use JsonFileSessionStore")] + public class FileSessionStore : BinaryFileSessionStore + { + } + + public class BinaryFileSessionStore : ISessionStore { private readonly DirectoryInfo basePath; - public FileSessionStore(DirectoryInfo basePath = null) + public BinaryFileSessionStore(DirectoryInfo basePath = null) { if (basePath != null && !basePath.Exists) { @@ -58,6 +63,52 @@ public Session Load(string sessionUserId) } } + public class JsonFileSessionStore : ISessionStore + { + private readonly DirectoryInfo basePath; + private readonly JsonSerializerSettings jsonSerializerSettings; + + internal static JsonFileSessionStore DefaultSessionStore () + { + return new JsonFileSessionStore (null, new JsonSerializerSettings () { + Formatting = Formatting.Indented + }); + } + + public JsonFileSessionStore(DirectoryInfo basePath = null, JsonSerializerSettings jsonSerializerSettings = null) + { + if (basePath != null && !basePath.Exists) + { + throw new ArgumentException("basePath doesn't exist", nameof(basePath)); + } + + this.basePath = basePath; + this.jsonSerializerSettings = jsonSerializerSettings; + } + + public void Save(Session session) + { + File.WriteAllText(GetSessionPath(session.SessionUserId), JsonConvert.SerializeObject(session, jsonSerializerSettings)); + } + + public Session Load(string sessionUserId) + { + string sessionPath = GetSessionPath(sessionUserId); + + if (File.Exists(sessionPath)) + { + return JsonConvert.DeserializeObject (File.ReadAllText (sessionPath), jsonSerializerSettings); + } + + return null; + } + + private string GetSessionPath(string sessionUserId) + { + return Path.Combine(basePath?.FullName ?? string.Empty, sessionUserId + ".json"); + } + } + public class FakeSessionStore : ISessionStore { public void Save(Session session) @@ -77,7 +128,7 @@ internal static Session TryLoadOrCreateNew (ISessionStore store, string sessionU { var session = store.Load (sessionUserId); if (null == session) { - var defaultDataCenter = new DataCenter (); + var defaultDataCenter = new DataCenter (null); session = new Session { Id = GenerateRandomUlong (), SessionUserId = sessionUserId, @@ -111,20 +162,30 @@ private static int CurrentTime () #endif public string SessionUserId { get; set; } - internal DataCenter DataCenter { get; set; } + public DataCenter DataCenter { get; set; } public AuthKey AuthKey { get; set; } public ulong Id { get; set; } public ulong Salt { get; set; } public int TimeOffset { get; set; } public long LastMessageId { get; set; } public int SessionExpires { get; set; } - public TLUser TLUser { get; set; } - private Random random; - public Session() - { - random = new Random(); + public int UserId { get; set; } + private TLUser user = null; + public TLUser User { + get { return user; } + internal set { + user = value; + if (value != null) { + UserId = value.Id; + } + } } + public bool AuthenticatedSuccessfully { + get { return UserId != default; } + } + + private readonly Random random = new Random(); public byte[] ToBytes() { @@ -139,11 +200,11 @@ public byte[] ToBytes() Serializers.String.Write(writer, DataCenter.Address); writer.Write(DataCenter.Port); - if (TLUser != null) + if (AuthenticatedSuccessfully) { writer.Write(1); writer.Write(SessionExpires); - ObjectUtils.SerializeObject(TLUser, writer); + ObjectUtils.SerializeObject (UserId, writer); } else { @@ -178,15 +239,15 @@ public static Session FromBytes(byte[] buffer, ISessionStore store, string sessi var isAuthExsist = reader.ReadInt32() == 1; int sessionExpires = 0; - TLUser TLUser = null; + int userId = default; if (isAuthExsist) { sessionExpires = reader.ReadInt32(); - TLUser = (TLUser)ObjectUtils.DeserializeObject(reader); + userId = (int)ObjectUtils.DeserializeObject (reader); } var authData = Serializers.Bytes.Read(reader); - var defaultDataCenter = new DataCenter (serverAddress, port); + var defaultDataCenter = new DataCenter (null, serverAddress, port); return new Session() { @@ -197,7 +258,7 @@ public static Session FromBytes(byte[] buffer, ISessionStore store, string sessi LastMessageId = lastMessageId, TimeOffset = timeOffset, SessionExpires = sessionExpires, - TLUser = TLUser, + UserId = userId, SessionUserId = sessionUserId, DataCenter = defaultDataCenter, }; diff --git a/src/TgSharp.Core/TelegramClient.cs b/src/TgSharp.Core/TelegramClient.cs index ceae05e0..27452b33 100644 --- a/src/TgSharp.Core/TelegramClient.cs +++ b/src/TgSharp.Core/TelegramClient.cs @@ -1,11 +1,12 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Security.Cryptography; using System.Text; using System.Threading; using System.Threading.Tasks; - +using Newtonsoft.Json; using TgSharp.TL; using TgSharp.TL.Account; using TgSharp.TL.Auth; @@ -59,7 +60,10 @@ public TelegramClient(int apiId, string apiHash, throw new MissingApiConfigurationException("API_HASH"); if (store == null) - store = new FileSessionStore(); + { + store = JsonFileSessionStore.DefaultSessionStore (); + } + this.store = store; this.apiHash = apiHash; @@ -119,7 +123,7 @@ public TelegramClient(int apiId, string apiHash, throw new InvalidOperationException($"Can't reconnect. Establish initial connection first."); TLExportedAuthorization exported = null; - if (Session.TLUser != null) + if (Session.AuthenticatedSuccessfully) { TLRequestExportAuthorization exportAuthorization = new TLRequestExportAuthorization() { DcId = dcId }; exported = await SendRequestAsync(exportAuthorization, token).ConfigureAwait(false); @@ -152,7 +156,7 @@ public TelegramClient(int apiId, string apiHash, await ConnectInternalAsync(true, token).ConfigureAwait(false); - if (Session.TLUser != null) + if (Session.AuthenticatedSuccessfully) { TLRequestImportAuthorization importAuthorization = new TLRequestImportAuthorization() { Id = exported.Id, Bytes = exported.Bytes }; var imported = await SendRequestAsync(importAuthorization, token).ConfigureAwait(false); @@ -191,7 +195,7 @@ public TelegramClient(int apiId, string apiHash, public bool IsUserAuthorized() { - return Session.TLUser != null; + return Session.AuthenticatedSuccessfully; } public async Task SendCodeRequestAsync(string phoneNumber, CancellationToken token = default(CancellationToken)) @@ -421,9 +425,9 @@ await sender.SendPingAsync(token) .ConfigureAwait(false); } - private void OnUserAuthenticated(TLUser TLUser) + private void OnUserAuthenticated(TLUser user) { - Session.TLUser = TLUser; + Session.User = user; Session.SessionExpires = int.MaxValue; this.store.Save (Session); diff --git a/src/TgSharp.Core/TgSharp.Core.csproj b/src/TgSharp.Core/TgSharp.Core.csproj index 799b3e8a..08832374 100644 --- a/src/TgSharp.Core/TgSharp.Core.csproj +++ b/src/TgSharp.Core/TgSharp.Core.csproj @@ -34,6 +34,9 @@ $(DefineConstants);CI + + ..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll + diff --git a/src/TgSharp.Core/packages.config b/src/TgSharp.Core/packages.config index 6b8deb9c..43cb3fa8 100644 --- a/src/TgSharp.Core/packages.config +++ b/src/TgSharp.Core/packages.config @@ -1,3 +1,4 @@  + \ No newline at end of file