diff --git a/Builder/IISMainHandler/build.txt b/Builder/IISMainHandler/build.txt
index 755ab3c..0316458 100644
--- a/Builder/IISMainHandler/build.txt
+++ b/Builder/IISMainHandler/build.txt
@@ -1 +1 @@
-495
\ No newline at end of file
+503
\ No newline at end of file
diff --git a/Builder/IISUploadHandler/build.txt b/Builder/IISUploadHandler/build.txt
index 565f1b0..34578cc 100644
--- a/Builder/IISUploadHandler/build.txt
+++ b/Builder/IISUploadHandler/build.txt
@@ -1 +1 @@
-231
\ No newline at end of file
+239
\ No newline at end of file
diff --git a/Common/BBCodes/B.cs b/Common/BBCodes/B.cs
new file mode 100644
index 0000000..5197f1c
--- /dev/null
+++ b/Common/BBCodes/B.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using PJonDevelopment.BBCode;
+
+namespace FLocal.Common.BBCodes {
+ class B : BBCode {
+
+ public B()
+ : base("b") {
+ }
+
+ public override string Format(ITextFormatter formatter) {
+ return "" + this.GetInnerHTML(formatter) + "";
+ }
+
+ }
+}
diff --git a/Common/BBCodes/Code.cs b/Common/BBCodes/Code.cs
new file mode 100644
index 0000000..83ad886
--- /dev/null
+++ b/Common/BBCodes/Code.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using PJonDevelopment.BBCode;
+
+namespace FLocal.Common.BBCodes {
+ class Code : BBCode {
+
+ public Code()
+ : base("code") {
+ }
+
+ public override string Format(ITextFormatter formatter) {
+ return "
" + this.InnerBBCode + "
";
+ }
+
+ }
+}
diff --git a/Common/BBCodes/FUrl.cs b/Common/BBCodes/FUrl.cs
new file mode 100644
index 0000000..8f82c6c
--- /dev/null
+++ b/Common/BBCodes/FUrl.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using PJonDevelopment.BBCode;
+
+namespace FLocal.Common.BBCodes {
+ class FUrl : BBCode {
+
+ public FUrl()
+ : base("furl") {
+ }
+
+ public override string Format(ITextFormatter formatter) {
+ string rawUrl = this.Default;
+ if(rawUrl == null) {
+ // throw new ApplicationException(String.Join("; ", (from kvp in this.Attributes select kvp.Key + "=" + kvp.Value).ToArray()));
+ rawUrl = this.InnerText;
+ }
+ Uri uri = new Uri(rawUrl);
+ return "" + this.GetInnerHTML(formatter) + "";
+ }
+
+ }
+}
diff --git a/Common/BBCodes/I.cs b/Common/BBCodes/I.cs
new file mode 100644
index 0000000..2241b5b
--- /dev/null
+++ b/Common/BBCodes/I.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using PJonDevelopment.BBCode;
+
+namespace FLocal.Common.BBCodes {
+ class I : BBCode {
+
+ public I()
+ : base("i") {
+ }
+
+ public override string Format(ITextFormatter formatter) {
+ return "" + this.GetInnerHTML(formatter) + "";
+ }
+
+ }
+}
diff --git a/Common/BBCodes/Image.cs b/Common/BBCodes/Image.cs
new file mode 100644
index 0000000..fc0562c
--- /dev/null
+++ b/Common/BBCodes/Image.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using PJonDevelopment.BBCode;
+
+namespace FLocal.Common.BBCodes {
+ class Image : BBCode {
+
+ public Image() : base("image") {
+ }
+
+ public override string Format(ITextFormatter formatter) {
+ var urlInfo = UrlProcessor.Process(this.InnerText);
+ if (urlInfo.isLocal && urlInfo.relativeUrl.StartsWith("/user/upload/")) {
+ return "" + urlInfo.relativeUrl + "" + urlInfo.relativeUrl + "";
+ } else {
+ return "" + urlInfo.relativeUrl + "";
+ }
+ }
+
+ }
+}
diff --git a/Common/BBCodes/S.cs b/Common/BBCodes/S.cs
new file mode 100644
index 0000000..76796cb
--- /dev/null
+++ b/Common/BBCodes/S.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using PJonDevelopment.BBCode;
+
+namespace FLocal.Common.BBCodes {
+ class S : BBCode {
+
+ public S()
+ : base("s") {
+ }
+
+ public override string Format(ITextFormatter formatter) {
+ return "" + this.GetInnerHTML(formatter) + "";
+ }
+
+ }
+}
diff --git a/Common/BBCodes/U.cs b/Common/BBCodes/U.cs
new file mode 100644
index 0000000..8548211
--- /dev/null
+++ b/Common/BBCodes/U.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using PJonDevelopment.BBCode;
+
+namespace FLocal.Common.BBCodes {
+ class U : BBCode {
+
+ public U()
+ : base("u") {
+ }
+
+ public override string Format(ITextFormatter formatter) {
+ return "" + this.GetInnerHTML(formatter) + "";
+ }
+
+ }
+}
diff --git a/Common/BBCodes/Url.cs b/Common/BBCodes/Url.cs
new file mode 100644
index 0000000..521fcd2
--- /dev/null
+++ b/Common/BBCodes/Url.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using PJonDevelopment.BBCode;
+
+namespace FLocal.Common.BBCodes {
+ class Url : BBCode {
+
+ public Url()
+ : base("url") {
+ }
+
+ public override string Format(ITextFormatter formatter) {
+ string rawUrl = this.Default;
+ if(rawUrl == null) {
+// throw new ApplicationException(String.Join("; ", (from kvp in this.Attributes select kvp.Key + "=" + kvp.Value).ToArray()));
+ rawUrl = this.InnerText;
+ }
+ var urlInfo = UrlProcessor.Process(rawUrl);
+ return "" + this.GetInnerHTML(formatter) + "";
+ }
+
+ }
+}
diff --git a/Common/BBCodes/helpers/BBCode.cs b/Common/BBCodes/helpers/BBCode.cs
new file mode 100644
index 0000000..552dd5c
--- /dev/null
+++ b/Common/BBCodes/helpers/BBCode.cs
@@ -0,0 +1,35 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace FLocal.Common.BBCodes {
+ abstract class BBCode : PJonDevelopment.BBCode.BBCodeElement {
+
+ public BBCode(string name)
+ : base(name) {
+ }
+
+ protected string GetInnerHTML(PJonDevelopment.BBCode.ITextFormatter formatter) {
+ StringBuilder builder = new StringBuilder();
+ foreach (var node in this.Nodes) {
+ builder.Append(node.Format(formatter));
+ }
+ return builder.ToString();
+ }
+
+ protected string Default {
+ get {
+ if(!this.Attributes.ContainsKey("DEFAULT")) {
+ return null;
+ }
+ string result = this.Attributes["DEFAULT"];
+ if(result == null || result == "") {
+ return null;
+ }
+ return result;
+ }
+ }
+
+ }
+}
diff --git a/Common/BBCodes/helpers/UrlProcessor.cs b/Common/BBCodes/helpers/UrlProcessor.cs
new file mode 100644
index 0000000..11f71f5
--- /dev/null
+++ b/Common/BBCodes/helpers/UrlProcessor.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Web;
+
+namespace FLocal.Common.BBCodes {
+ class UrlProcessor {
+
+ public struct UrlInfo {
+ public readonly bool isLocal;
+ private readonly string rawRelativeUrl;
+ public string relativeUrl {
+ get {
+ return this.rawRelativeUrl;
+ //return HttpUtility.UrlEncode(this.rawRelativeUrl);
+ }
+ }
+ public UrlInfo(bool isLocal, string relativeUrl) {
+ this.isLocal = isLocal;
+ this.rawRelativeUrl = relativeUrl;
+ }
+ }
+
+ public static HashSet KnownAliases = new HashSet {
+ "forum.local",
+ "forum.b.gz.ru",
+ "www.snto-msu.net",
+ "hq.sectorb.msk.ru",
+ "petaflop.b.gz.ru",
+ "194.88.210.5",
+ "forumlocal.ru",
+ "forumbgz.ru",
+ "forum.hn",
+ };
+
+ public const string DOMAIN = ".forum.hn";
+
+ public static UrlInfo Process(string url) {
+ if (url.StartsWith("/")) {
+ return new UrlInfo(true, url);
+ }
+ Uri uri;
+ try {
+ uri = new Uri(url);
+ } catch(UriFormatException) {
+ throw new Core.FLocalException("wrong url: " + url);
+ }
+ if (KnownAliases.Contains(uri.Host.ToLower()) || uri.Host.ToLower().EndsWith(DOMAIN)) {
+ return new UrlInfo(true, uri.PathAndQuery);
+ } else {
+ return new UrlInfo(false, uri.ToString());
+ }
+ }
+
+ }
+}
diff --git a/Common/Common.csproj b/Common/Common.csproj
index 9a50095..518e1e4 100644
--- a/Common/Common.csproj
+++ b/Common/Common.csproj
@@ -57,6 +57,15 @@
+
+
+
+
+
+
+
+
+
@@ -79,6 +88,7 @@
+
@@ -92,6 +102,10 @@
{E38DE5B1-F9C2-43BA-A5DF-0743ABD4DFC7}
MySQLConnector
+
+ {ACDDD5D1-119F-4B82-8FE6-6515C812795B}
+ BBCode
+