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;
|
||||
|
||||
//Subscribe
|
||||
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
|
||||
notificationTopics.Items.Add(dialog.Unique);
|
||||
@@ -167,7 +174,24 @@ namespace ntfysh_client
|
||||
//Load them in
|
||||
foreach (SubscribedTopic topic in topics)
|
||||
{
|
||||
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}");
|
||||
}
|
||||
}
|
||||
|
@@ -31,7 +31,7 @@ namespace ntfysh_client
|
||||
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));
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
#if DEBUG
|
||||
@@ -120,11 +173,26 @@ namespace ntfysh_client
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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.password = new System.Windows.Forms.TextBox();
|
||||
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.SuspendLayout();
|
||||
//
|
||||
@@ -49,18 +51,19 @@ namespace ntfysh_client
|
||||
this.panel1.Controls.Add(this.button2);
|
||||
this.panel1.Controls.Add(this.button1);
|
||||
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.Size = new System.Drawing.Size(297, 44);
|
||||
this.panel1.Size = new System.Drawing.Size(346, 51);
|
||||
this.panel1.TabIndex = 0;
|
||||
//
|
||||
// button2
|
||||
//
|
||||
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.Margin = new System.Windows.Forms.Padding(10, 10, 3, 10);
|
||||
this.button2.Location = new System.Drawing.Point(153, 13);
|
||||
this.button2.Margin = new System.Windows.Forms.Padding(12, 12, 4, 12);
|
||||
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.Text = "Cancel";
|
||||
this.button2.UseVisualStyleBackColor = true;
|
||||
@@ -69,10 +72,10 @@ namespace ntfysh_client
|
||||
// button1
|
||||
//
|
||||
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.Margin = new System.Windows.Forms.Padding(3, 10, 10, 10);
|
||||
this.button1.Location = new System.Drawing.Point(247, 13);
|
||||
this.button1.Margin = new System.Windows.Forms.Padding(4, 12, 12, 12);
|
||||
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.Text = "Subscribe";
|
||||
this.button1.UseVisualStyleBackColor = true;
|
||||
@@ -81,9 +84,10 @@ namespace ntfysh_client
|
||||
// label1
|
||||
//
|
||||
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.Size = new System.Drawing.Size(51, 13);
|
||||
this.label1.Size = new System.Drawing.Size(52, 15);
|
||||
this.label1.TabIndex = 1;
|
||||
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)
|
||||
| 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.Size = new System.Drawing.Size(273, 20);
|
||||
this.topicId.Size = new System.Drawing.Size(318, 23);
|
||||
this.topicId.TabIndex = 0;
|
||||
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)
|
||||
| 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.Size = new System.Drawing.Size(273, 20);
|
||||
this.serverUrl.Size = new System.Drawing.Size(318, 23);
|
||||
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);
|
||||
//
|
||||
// label2
|
||||
//
|
||||
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.Size = new System.Drawing.Size(66, 13);
|
||||
this.label2.Size = new System.Drawing.Size(66, 15);
|
||||
this.label2.TabIndex = 3;
|
||||
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)
|
||||
| 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.Size = new System.Drawing.Size(273, 20);
|
||||
this.username.Size = new System.Drawing.Size(318, 23);
|
||||
this.username.TabIndex = 4;
|
||||
this.username.KeyDown += new System.Windows.Forms.KeyEventHandler(this.username_KeyDown);
|
||||
//
|
||||
// label3
|
||||
//
|
||||
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.Size = new System.Drawing.Size(58, 13);
|
||||
this.label3.Size = new System.Drawing.Size(63, 15);
|
||||
this.label3.TabIndex = 5;
|
||||
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)
|
||||
| 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.Size = new System.Drawing.Size(273, 20);
|
||||
this.password.Size = new System.Drawing.Size(318, 23);
|
||||
this.password.TabIndex = 6;
|
||||
this.password.UseSystemPasswordChar = true;
|
||||
this.password.KeyDown += new System.Windows.Forms.KeyEventHandler(this.password_KeyDown);
|
||||
@@ -150,18 +160,44 @@ namespace ntfysh_client
|
||||
// label4
|
||||
//
|
||||
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.Size = new System.Drawing.Size(56, 13);
|
||||
this.label4.Size = new System.Drawing.Size(60, 15);
|
||||
this.label4.TabIndex = 7;
|
||||
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
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
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.label4);
|
||||
this.Controls.Add(this.username);
|
||||
@@ -172,6 +208,7 @@ namespace ntfysh_client
|
||||
this.Controls.Add(this.label1);
|
||||
this.Controls.Add(this.panel1);
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
|
||||
this.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
this.MaximizeBox = false;
|
||||
this.MinimizeBox = false;
|
||||
this.Name = "SubscribeDialog";
|
||||
@@ -179,6 +216,7 @@ namespace ntfysh_client
|
||||
this.ShowInTaskbar = false;
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||
this.Text = "Subscribe to new topic";
|
||||
this.Load += new System.EventHandler(this.SubscribeDialog_Load);
|
||||
this.panel1.ResumeLayout(false);
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
@@ -199,5 +237,7 @@ namespace ntfysh_client
|
||||
private System.Windows.Forms.Label label3;
|
||||
private System.Windows.Forms.TextBox password;
|
||||
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 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)
|
||||
{
|
||||
_notificationTopics = notificationTopics;
|
||||
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)
|
||||
{
|
||||
if (topicId.Text.Length < 1)
|
||||
@@ -35,7 +130,7 @@ namespace ntfysh_client
|
||||
|
||||
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;
|
||||
serverUrl.Focus();
|
||||
return;
|
||||
@@ -65,6 +160,26 @@ namespace ntfysh_client
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -108,5 +223,10 @@ namespace ntfysh_client
|
||||
e.SuppressKeyPress = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void connectionType_TextChanged(object sender, EventArgs e)
|
||||
{
|
||||
ReparseAddress();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,64 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<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.
|
||||
-->
|
||||
<root>
|
||||
<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:element name="root" msdata:IsDataSet="true">
|
||||
|
Reference in New Issue
Block a user