diff --git a/src/TgSharp.Core/DataCenter.cs b/src/TgSharp.Core/DataCenter.cs index 6ef338fd..cce2b67e 100644 --- a/src/TgSharp.Core/DataCenter.cs +++ b/src/TgSharp.Core/DataCenter.cs @@ -1,20 +1,26 @@ +using System; + namespace TgSharp.Core { - internal class DataCenter + public 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; + + [Obsolete("Do not use, this ctor is public only for serialization")] + public DataCenter() { - DataCenterId = dcId; - Address = address; - Port = port; } - internal DataCenter (string address, int port) : this (null, address, port) + internal DataCenter (int? dcId, string address = defaultConnectionAddress, int port = defaultConnectionPort) { + DataCenterId = dcId; + Address = address; + Port = 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/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..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) @@ -71,11 +122,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 (null); + 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 @@ -90,23 +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; - - private ISessionStore store; - public Session(ISessionStore store) - { - random = new Random(); - this.store = store; + 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() { @@ -121,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 { @@ -160,17 +239,17 @@ 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(store) + return new Session() { AuthKey = new AuthKey(authData), Id = id, @@ -179,37 +258,13 @@ public static Session FromBytes(byte[] buffer, ISessionStore store, string sessi LastMessageId = lastMessageId, TimeOffset = timeOffset, SessionExpires = sessionExpires, - TLUser = TLUser, + UserId = userId, SessionUserId = sessionUserId, DataCenter = defaultDataCenter, }; } } - 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..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; @@ -31,15 +32,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 @@ -63,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; @@ -83,17 +83,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 +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); @@ -151,12 +151,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.AuthenticatedSuccessfully) { TLRequestImportAuthorization importAuthorization = new TLRequestImportAuthorization() { Id = exported.Id, Bytes = exported.Bytes }; var imported = await SendRequestAsync(importAuthorization, token).ConfigureAwait(false); @@ -180,8 +180,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 +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)) @@ -425,12 +425,12 @@ await sender.SendPingAsync(token) .ConfigureAwait(false); } - private void OnUserAuthenticated(TLUser TLUser) + private void OnUserAuthenticated(TLUser user) { - session.TLUser = TLUser; - session.SessionExpires = int.MaxValue; + Session.User = user; + Session.SessionExpires = int.MaxValue; - session.Save(); + this.store.Save (Session); } public bool IsConnected 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