* Error handling on URL additions in embeds and components. * Wording on exception comment * Wording on exceptions * changed IsWellFormatted to validating urls start with protocol. May not keep all url validation, may just keep image-based url validation as those definitely only require http/s. * Helper utility made for url validation * xml Co-authored-by: Quin Lynch <49576606+quinchs@users.noreply.github.com>pull/1923/head
| @@ -11643,5 +11643,13 @@ | |||||
| <exception cref="T:System.ArgumentNullException"> Thrown when the supplied token string is <c>null</c>, empty, or contains only whitespace.</exception> | <exception cref="T:System.ArgumentNullException"> Thrown when the supplied token string is <c>null</c>, empty, or contains only whitespace.</exception> | ||||
| <exception cref="T:System.ArgumentException"> Thrown when the supplied <see cref="T:Discord.TokenType"/> or token value is invalid. </exception> | <exception cref="T:System.ArgumentException"> Thrown when the supplied <see cref="T:Discord.TokenType"/> or token value is invalid. </exception> | ||||
| </member> | </member> | ||||
| <member name="M:Discord.Utils.UrlValidation.Validate(System.String)"> | |||||
| <summary> | |||||
| Not full URL validation right now. Just ensures protocol is present and that it's either http or https | |||||
| </summary> | |||||
| <param name="url">url to validate before sending to Discord</param> | |||||
| <exception cref="T:System.InvalidOperationException">A URL must include a protocol (http or https).</exception> | |||||
| <returns>true if url is valid by our standard, false if null, throws an error upon invalid </returns> | |||||
| </member> | |||||
| </members> | </members> | ||||
| </doc> | </doc> | ||||
| @@ -1,6 +1,7 @@ | |||||
| using System; | using System; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Linq; | using System.Linq; | ||||
| using Discord.Utils; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| @@ -535,12 +536,11 @@ namespace Discord | |||||
| if (this.Style == ButtonStyle.Link) | if (this.Style == ButtonStyle.Link) | ||||
| { | { | ||||
| if (string.IsNullOrEmpty(this.Url)) | |||||
| throw new InvalidOperationException("Link buttons must have a link associated with them"); | |||||
| else if (!Uri.IsWellFormedUriString(this.Url, UriKind.Absolute)) | |||||
| throw new InvalidOperationException("Urls must be well formatted and include their protocol (either HTTP or HTTPS)"); | |||||
| } | |||||
| if (string.IsNullOrEmpty(this.Url)) | |||||
| throw new InvalidOperationException("Link buttons must have a link associated with them"); | |||||
| else | |||||
| UrlValidation.Validate(this.Url); | |||||
| } | |||||
| else if (string.IsNullOrEmpty(this.CustomId)) | else if (string.IsNullOrEmpty(this.CustomId)) | ||||
| throw new InvalidOperationException("Non-link buttons must have a custom id associated with them"); | throw new InvalidOperationException("Non-link buttons must have a custom id associated with them"); | ||||
| @@ -2,6 +2,7 @@ using System; | |||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Collections.Immutable; | using System.Collections.Immutable; | ||||
| using System.Linq; | using System.Linq; | ||||
| using Discord.Utils; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| @@ -401,25 +402,29 @@ namespace Discord | |||||
| /// The built embed object. | /// The built embed object. | ||||
| /// </returns> | /// </returns> | ||||
| /// <exception cref="InvalidOperationException">Total embed length exceeds <see cref="MaxEmbedLength"/>.</exception> | /// <exception cref="InvalidOperationException">Total embed length exceeds <see cref="MaxEmbedLength"/>.</exception> | ||||
| /// <exception cref="InvalidOperationException">Any Url must be well formatted include its protocols (i.e http:// or https://).</exception> | |||||
| /// <exception cref="InvalidOperationException">Any Url must include its protocols (i.e http:// or https://).</exception> | |||||
| public Embed Build() | public Embed Build() | ||||
| { | { | ||||
| if (Length > MaxEmbedLength) | if (Length > MaxEmbedLength) | ||||
| throw new InvalidOperationException($"Total embed length must be less than or equal to {MaxEmbedLength}."); | throw new InvalidOperationException($"Total embed length must be less than or equal to {MaxEmbedLength}."); | ||||
| if (!string.IsNullOrEmpty(Url) && !Uri.IsWellFormedUriString(Url, UriKind.Absolute)) | |||||
| throw new InvalidOperationException("Url must be well formatted and include its protocol (either HTTP or HTTPS)"); | |||||
| if (!string.IsNullOrEmpty(ThumbnailUrl) && !Uri.IsWellFormedUriString(ThumbnailUrl, UriKind.Absolute)) | |||||
| throw new InvalidOperationException("Thumbnail Url must be well formatted and include its protocol (either HTTP or HTTPS)"); | |||||
| if (!string.IsNullOrEmpty(ImageUrl) && !Uri.IsWellFormedUriString(ImageUrl, UriKind.Absolute)) | |||||
| throw new InvalidOperationException("Image Url must be well formatted and include its protocol (either HTTP or HTTPS)"); | |||||
| if (!string.IsNullOrEmpty(Url)) | |||||
| UrlValidation.Validate(Url); | |||||
| if (!string.IsNullOrEmpty(ThumbnailUrl)) | |||||
| UrlValidation.Validate(ThumbnailUrl); | |||||
| if (!string.IsNullOrEmpty(ImageUrl)) | |||||
| UrlValidation.Validate(ImageUrl); | |||||
| if (Author != null) | if (Author != null) | ||||
| { | { | ||||
| if(!string.IsNullOrEmpty(Author.Url) && !Uri.IsWellFormedUriString(Author.Url, UriKind.Absolute)) | |||||
| throw new InvalidOperationException("Author Url must be well formatted and include its protocol (either HTTP or HTTPS)"); | |||||
| if (!string.IsNullOrEmpty(Author.IconUrl) && !Uri.IsWellFormedUriString(Author.IconUrl, UriKind.Absolute)) | |||||
| throw new InvalidOperationException("Author Icon Url must be well formatted and include its protocol (either HTTP or HTTPS)"); | |||||
| if (!string.IsNullOrEmpty(Author.Url)) | |||||
| UrlValidation.Validate(Author.Url); | |||||
| if (!string.IsNullOrEmpty(Author.IconUrl)) | |||||
| UrlValidation.Validate(Author.IconUrl); | |||||
| } | |||||
| if(Footer != null) | |||||
| { | |||||
| if (!string.IsNullOrEmpty(Footer.IconUrl)) | |||||
| UrlValidation.Validate(Footer.IconUrl); | |||||
| } | } | ||||
| var fields = ImmutableArray.CreateBuilder<EmbedField>(Fields.Count); | var fields = ImmutableArray.CreateBuilder<EmbedField>(Fields.Count); | ||||
| for (int i = 0; i < Fields.Count; i++) | for (int i = 0; i < Fields.Count; i++) | ||||
| fields.Add(Fields[i].Build()); | fields.Add(Fields[i].Build()); | ||||
| @@ -0,0 +1,22 @@ | |||||
| using System; | |||||
| namespace Discord.Utils | |||||
| { | |||||
| static class UrlValidation | |||||
| { | |||||
| /// <summary> | |||||
| /// Not full URL validation right now. Just ensures protocol is present and that it's either http or https | |||||
| /// </summary> | |||||
| /// <param name="url">url to validate before sending to Discord</param> | |||||
| /// <exception cref="InvalidOperationException">A URL must include a protocol (http or https).</exception> | |||||
| /// <returns>true if url is valid by our standard, false if null, throws an error upon invalid </returns> | |||||
| public static bool Validate(string url) | |||||
| { | |||||
| if (string.IsNullOrEmpty(url)) | |||||
| return false; | |||||
| if(!(url.StartsWith("http://", StringComparison.OrdinalIgnoreCase) || (url.StartsWith("https://", StringComparison.OrdinalIgnoreCase)))) | |||||
| throw new InvalidOperationException($"Url {url} must be include its protocol (either HTTP or HTTPS)"); | |||||
| return true; | |||||
| } | |||||
| } | |||||
| } | |||||