mirror of
https://github.com/sochix/TLSharp.git
synced 2025-12-06 08:02:00 +01:00
Even if you're not supposed to use multiple threads with TLSharp, it might be worth it to try to make this sequence increment in a thread-safe way. Race conditions are always bad even if you know you are not supposed to use something in a certain way... @Laituex was having a ErrCode=32 'msg_seqno too low' problem and this is the first thing we looked at (even if he swore that he was not using different threads to access the telegram network)
214 lines
6.8 KiB
C#
214 lines
6.8 KiB
C#
using System;
|
|
using System.IO;
|
|
using TeleSharp.TL;
|
|
using TLSharp.Core.MTProto;
|
|
using TLSharp.Core.MTProto.Crypto;
|
|
|
|
namespace TLSharp.Core
|
|
{
|
|
public interface ISessionStore
|
|
{
|
|
void Save(Session session);
|
|
Session Load(string sessionUserId);
|
|
}
|
|
|
|
public class FileSessionStore : ISessionStore
|
|
{
|
|
private readonly DirectoryInfo basePath;
|
|
|
|
public FileSessionStore(DirectoryInfo basePath = null)
|
|
{
|
|
if (basePath != null && !basePath.Exists)
|
|
{
|
|
throw new ArgumentException("basePath doesn't exist", nameof(basePath));
|
|
}
|
|
this.basePath = basePath;
|
|
}
|
|
|
|
public void Save(Session session)
|
|
{
|
|
string sessionFileName = $"{session.SessionUserId}.dat";
|
|
var sessionPath = basePath == null ? sessionFileName :
|
|
Path.Combine(basePath.FullName, sessionFileName);
|
|
|
|
using (var stream = new FileStream(sessionPath, FileMode.OpenOrCreate))
|
|
{
|
|
var result = session.ToBytes();
|
|
stream.Write(result, 0, result.Length);
|
|
}
|
|
}
|
|
|
|
public Session Load(string sessionUserId)
|
|
{
|
|
string sessionFileName = $"{sessionUserId}.dat";
|
|
var sessionPath = basePath == null ? sessionFileName :
|
|
Path.Combine(basePath.FullName, sessionFileName);
|
|
|
|
if (!File.Exists(sessionPath))
|
|
return null;
|
|
|
|
using (var stream = new FileStream(sessionPath, FileMode.Open))
|
|
{
|
|
var buffer = new byte[2048];
|
|
stream.Read(buffer, 0, 2048);
|
|
|
|
return Session.FromBytes(buffer, this, sessionUserId);
|
|
}
|
|
}
|
|
}
|
|
|
|
public class FakeSessionStore : ISessionStore
|
|
{
|
|
public void Save(Session session)
|
|
{
|
|
|
|
}
|
|
|
|
public Session Load(string sessionUserId)
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public class Session
|
|
{
|
|
internal object Lock = new object ();
|
|
|
|
private const string defaultConnectionAddress = "149.154.175.100";//"149.154.167.50";
|
|
|
|
private const int defaultConnectionPort = 443;
|
|
|
|
public string SessionUserId { get; set; }
|
|
internal DataCenter DataCenter { get; set; }
|
|
public AuthKey AuthKey { get; set; }
|
|
public ulong Id { get; set; }
|
|
public int Sequence { 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 byte[] ToBytes()
|
|
{
|
|
using (var stream = new MemoryStream())
|
|
using (var writer = new BinaryWriter(stream))
|
|
{
|
|
writer.Write(Id);
|
|
writer.Write(Sequence);
|
|
writer.Write(Salt);
|
|
writer.Write(LastMessageId);
|
|
writer.Write(TimeOffset);
|
|
Serializers.String.Write(writer, DataCenter.Address);
|
|
writer.Write(DataCenter.Port);
|
|
|
|
if (TLUser != null)
|
|
{
|
|
writer.Write(1);
|
|
writer.Write(SessionExpires);
|
|
ObjectUtils.SerializeObject(TLUser, writer);
|
|
}
|
|
else
|
|
{
|
|
writer.Write(0);
|
|
}
|
|
|
|
Serializers.Bytes.Write(writer, AuthKey.Data);
|
|
|
|
return stream.ToArray();
|
|
}
|
|
}
|
|
|
|
public static Session FromBytes(byte[] buffer, ISessionStore store, string sessionUserId)
|
|
{
|
|
using (var stream = new MemoryStream(buffer))
|
|
using (var reader = new BinaryReader(stream))
|
|
{
|
|
var id = reader.ReadUInt64();
|
|
var sequence = reader.ReadInt32();
|
|
var salt = reader.ReadUInt64();
|
|
var lastMessageId = reader.ReadInt64();
|
|
var timeOffset = reader.ReadInt32();
|
|
var serverAddress = Serializers.String.Read(reader);
|
|
var port = reader.ReadInt32();
|
|
|
|
var isAuthExsist = reader.ReadInt32() == 1;
|
|
int sessionExpires = 0;
|
|
TLUser TLUser = null;
|
|
if (isAuthExsist)
|
|
{
|
|
sessionExpires = reader.ReadInt32();
|
|
TLUser = (TLUser)ObjectUtils.DeserializeObject(reader);
|
|
}
|
|
|
|
var authData = Serializers.Bytes.Read(reader);
|
|
var defaultDataCenter = new DataCenter (serverAddress, port);
|
|
|
|
return new Session(store)
|
|
{
|
|
AuthKey = new AuthKey(authData),
|
|
Id = id,
|
|
Salt = salt,
|
|
Sequence = sequence,
|
|
LastMessageId = lastMessageId,
|
|
TimeOffset = timeOffset,
|
|
SessionExpires = sessionExpires,
|
|
TLUser = TLUser,
|
|
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);
|
|
long newMessageId = ((time / 1000 + TimeOffset) << 32) |
|
|
((time % 1000) << 22) |
|
|
(random.Next(524288) << 2); // 2^19
|
|
// [ unix timestamp : 32 bit] [ milliseconds : 10 bit ] [ buffer space : 1 bit ] [ random : 19 bit ] [ msg_id type : 2 bit ] = [ msg_id : 64 bit ]
|
|
|
|
if (LastMessageId >= newMessageId)
|
|
{
|
|
newMessageId = LastMessageId + 4;
|
|
}
|
|
|
|
LastMessageId = newMessageId;
|
|
return newMessageId;
|
|
}
|
|
}
|
|
}
|