Basic Websocket support working, without authentication (auth broken)
This commit is contained in:
@@ -34,7 +34,14 @@ namespace ntfysh_client
|
|||||||
if (result != DialogResult.OK) return;
|
if (result != DialogResult.OK) return;
|
||||||
|
|
||||||
//Subscribe
|
//Subscribe
|
||||||
_notificationListener.SubscribeToTopicUsingLongHttpJson(dialog.Unique, dialog.TopicId, dialog.ServerUrl, dialog.Username, dialog.Password);
|
if (dialog.UseWebsockets)
|
||||||
|
{
|
||||||
|
_notificationListener.SubscribeToTopicUsingWebsocket(dialog.Unique, dialog.TopicId, dialog.ServerUrl, dialog.Username, dialog.Password);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_notificationListener.SubscribeToTopicUsingLongHttpJson(dialog.Unique, dialog.TopicId, dialog.ServerUrl, dialog.Username, dialog.Password);
|
||||||
|
}
|
||||||
|
|
||||||
//Add to the user visible list
|
//Add to the user visible list
|
||||||
notificationTopics.Items.Add(dialog.Unique);
|
notificationTopics.Items.Add(dialog.Unique);
|
||||||
@@ -167,7 +174,24 @@ namespace ntfysh_client
|
|||||||
//Load them in
|
//Load them in
|
||||||
foreach (SubscribedTopic topic in topics)
|
foreach (SubscribedTopic topic in topics)
|
||||||
{
|
{
|
||||||
_notificationListener.SubscribeToTopicUsingLongHttpJson($"{topic.TopicId}@{topic.ServerUrl}", topic.TopicId, topic.ServerUrl, topic.Username, topic.Password);
|
string[] parts = topic.ServerUrl.Split("://", 2);
|
||||||
|
|
||||||
|
switch (parts[0].ToLower())
|
||||||
|
{
|
||||||
|
case "ws":
|
||||||
|
case "wss":
|
||||||
|
_notificationListener.SubscribeToTopicUsingWebsocket($"{topic.TopicId}@{topic.ServerUrl}", topic.TopicId, topic.ServerUrl, topic.Username, topic.Password);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "http":
|
||||||
|
case "https":
|
||||||
|
_notificationListener.SubscribeToTopicUsingLongHttpJson($"{topic.TopicId}@{topic.ServerUrl}", topic.TopicId, topic.ServerUrl, topic.Username, topic.Password);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
notificationTopics.Items.Add($"{topic.TopicId}@{topic.ServerUrl}");
|
notificationTopics.Items.Add($"{topic.TopicId}@{topic.ServerUrl}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -31,7 +31,7 @@ namespace ntfysh_client
|
|||||||
ServicePointManager.DefaultConnectionLimit = 100;
|
ServicePointManager.DefaultConnectionLimit = 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ListenToTopicAsync(HttpRequestMessage message, CancellationToken cancellationToken)
|
private async Task ListenToTopicWithHttpLongJsonAsync(HttpRequestMessage message, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (_isDisposed) throw new ObjectDisposedException(nameof(NotificationListener));
|
if (_isDisposed) throw new ObjectDisposedException(nameof(NotificationListener));
|
||||||
|
|
||||||
@@ -84,6 +84,59 @@ namespace ntfysh_client
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task ListenToTopicWithWebsocketAsync(Uri uri, NetworkCredential credentials, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (_isDisposed) throw new ObjectDisposedException(nameof(NotificationListener));
|
||||||
|
|
||||||
|
while (!cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
using ClientWebSocket socket = new();
|
||||||
|
socket.Options.Credentials = credentials;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
StringBuilder mainBuffer = new();
|
||||||
|
|
||||||
|
await socket.ConnectAsync(uri, cancellationToken);
|
||||||
|
|
||||||
|
while (!cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
//Read as much as possible
|
||||||
|
byte[] buffer = new byte[8192];
|
||||||
|
WebSocketReceiveResult? result = await socket.ReceiveAsync(new ArraySegment<byte>(buffer), cancellationToken);
|
||||||
|
|
||||||
|
//Append it to our main buffer
|
||||||
|
mainBuffer.Append(Encoding.UTF8.GetString(buffer, 0, result.Count));
|
||||||
|
|
||||||
|
List<string> lines = mainBuffer.ToString().Split('\n').ToList();
|
||||||
|
//If we have not yet received a full line, meaning theres only 1 part, go back to reading
|
||||||
|
if (lines.Count <= 1) continue;
|
||||||
|
|
||||||
|
//We now have at least 1 line! Count how many full lines. There will always be a partial line at the end, even if that partial line is empty
|
||||||
|
//Separate the partial line from the full lines
|
||||||
|
int partialLineIndex = lines.Count - 1;
|
||||||
|
string partialLine = lines[partialLineIndex];
|
||||||
|
lines.RemoveAt(partialLineIndex);
|
||||||
|
|
||||||
|
//Process the full lines
|
||||||
|
foreach (string line in lines) ProcessMessage(line);
|
||||||
|
|
||||||
|
//Write back the partial line
|
||||||
|
mainBuffer.Clear();
|
||||||
|
mainBuffer.Append(partialLine);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
Debug.WriteLine(ex);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//Fall back to the outer loop to restart the listen, or cancel if requested
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void ProcessMessage(string message)
|
private void ProcessMessage(string message)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
@@ -120,11 +173,26 @@ namespace ntfysh_client
|
|||||||
}
|
}
|
||||||
|
|
||||||
CancellationTokenSource listenCanceller = new();
|
CancellationTokenSource listenCanceller = new();
|
||||||
Task listenTask = ListenToTopicAsync(message, listenCanceller.Token);
|
Task listenTask = ListenToTopicWithHttpLongJsonAsync(message, listenCanceller.Token);
|
||||||
|
|
||||||
SubscribedTopicsByUnique.Add(unique, new SubscribedTopic(topicId, serverUrl, username, password, listenTask, listenCanceller));
|
SubscribedTopicsByUnique.Add(unique, new SubscribedTopic(topicId, serverUrl, username, password, listenTask, listenCanceller));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SubscribeToTopicUsingWebsocket(string unique, string topicId, string serverUrl, string? username, string? password)
|
||||||
|
{
|
||||||
|
if (_isDisposed) throw new ObjectDisposedException(nameof(NotificationListener));
|
||||||
|
|
||||||
|
if (SubscribedTopicsByUnique.ContainsKey(unique)) throw new InvalidOperationException("A topic with this unique already exists");
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(username)) username = null;
|
||||||
|
if (string.IsNullOrWhiteSpace(password)) password = null;
|
||||||
|
|
||||||
|
Uri targetUri = new($"{serverUrl}/{HttpUtility.UrlEncode(topicId)}/ws");
|
||||||
|
CancellationTokenSource listenCanceller = new();
|
||||||
|
Task listenTask = ListenToTopicWithWebsocketAsync(targetUri, new NetworkCredential(username, password), listenCanceller.Token);
|
||||||
|
SubscribedTopicsByUnique.Add(unique, new SubscribedTopic(topicId, serverUrl, username, password, listenTask, listenCanceller));
|
||||||
|
}
|
||||||
|
|
||||||
public async Task UnsubscribeFromTopicAsync(string topicUniqueString)
|
public async Task UnsubscribeFromTopicAsync(string topicUniqueString)
|
||||||
{
|
{
|
||||||
if (_isDisposed) throw new ObjectDisposedException(nameof(NotificationListener));
|
if (_isDisposed) throw new ObjectDisposedException(nameof(NotificationListener));
|
||||||
|
94
ntfysh_client/SubscribeDialog.Designer.cs
generated
94
ntfysh_client/SubscribeDialog.Designer.cs
generated
@@ -40,6 +40,8 @@ namespace ntfysh_client
|
|||||||
this.label3 = new System.Windows.Forms.Label();
|
this.label3 = new System.Windows.Forms.Label();
|
||||||
this.password = new System.Windows.Forms.TextBox();
|
this.password = new System.Windows.Forms.TextBox();
|
||||||
this.label4 = new System.Windows.Forms.Label();
|
this.label4 = new System.Windows.Forms.Label();
|
||||||
|
this.label5 = new System.Windows.Forms.Label();
|
||||||
|
this.connectionType = new System.Windows.Forms.ComboBox();
|
||||||
this.panel1.SuspendLayout();
|
this.panel1.SuspendLayout();
|
||||||
this.SuspendLayout();
|
this.SuspendLayout();
|
||||||
//
|
//
|
||||||
@@ -49,18 +51,19 @@ namespace ntfysh_client
|
|||||||
this.panel1.Controls.Add(this.button2);
|
this.panel1.Controls.Add(this.button2);
|
||||||
this.panel1.Controls.Add(this.button1);
|
this.panel1.Controls.Add(this.button1);
|
||||||
this.panel1.Dock = System.Windows.Forms.DockStyle.Bottom;
|
this.panel1.Dock = System.Windows.Forms.DockStyle.Bottom;
|
||||||
this.panel1.Location = new System.Drawing.Point(0, 175);
|
this.panel1.Location = new System.Drawing.Point(0, 244);
|
||||||
|
this.panel1.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||||
this.panel1.Name = "panel1";
|
this.panel1.Name = "panel1";
|
||||||
this.panel1.Size = new System.Drawing.Size(297, 44);
|
this.panel1.Size = new System.Drawing.Size(346, 51);
|
||||||
this.panel1.TabIndex = 0;
|
this.panel1.TabIndex = 0;
|
||||||
//
|
//
|
||||||
// button2
|
// button2
|
||||||
//
|
//
|
||||||
this.button2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
this.button2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
||||||
this.button2.Location = new System.Drawing.Point(131, 11);
|
this.button2.Location = new System.Drawing.Point(153, 13);
|
||||||
this.button2.Margin = new System.Windows.Forms.Padding(10, 10, 3, 10);
|
this.button2.Margin = new System.Windows.Forms.Padding(12, 12, 4, 12);
|
||||||
this.button2.Name = "button2";
|
this.button2.Name = "button2";
|
||||||
this.button2.Size = new System.Drawing.Size(75, 23);
|
this.button2.Size = new System.Drawing.Size(88, 27);
|
||||||
this.button2.TabIndex = 1;
|
this.button2.TabIndex = 1;
|
||||||
this.button2.Text = "Cancel";
|
this.button2.Text = "Cancel";
|
||||||
this.button2.UseVisualStyleBackColor = true;
|
this.button2.UseVisualStyleBackColor = true;
|
||||||
@@ -69,10 +72,10 @@ namespace ntfysh_client
|
|||||||
// button1
|
// button1
|
||||||
//
|
//
|
||||||
this.button1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
this.button1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
||||||
this.button1.Location = new System.Drawing.Point(212, 11);
|
this.button1.Location = new System.Drawing.Point(247, 13);
|
||||||
this.button1.Margin = new System.Windows.Forms.Padding(3, 10, 10, 10);
|
this.button1.Margin = new System.Windows.Forms.Padding(4, 12, 12, 12);
|
||||||
this.button1.Name = "button1";
|
this.button1.Name = "button1";
|
||||||
this.button1.Size = new System.Drawing.Size(75, 23);
|
this.button1.Size = new System.Drawing.Size(88, 27);
|
||||||
this.button1.TabIndex = 2;
|
this.button1.TabIndex = 2;
|
||||||
this.button1.Text = "Subscribe";
|
this.button1.Text = "Subscribe";
|
||||||
this.button1.UseVisualStyleBackColor = true;
|
this.button1.UseVisualStyleBackColor = true;
|
||||||
@@ -81,9 +84,10 @@ namespace ntfysh_client
|
|||||||
// label1
|
// label1
|
||||||
//
|
//
|
||||||
this.label1.AutoSize = true;
|
this.label1.AutoSize = true;
|
||||||
this.label1.Location = new System.Drawing.Point(12, 9);
|
this.label1.Location = new System.Drawing.Point(14, 10);
|
||||||
|
this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
|
||||||
this.label1.Name = "label1";
|
this.label1.Name = "label1";
|
||||||
this.label1.Size = new System.Drawing.Size(51, 13);
|
this.label1.Size = new System.Drawing.Size(52, 15);
|
||||||
this.label1.TabIndex = 1;
|
this.label1.TabIndex = 1;
|
||||||
this.label1.Text = "Topic ID:";
|
this.label1.Text = "Topic ID:";
|
||||||
//
|
//
|
||||||
@@ -91,9 +95,10 @@ namespace ntfysh_client
|
|||||||
//
|
//
|
||||||
this.topicId.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
this.topicId.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||||
| System.Windows.Forms.AnchorStyles.Right)));
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
this.topicId.Location = new System.Drawing.Point(12, 25);
|
this.topicId.Location = new System.Drawing.Point(14, 29);
|
||||||
|
this.topicId.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||||
this.topicId.Name = "topicId";
|
this.topicId.Name = "topicId";
|
||||||
this.topicId.Size = new System.Drawing.Size(273, 20);
|
this.topicId.Size = new System.Drawing.Size(318, 23);
|
||||||
this.topicId.TabIndex = 0;
|
this.topicId.TabIndex = 0;
|
||||||
this.topicId.KeyDown += new System.Windows.Forms.KeyEventHandler(this.topicId_KeyDown);
|
this.topicId.KeyDown += new System.Windows.Forms.KeyEventHandler(this.topicId_KeyDown);
|
||||||
//
|
//
|
||||||
@@ -101,19 +106,21 @@ namespace ntfysh_client
|
|||||||
//
|
//
|
||||||
this.serverUrl.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
this.serverUrl.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||||
| System.Windows.Forms.AnchorStyles.Right)));
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
this.serverUrl.Location = new System.Drawing.Point(12, 64);
|
this.serverUrl.Location = new System.Drawing.Point(14, 74);
|
||||||
|
this.serverUrl.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||||
this.serverUrl.Name = "serverUrl";
|
this.serverUrl.Name = "serverUrl";
|
||||||
this.serverUrl.Size = new System.Drawing.Size(273, 20);
|
this.serverUrl.Size = new System.Drawing.Size(318, 23);
|
||||||
this.serverUrl.TabIndex = 2;
|
this.serverUrl.TabIndex = 2;
|
||||||
this.serverUrl.Text = "https://ntfy.sh";
|
this.serverUrl.Text = "wss://ntfy.sh";
|
||||||
this.serverUrl.KeyDown += new System.Windows.Forms.KeyEventHandler(this.serverUrl_KeyDown);
|
this.serverUrl.KeyDown += new System.Windows.Forms.KeyEventHandler(this.serverUrl_KeyDown);
|
||||||
//
|
//
|
||||||
// label2
|
// label2
|
||||||
//
|
//
|
||||||
this.label2.AutoSize = true;
|
this.label2.AutoSize = true;
|
||||||
this.label2.Location = new System.Drawing.Point(10, 48);
|
this.label2.Location = new System.Drawing.Point(12, 55);
|
||||||
|
this.label2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
|
||||||
this.label2.Name = "label2";
|
this.label2.Name = "label2";
|
||||||
this.label2.Size = new System.Drawing.Size(66, 13);
|
this.label2.Size = new System.Drawing.Size(66, 15);
|
||||||
this.label2.TabIndex = 3;
|
this.label2.TabIndex = 3;
|
||||||
this.label2.Text = "Server URL:";
|
this.label2.Text = "Server URL:";
|
||||||
//
|
//
|
||||||
@@ -121,18 +128,20 @@ namespace ntfysh_client
|
|||||||
//
|
//
|
||||||
this.username.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
this.username.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||||
| System.Windows.Forms.AnchorStyles.Right)));
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
this.username.Location = new System.Drawing.Point(12, 103);
|
this.username.Location = new System.Drawing.Point(14, 119);
|
||||||
|
this.username.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||||
this.username.Name = "username";
|
this.username.Name = "username";
|
||||||
this.username.Size = new System.Drawing.Size(273, 20);
|
this.username.Size = new System.Drawing.Size(318, 23);
|
||||||
this.username.TabIndex = 4;
|
this.username.TabIndex = 4;
|
||||||
this.username.KeyDown += new System.Windows.Forms.KeyEventHandler(this.username_KeyDown);
|
this.username.KeyDown += new System.Windows.Forms.KeyEventHandler(this.username_KeyDown);
|
||||||
//
|
//
|
||||||
// label3
|
// label3
|
||||||
//
|
//
|
||||||
this.label3.AutoSize = true;
|
this.label3.AutoSize = true;
|
||||||
this.label3.Location = new System.Drawing.Point(10, 87);
|
this.label3.Location = new System.Drawing.Point(12, 100);
|
||||||
|
this.label3.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
|
||||||
this.label3.Name = "label3";
|
this.label3.Name = "label3";
|
||||||
this.label3.Size = new System.Drawing.Size(58, 13);
|
this.label3.Size = new System.Drawing.Size(63, 15);
|
||||||
this.label3.TabIndex = 5;
|
this.label3.TabIndex = 5;
|
||||||
this.label3.Text = "Username:";
|
this.label3.Text = "Username:";
|
||||||
//
|
//
|
||||||
@@ -140,9 +149,10 @@ namespace ntfysh_client
|
|||||||
//
|
//
|
||||||
this.password.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
this.password.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||||
| System.Windows.Forms.AnchorStyles.Right)));
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
this.password.Location = new System.Drawing.Point(12, 142);
|
this.password.Location = new System.Drawing.Point(14, 164);
|
||||||
|
this.password.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||||
this.password.Name = "password";
|
this.password.Name = "password";
|
||||||
this.password.Size = new System.Drawing.Size(273, 20);
|
this.password.Size = new System.Drawing.Size(318, 23);
|
||||||
this.password.TabIndex = 6;
|
this.password.TabIndex = 6;
|
||||||
this.password.UseSystemPasswordChar = true;
|
this.password.UseSystemPasswordChar = true;
|
||||||
this.password.KeyDown += new System.Windows.Forms.KeyEventHandler(this.password_KeyDown);
|
this.password.KeyDown += new System.Windows.Forms.KeyEventHandler(this.password_KeyDown);
|
||||||
@@ -150,18 +160,44 @@ namespace ntfysh_client
|
|||||||
// label4
|
// label4
|
||||||
//
|
//
|
||||||
this.label4.AutoSize = true;
|
this.label4.AutoSize = true;
|
||||||
this.label4.Location = new System.Drawing.Point(10, 126);
|
this.label4.Location = new System.Drawing.Point(12, 145);
|
||||||
|
this.label4.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
|
||||||
this.label4.Name = "label4";
|
this.label4.Name = "label4";
|
||||||
this.label4.Size = new System.Drawing.Size(56, 13);
|
this.label4.Size = new System.Drawing.Size(60, 15);
|
||||||
this.label4.TabIndex = 7;
|
this.label4.TabIndex = 7;
|
||||||
this.label4.Text = "Password:";
|
this.label4.Text = "Password:";
|
||||||
//
|
//
|
||||||
|
// label5
|
||||||
|
//
|
||||||
|
this.label5.AutoSize = true;
|
||||||
|
this.label5.Location = new System.Drawing.Point(12, 190);
|
||||||
|
this.label5.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
|
||||||
|
this.label5.Name = "label5";
|
||||||
|
this.label5.Size = new System.Drawing.Size(99, 15);
|
||||||
|
this.label5.TabIndex = 9;
|
||||||
|
this.label5.Text = "Connection Type:";
|
||||||
|
//
|
||||||
|
// connectionType
|
||||||
|
//
|
||||||
|
this.connectionType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||||
|
this.connectionType.FormattingEnabled = true;
|
||||||
|
this.connectionType.Items.AddRange(new object[] {
|
||||||
|
"Websockets (Recommended)",
|
||||||
|
"Long HTTP JSON (Robust)"});
|
||||||
|
this.connectionType.Location = new System.Drawing.Point(14, 208);
|
||||||
|
this.connectionType.Name = "connectionType";
|
||||||
|
this.connectionType.Size = new System.Drawing.Size(318, 23);
|
||||||
|
this.connectionType.TabIndex = 10;
|
||||||
|
this.connectionType.TextChanged += new System.EventHandler(this.connectionType_TextChanged);
|
||||||
|
//
|
||||||
// SubscribeDialog
|
// SubscribeDialog
|
||||||
//
|
//
|
||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
|
||||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
this.BackColor = System.Drawing.Color.White;
|
this.BackColor = System.Drawing.Color.White;
|
||||||
this.ClientSize = new System.Drawing.Size(297, 219);
|
this.ClientSize = new System.Drawing.Size(346, 295);
|
||||||
|
this.Controls.Add(this.connectionType);
|
||||||
|
this.Controls.Add(this.label5);
|
||||||
this.Controls.Add(this.password);
|
this.Controls.Add(this.password);
|
||||||
this.Controls.Add(this.label4);
|
this.Controls.Add(this.label4);
|
||||||
this.Controls.Add(this.username);
|
this.Controls.Add(this.username);
|
||||||
@@ -172,6 +208,7 @@ namespace ntfysh_client
|
|||||||
this.Controls.Add(this.label1);
|
this.Controls.Add(this.label1);
|
||||||
this.Controls.Add(this.panel1);
|
this.Controls.Add(this.panel1);
|
||||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
|
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
|
||||||
|
this.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||||
this.MaximizeBox = false;
|
this.MaximizeBox = false;
|
||||||
this.MinimizeBox = false;
|
this.MinimizeBox = false;
|
||||||
this.Name = "SubscribeDialog";
|
this.Name = "SubscribeDialog";
|
||||||
@@ -179,6 +216,7 @@ namespace ntfysh_client
|
|||||||
this.ShowInTaskbar = false;
|
this.ShowInTaskbar = false;
|
||||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||||
this.Text = "Subscribe to new topic";
|
this.Text = "Subscribe to new topic";
|
||||||
|
this.Load += new System.EventHandler(this.SubscribeDialog_Load);
|
||||||
this.panel1.ResumeLayout(false);
|
this.panel1.ResumeLayout(false);
|
||||||
this.ResumeLayout(false);
|
this.ResumeLayout(false);
|
||||||
this.PerformLayout();
|
this.PerformLayout();
|
||||||
@@ -199,5 +237,7 @@ namespace ntfysh_client
|
|||||||
private System.Windows.Forms.Label label3;
|
private System.Windows.Forms.Label label3;
|
||||||
private System.Windows.Forms.TextBox password;
|
private System.Windows.Forms.TextBox password;
|
||||||
private System.Windows.Forms.Label label4;
|
private System.Windows.Forms.Label label4;
|
||||||
|
private System.Windows.Forms.Label label5;
|
||||||
|
private System.Windows.Forms.ComboBox connectionType;
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -17,12 +17,107 @@ namespace ntfysh_client
|
|||||||
|
|
||||||
public string Unique => $"{topicId.Text}@{serverUrl.Text}";
|
public string Unique => $"{topicId.Text}@{serverUrl.Text}";
|
||||||
|
|
||||||
|
public bool UseWebsockets
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
switch (connectionType.Text)
|
||||||
|
{
|
||||||
|
case "Websockets (Recommended)":
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case "Long HTTP JSON (Robust)":
|
||||||
|
return false;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public SubscribeDialog(ListBox notificationTopics)
|
public SubscribeDialog(ListBox notificationTopics)
|
||||||
{
|
{
|
||||||
_notificationTopics = notificationTopics;
|
_notificationTopics = notificationTopics;
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void SubscribeDialog_Load(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
connectionType.SelectedIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ReparseAddress()
|
||||||
|
{
|
||||||
|
//Separate schema and address
|
||||||
|
string[] parts = serverUrl.Text.Split("://", 2);
|
||||||
|
|
||||||
|
//Validate the basic formatting is correct
|
||||||
|
if (parts.Length != 2) return false;
|
||||||
|
|
||||||
|
//Take the schema aside for parsing
|
||||||
|
string schema = parts[0].ToLower();
|
||||||
|
|
||||||
|
//Ensure the schema is actually valid
|
||||||
|
switch (schema)
|
||||||
|
{
|
||||||
|
case "http":
|
||||||
|
case "https":
|
||||||
|
case "ws":
|
||||||
|
case "wss":
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Correct the schema based on connection type if required
|
||||||
|
if (UseWebsockets)
|
||||||
|
{
|
||||||
|
switch (schema)
|
||||||
|
{
|
||||||
|
case "http":
|
||||||
|
schema = "ws";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "https":
|
||||||
|
schema = "wss";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "ws":
|
||||||
|
case "wss":
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (schema)
|
||||||
|
{
|
||||||
|
case "ws":
|
||||||
|
schema = "http";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "wss":
|
||||||
|
schema = "https";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "http":
|
||||||
|
case "https":
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Reconstruct the address
|
||||||
|
string finalAddress = schema + "://" + parts[1];
|
||||||
|
|
||||||
|
//Validate the address
|
||||||
|
if (!Uri.IsWellFormedUriString(finalAddress, UriKind.Absolute)) return false;
|
||||||
|
|
||||||
|
//Set the final address and OK it
|
||||||
|
serverUrl.Text = finalAddress;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private void button1_Click(object sender, EventArgs e)
|
private void button1_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
if (topicId.Text.Length < 1)
|
if (topicId.Text.Length < 1)
|
||||||
@@ -35,7 +130,7 @@ namespace ntfysh_client
|
|||||||
|
|
||||||
if (serverUrl.Text.Length < 1)
|
if (serverUrl.Text.Length < 1)
|
||||||
{
|
{
|
||||||
MessageBox.Show("You must specify a server URL. The default is https://ntfy.sh", "Server URL not specified", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
MessageBox.Show("You must specify a server URL. The default is wss://ntfy.sh", "Server URL not specified", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
DialogResult = DialogResult.None;
|
DialogResult = DialogResult.None;
|
||||||
serverUrl.Focus();
|
serverUrl.Focus();
|
||||||
return;
|
return;
|
||||||
@@ -65,6 +160,26 @@ namespace ntfysh_client
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!ReparseAddress())
|
||||||
|
{
|
||||||
|
MessageBox.Show($"The specified server URL is invalid. Accepted schemas are: http:// https:// ws:// wss://", "Invalid Server URL", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
DialogResult = DialogResult.None;
|
||||||
|
connectionType.Focus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException)
|
||||||
|
{
|
||||||
|
MessageBox.Show($"The selected Connection Type '{connectionType.Text}' is invalid.", "Invalid Connection Type", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
DialogResult = DialogResult.None;
|
||||||
|
connectionType.Focus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
DialogResult = DialogResult.OK;
|
DialogResult = DialogResult.OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,5 +223,10 @@ namespace ntfysh_client
|
|||||||
e.SuppressKeyPress = true;
|
e.SuppressKeyPress = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void connectionType_TextChanged(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
ReparseAddress();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,64 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<root>
|
||||||
<root>
|
|
||||||
<!--
|
|
||||||
Microsoft ResX Schema
|
|
||||||
|
|
||||||
Version 2.0
|
|
||||||
|
|
||||||
The primary goals of this format is to allow a simple XML format
|
|
||||||
that is mostly human readable. The generation and parsing of the
|
|
||||||
various data types are done through the TypeConverter classes
|
|
||||||
associated with the data types.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
... ado.net/XML headers & schema ...
|
|
||||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
|
||||||
<resheader name="version">2.0</resheader>
|
|
||||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
|
||||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
|
||||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
|
||||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
|
||||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
|
||||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
|
||||||
</data>
|
|
||||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
|
||||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
|
||||||
<comment>This is a comment</comment>
|
|
||||||
</data>
|
|
||||||
|
|
||||||
There are any number of "resheader" rows that contain simple
|
|
||||||
name/value pairs.
|
|
||||||
|
|
||||||
Each data row contains a name, and value. The row also contains a
|
|
||||||
type or mimetype. Type corresponds to a .NET class that support
|
|
||||||
text/value conversion through the TypeConverter architecture.
|
|
||||||
Classes that don't support this are serialized and stored with the
|
|
||||||
mimetype set.
|
|
||||||
|
|
||||||
The mimetype is used for serialized objects, and tells the
|
|
||||||
ResXResourceReader how to depersist the object. This is currently not
|
|
||||||
extensible. For a given mimetype the value must be set accordingly:
|
|
||||||
|
|
||||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
|
||||||
that the ResXResourceWriter will generate, however the reader can
|
|
||||||
read any of the formats listed below.
|
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.binary.base64
|
|
||||||
value : The object must be serialized with
|
|
||||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
|
||||||
: and then encoded with base64 encoding.
|
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.soap.base64
|
|
||||||
value : The object must be serialized with
|
|
||||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
|
||||||
: and then encoded with base64 encoding.
|
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
|
||||||
value : The object must be serialized into a byte array
|
|
||||||
: using a System.ComponentModel.TypeConverter
|
|
||||||
: and then encoded with base64 encoding.
|
|
||||||
-->
|
|
||||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
<xsd:element name="root" msdata:IsDataSet="true">
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
Reference in New Issue
Block a user