Skip to content

Commit

Permalink
Add new ssid support.
Browse files Browse the repository at this point in the history
  • Loading branch information
Berrysoft committed Apr 12, 2019
1 parent 6cabcca commit 5d2bf27
Show file tree
Hide file tree
Showing 10 changed files with 219 additions and 186 deletions.
37 changes: 16 additions & 21 deletions samples/TsinghuaNet/Program.vb
Original file line number Diff line number Diff line change
Expand Up @@ -24,31 +24,30 @@ Module Program
Console.Error.WriteLine("Exception occured: {0}", ex.Message)
Return
End Try
Dim helper As IConnect = Nothing

Dim username As String = command.Username
Dim password As String = If(command.Password, String.Empty)
Dim helper As IConnect = CreateHelper(username, password, command.Host)
If helper Is Nothing Then
Console.Error.WriteLine("Invalid host.")
Return
End If
If command.Login Then
If command.Logout Then
Console.Error.WriteLine("Cannot login and logout at the same time!")
Return
End If
Dim username As String = command.Username
If username Is Nothing Then
Console.Error.WriteLine("Please input username.")
Return
End If
Dim password As String = If(command.Password, String.Empty)
helper = CreateHelper(username, password, command.Host)
If helper Is Nothing Then
Console.Error.WriteLine("Invalid host.")
If command.Logout Then
Console.Error.WriteLine("Cannot login and logout at the same time!")
Return
End If
Login(helper).Wait()
ElseIf command.Logout Then
helper = CreateHelper(Nothing, Nothing, command.Host)
If helper Is Nothing Then
Console.Error.WriteLine("Invalid host.")
If username Is Nothing Then
Console.Error.WriteLine("Please input username.")
Return
End If
Logout(helper, command.Username).Wait()
Logout(helper).Wait()
End If
If command.Flux Then
If helper Is Nothing Then
Expand Down Expand Up @@ -78,18 +77,14 @@ Module Program
End Function
Async Function Login(helper As IConnect) As Task
Try
Console.WriteLine(Await helper.LoginAsync())
Console.WriteLine((Await helper.LoginAsync()).Message)
Catch ex As Exception
Console.Error.WriteLine("Exception occured: {0}", ex.Message)
End Try
End Function
Async Function Logout(helper As IConnect, username As String) As Task
Async Function Logout(helper As IConnect) As Task
Try
If username Is Nothing Then
Console.WriteLine(Await helper.LogoutAsync())
Else
Console.WriteLine(Await helper.LogoutAsync(username))
End If
Console.WriteLine((Await helper.LogoutAsync()).Message)
Catch ex As Exception
Console.Error.WriteLine("Exception occured: {0}", ex.Message)
End Try
Expand Down
2 changes: 1 addition & 1 deletion samples/TsinghuaNet/TsinghuaNetCommand.vb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Class TsinghuaNetCommand
Public Property Logout As Boolean
<[Option]("f"c, "flux", HelpText:="Option to get flux of the user online")>
Public Property Flux As Boolean
<[Option]("u"c, "username", HelpText:="Username to login, required when -i")>
<[Option]("u"c, "username", HelpText:="Username to login, required when -i or -o")>
Public Property Username As String
<[Option]("p"c, "password", HelpText:="Password to login")>
Public Property Password As String
Expand Down
103 changes: 65 additions & 38 deletions src/Berrysoft.Tsinghua.Net/AuthHelper.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Json;
using System.Net.Http;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace Berrysoft.Tsinghua.Net
Expand All @@ -14,8 +13,7 @@ public abstract class AuthHelper : NetHelperBase, IConnect
private const string LogUriBase = "https://auth{0}.tsinghua.edu.cn/cgi-bin/srun_portal";
private const string FluxUriBase = "https://auth{0}.tsinghua.edu.cn/rad_user_info.php";
private const string ChallengeUriBase = "https://auth{0}.tsinghua.edu.cn/cgi-bin/get_challenge?username={{0}}&double_stack=1&ip&callback=callback";
private const string LogoutData = "action=logout";
private const string LogoutUserData = "action=logout&username={0}";
private static readonly int[] AcIds = new int[] { 1, 25, 33, 35 };
private readonly string LogUri;
private readonly string FluxUri;
private readonly string ChallengeUri;
Expand Down Expand Up @@ -61,66 +59,95 @@ internal AuthHelper(string username, string password, HttpClient client, int ver
/// Login to the network.
/// </summary>
/// <returns>The response of the website.</returns>
public async Task<string> LoginAsync() => await PostAsync(LogUri, await GetLoginDataAsync());
public async Task<LogResponse> LoginAsync()
{
LogResponse response = null;
foreach (int ac_id in AcIds)
{
response = LogResponse.ParseFromAuth(await PostAsync(LogUri, await GetLoginDataAsync(ac_id)));
if (response.Succeed)
break;
}
return response;
}

/// <summary>
/// Logout from the network.
/// </summary>
/// <returns>The response of the website.</returns>
public Task<string> LogoutAsync() => PostAsync(LogUri, LogoutData);
/// <summary>
/// Logout from the network with the specified username.
/// When a user logged in through <see cref="AuthHelper"/> and logged out through <see cref="NetHelper"/>,
/// he should call this method with his username explicitly, or he can't logout.
/// </summary>
/// <param name="username">The specified username.</param>
/// <returns>The response of the website.</returns>
public Task<string> LogoutAsync(string username) => PostAsync(LogUri, string.Format(LogoutUserData, username));
public async Task<LogResponse> LogoutAsync()
{
LogResponse response = null;
foreach (int ac_id in AcIds)
{
response = LogResponse.ParseFromAuth(await PostAsync(LogUri, await GetLogoutDataAsync(ac_id)));
if (response.Succeed)
break;
}
return response;
}

/// <summary>
/// Get information of the user online.
/// </summary>
/// <returns>An instance of <see cref="FluxUser"/> class of the current user.</returns>
public async Task<FluxUser> GetFluxAsync() => FluxUser.Parse(await PostAsync(FluxUri));

private static readonly Regex ChallengeRegex = new Regex(@"""challenge"":""(.*?)""");
/// <summary>
/// Get "challenge" to encode the password.
/// </summary>
/// <returns>The content of the website.</returns>
private async Task<string> GetChallengeAsync()
{
string result = await GetAsync(string.Format(ChallengeUri, Username));
Match match = ChallengeRegex.Match(result);
return match.Groups[1].Value;
JsonValue json = JsonValue.Parse(result.Substring(9, result.Length - 10));
return json["challenge"];
}

private Dictionary<string, string> logDataDictionary;
private const string LoginInfoJson = "{{\"ip\": \"\", \"acid\": \"1\", \"enc_ver\": \"srun_bx1\", \"username\": \"{0}\", \"password\": \"{1}\"}}";
private const string ChkSumData = "{0}{1}{0}{2}{0}1{0}{0}200{0}1{0}{3}";
private const string LoginInfoJson = "{{\"username\": \"{0}\", \"password\": \"{1}\", \"ip\": \"\", \"acid\": \"{2}\", \"enc_ver\": \"srun_bx1\"}}";
private const string ChkSumData = "{0}{1}{0}{2}{0}{4}{0}{0}200{0}1{0}{3}";
/// <summary>
/// Get login data with username, password and "challenge".
/// </summary>
/// <returns>A dictionary contains the data.</returns>
private async Task<Dictionary<string, string>> GetLoginDataAsync()
private async Task<Dictionary<string, string>> GetLoginDataAsync(int ac_id)
{
//const string passwordMD5 = "5e543256c480ac577d30f76f9120eb74";
string token = await GetChallengeAsync();
string passwordMD5 = CryptographyHelper.GetHMACMD5(token);
if (logDataDictionary == null)
string info = "{SRBX1}" + CryptographyHelper.Base64Encode(CryptographyHelper.XEncode(string.Format(LoginInfoJson, Username, Password, ac_id), token));
return new Dictionary<string, string>
{
logDataDictionary = new Dictionary<string, string>
{
["ac_id"] = "1",
["double_stack"] = "1",
["n"] = "200",
["type"] = "1"
};
}
logDataDictionary["action"] = "login";
logDataDictionary["password"] = "{MD5}" + passwordMD5;
logDataDictionary["info"] = "{SRBX1}" + CryptographyHelper.Base64Encode(CryptographyHelper.XEncode(string.Format(LoginInfoJson, Username, Password), token));
logDataDictionary["username"] = Username;
logDataDictionary["chksum"] = CryptographyHelper.GetSHA1(string.Format(ChkSumData, token, Username, passwordMD5, logDataDictionary["info"]));
return logDataDictionary;
["action"] = "login",
["ac_id"] = ac_id.ToString(),
["double_stack"] = "1",
["n"] = "200",
["type"] = "1",
["username"] = Username,
["password"] = "{MD5}" + passwordMD5,
["info"] = info,
["chksum"] = CryptographyHelper.GetSHA1(string.Format(ChkSumData, token, Username, passwordMD5, info, ac_id)),
["callback"] = "callback"
};
}

private const string LogoutInfoJson = "{{\"username\": \"{0}\", \"ip\": \"\", \"acid\": \"{1}\", \"enc_ver\": \"srun_bx1\"}}";
private const string LogoutChkSumData = "{0}{1}{0}{3}{0}{0}200{0}1{0}{2}";
private async Task<Dictionary<string, string>> GetLogoutDataAsync(int ac_id)
{
string token = await GetChallengeAsync();
string info = "{SRBX1}" + CryptographyHelper.Base64Encode(CryptographyHelper.XEncode(string.Format(LogoutInfoJson, Username, ac_id), token));
return new Dictionary<string, string>
{
["action"] = "logout",
["ac_id"] = ac_id.ToString(),
["double_stack"] = "1",
["n"] = "200",
["type"] = "1",
["username"] = Username,
["info"] = info,
["chksum"] = CryptographyHelper.GetSHA1(string.Format(LogoutChkSumData, token, Username, info, ac_id)),
["callback"] = "callback"
};
}
}
/// <summary>
Expand Down
4 changes: 4 additions & 0 deletions src/Berrysoft.Tsinghua.Net/Berrysoft.Tsinghua.Net.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="System.Json" Version="4.5.0" />
</ItemGroup>

<ItemGroup>
<Reference Include="System.Net.Http" Condition="'$(TargetFramework)'=='net48'" />
</ItemGroup>
Expand Down
5 changes: 1 addition & 4 deletions src/Berrysoft.Tsinghua.Net/CryptographyHelper.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Security.Cryptography;
using System.Text;

namespace Berrysoft.Tsinghua.Net
Expand Down
58 changes: 58 additions & 0 deletions src/Berrysoft.Tsinghua.Net/FluxUser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using System;

namespace Berrysoft.Tsinghua.Net
{
/// <summary>
/// A simple class represents the current user online.
/// </summary>
public class FluxUser
{
/// <summary>
/// Initializes a new instance of the <see cref="FluxUser"/> class.
/// </summary>
/// <param name="username">Username of the user.</param>
/// <param name="flux">Flux used by the user this month.</param>
/// <param name="onlineTime">Online time used this time of the user.</param>
/// <param name="balance">The network balance of the user.</param>
public FluxUser(string username, long flux, TimeSpan onlineTime, decimal balance)
{
Username = username;
Flux = flux;
OnlineTime = onlineTime;
Balance = balance;
}
/// <summary>
/// Username of the user.
/// </summary>
public string Username { get; }
/// <summary>
/// Flux used by the user this month.
/// </summary>
public long Flux { get; }
/// <summary>
/// Online time used this time of the user.
/// </summary>
public TimeSpan OnlineTime { get; }
/// <summary>
/// The network balance of the user.
/// </summary>
public decimal Balance { get; }

internal static FluxUser Parse(string fluxstr)
{
string[] r = fluxstr.Split(',');
if (string.IsNullOrWhiteSpace(r[0]))
{
return null;
}
else
{
return new FluxUser(
r[0],
long.Parse(r[6]),
TimeSpan.FromSeconds(long.Parse(r[2]) - long.Parse(r[1])),
decimal.Parse(r[11]));
}
}
}
}
55 changes: 55 additions & 0 deletions src/Berrysoft.Tsinghua.Net/LogResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using System;
using System.Json;

namespace Berrysoft.Tsinghua.Net
{
/// <summary>
/// The response of Login or Logout.
/// </summary>
public class LogResponse
{
/// <summary>
/// Shows whether the command is succeed.
/// </summary>
public bool Succeed { get; }
/// <summary>
/// The formatted response message.
/// </summary>
public string Message { get; }

/// <summary>
/// Initialize a new instance of <see cref="LogResponse"/> class.
/// </summary>
/// <param name="succeed">Whether the command is succeed.</param>
/// <param name="message">The formatted response message.</param>
public LogResponse(bool succeed, string message)
{
Succeed = succeed;
Message = message;
}

internal static LogResponse ParseFromNet(string response)
{
return new LogResponse(response == "Login is successful.", response);
}

internal static LogResponse ParseFromAuth(string response)
{
try
{
string jsonstr = response.Substring(9, response.Length - 10);
JsonValue json = JsonValue.Parse(jsonstr);
return new LogResponse(json["error"] == "ok", $"error: {json["error"]}\nerror_msg: {json["error_msg"]}");
}
catch (Exception)
{
return new LogResponse(false, response);
}
}

internal static LogResponse ParseFromUsereg(string response)
{
return new LogResponse(response == "ok", response);
}
}
}
Loading

0 comments on commit 5d2bf27

Please sign in to comment.