User:John Ericson/AutoWikiBrowserHttpAuthAndSslToCustomProjects.patch

Adds features:

  • Add HTTP AUTH support for Wiki sites that are protected with for example a .htaccess file. A login dialog is displayed for entering username and password if the webserver returnes a HTTP 401 Unauthorized status code.
  • Also added SSL support to CustomProjects which allowes you to visit https:// sites. This is done by checking the "Use secure server" setting in Preferences.

Works with latest trunk of AutoWikiBrowser.

Index: AWB/AutoWikiBrowser.csproj
===================================================================
--- AWB/AutoWikiBrowser.csproj	(revision 7941)
+++ AWB/AutoWikiBrowser.csproj	(working copy)
@@ -100,6 +100,12 @@
     <Compile Include="ExitQuestion.Designer.cs">
       <DependentUpon>ExitQuestion.cs</DependentUpon>
     </Compile>
+    <Compile Include="Login.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="Login.Designer.cs">
+      <DependentUpon>Login.cs</DependentUpon>
+    </Compile>
     <Compile Include="TalkMessage.cs">
       <SubType>Form</SubType>
     </Compile>
@@ -184,6 +190,9 @@
       <SubType>Designer</SubType>
       <DependentUpon>CustomModule.cs</DependentUpon>
     </EmbeddedResource>
+    <EmbeddedResource Include="Login.resx">
+      <DependentUpon>Login.cs</DependentUpon>
+    </EmbeddedResource>
     <EmbeddedResource Include="TalkMessage.resx">
       <SubType>Designer</SubType>
       <DependentUpon>TalkMessage.cs</DependentUpon>
Index: AWB/Login.cs
===================================================================
--- AWB/Login.cs	(revision 0)
+++ AWB/Login.cs	(revision 0)
@@ -0,0 +1,51 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Text;
+using System.Windows.Forms;
+using WikiFunctions;
+
+namespace AutoWikiBrowser
+{
+    internal sealed partial class Login : Form
+    {
+        public Login()
+        {
+            InitializeComponent();
+		}
+
+		private void FormOnKeyUp(object sender, KeyEventArgs e)
+		{
+			if (e.KeyCode == Keys.Enter) {
+				CloseForm();
+			}
+		}
+
+        private void label1_Click(object sender, EventArgs e)
+        {
+
+        }
+
+        private void label2_Click(object sender, EventArgs e)
+        {
+
+        }
+
+		private void btnLogin_Click(object sender, EventArgs e)
+		{
+			CloseForm();
+		}
+
+		private void CloseForm() {
+			Variables.HttpAuthUsername = txtUsername.Text;
+			Variables.HttpAuthPassword = txtPassword.Text;
+			Close();
+		}
+
+		private void Login_Load(object sender, EventArgs e) {
+
+		}
+    }
+}
Index: AWB/Login.Designer.cs
===================================================================
--- AWB/Login.Designer.cs	(revision 0)
+++ AWB/Login.Designer.cs	(revision 0)
@@ -0,0 +1,127 @@
+namespace AutoWikiBrowser
+{
+    partial class Login
+    {
+        /// <summary>
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary>
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Windows Form Designer generated code
+
+        /// <summary>
+        /// Required method for Designer support - do not modify
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+			this.txtUsername = new System.Windows.Forms.TextBox();
+			this.lblDescription = new System.Windows.Forms.Label();
+			this.lblUsername = new System.Windows.Forms.Label();
+			this.lblPassword = new System.Windows.Forms.Label();
+			this.txtPassword = new System.Windows.Forms.TextBox();
+			this.btnLogin = new System.Windows.Forms.Button();
+			this.SuspendLayout();
+			// 
+			// txtUsername
+			// 
+			this.txtUsername.Location = new System.Drawing.Point(93, 62);
+			this.txtUsername.Name = "txtUsername";
+			this.txtUsername.Size = new System.Drawing.Size(152, 20);
+			this.txtUsername.TabIndex = 0;
+			this.txtUsername.KeyUp += new System.Windows.Forms.KeyEventHandler(this.FormOnKeyUp);
+			// 
+			// lblDescription
+			// 
+			this.lblDescription.Location = new System.Drawing.Point(12, 9);
+			this.lblDescription.Name = "lblDescription";
+			this.lblDescription.Size = new System.Drawing.Size(260, 39);
+			this.lblDescription.TabIndex = 1;
+			this.lblDescription.Text = "This wiki is protected using HTTP AUTH. Please login to proceed.";
+			this.lblDescription.Click += new System.EventHandler(this.label1_Click);
+			// 
+			// lblUsername
+			// 
+			this.lblUsername.AutoSize = true;
+			this.lblUsername.Location = new System.Drawing.Point(23, 62);
+			this.lblUsername.Name = "lblUsername";
+			this.lblUsername.Size = new System.Drawing.Size(58, 13);
+			this.lblUsername.TabIndex = 2;
+			this.lblUsername.Text = "Username:";
+			this.lblUsername.Click += new System.EventHandler(this.label2_Click);
+			// 
+			// lblPassword
+			// 
+			this.lblPassword.AutoSize = true;
+			this.lblPassword.Location = new System.Drawing.Point(23, 95);
+			this.lblPassword.Name = "lblPassword";
+			this.lblPassword.Size = new System.Drawing.Size(56, 13);
+			this.lblPassword.TabIndex = 4;
+			this.lblPassword.Text = "Password:";
+			// 
+			// txtPassword
+			// 
+			this.txtPassword.Location = new System.Drawing.Point(93, 95);
+			this.txtPassword.Name = "txtPassword";
+			this.txtPassword.PasswordChar = '*';
+			this.txtPassword.Size = new System.Drawing.Size(153, 20);
+			this.txtPassword.TabIndex = 3;
+			this.txtPassword.KeyUp += new System.Windows.Forms.KeyEventHandler(this.FormOnKeyUp);
+			// 
+			// btnLogin
+			// 
+			this.btnLogin.Location = new System.Drawing.Point(170, 140);
+			this.btnLogin.Name = "btnLogin";
+			this.btnLogin.Size = new System.Drawing.Size(75, 23);
+			this.btnLogin.TabIndex = 5;
+			this.btnLogin.Text = "Login";
+			this.btnLogin.UseVisualStyleBackColor = true;
+			this.btnLogin.Click += new System.EventHandler(this.btnLogin_Click);
+			// 
+			// Login
+			// 
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.ClientSize = new System.Drawing.Size(259, 175);
+			this.Controls.Add(this.btnLogin);
+			this.Controls.Add(this.lblPassword);
+			this.Controls.Add(this.txtPassword);
+			this.Controls.Add(this.lblUsername);
+			this.Controls.Add(this.lblDescription);
+			this.Controls.Add(this.txtUsername);
+			this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
+			this.MaximizeBox = false;
+			this.MinimizeBox = false;
+			this.Name = "Login";
+			this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
+			this.Text = "Login";
+			this.Load += new System.EventHandler(this.Login_Load);
+			this.KeyUp += new System.Windows.Forms.KeyEventHandler(this.FormOnKeyUp);
+			this.ResumeLayout(false);
+			this.PerformLayout();
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.TextBox txtUsername;
+        private System.Windows.Forms.Label lblDescription;
+        private System.Windows.Forms.Label lblUsername;
+        private System.Windows.Forms.Label lblPassword;
+        private System.Windows.Forms.TextBox txtPassword;
+        private System.Windows.Forms.Button btnLogin;
+    }
+}
\ No newline at end of file
Index: AWB/Login.resx
===================================================================
--- AWB/Login.resx	(revision 0)
+++ AWB/Login.resx	(revision 0)
@@ -0,0 +1,120 @@
+<?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.
+    -->
+  <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">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>
\ No newline at end of file
Index: AWB/Main.cs
===================================================================
--- AWB/Main.cs	(revision 7941)
+++ AWB/Main.cs	(working copy)
@@ -44,6 +44,7 @@
 using WikiFunctions.Controls.Lists;
 using AutoWikiBrowser.Plugins;
 using System.Web;
+using System.Net;
 
 namespace AutoWikiBrowser
 {
@@ -2863,9 +2864,29 @@
             SplashScreen.SetProgress(81);
             try
             {
-                //set namespaces
-                Variables.SetProject(code, project, customProject, usingSecure);
+                try
+                {
+                    //set namespaces
+                    Variables.SetProject(code, project, customProject, usingSecure);
+                }
+                catch (WebException ex) {
+					// Check for HTTP 401 error.
+					var resp = (HttpWebResponse)ex.Response;
+					if (resp == null) throw;
+					switch (resp.StatusCode) {
+						case HttpStatusCode.Unauthorized /*401*/:
+							ShowLogin();
+							
+							// Reload project.
+							Variables.SetProject(code, project, customProject, usingSecure);
 
+							break;
+					}
+                }
+                catch (Exception) {
+					throw;
+				}
+
                 if (Variables.TryLoadingAgainAfterLogin)
                 {
                     MessageBox.Show(
@@ -4716,6 +4737,12 @@
             chkMinor.Checked = markAllAsMinorToolStripMenuItem.Checked;
         }
 
+        private void ShowLogin()
+        {
+            Login login = new Login();
+            login.ShowDialog(this);
+		}
+
         #region Shutdown
         private void chkShutdown_CheckedChanged(object sender, EventArgs e)
         {
Index: AWB/Preferences.cs
===================================================================
--- AWB/Preferences.cs	(revision 7941)
+++ AWB/Preferences.cs	(working copy)
@@ -63,6 +63,7 @@
             cmboCustomProject.Text = customproj;
             
             PrefUsingSecure = usingSecure;
+			chkUsingSecure.Enabled = (cmboProject.Text != "wikia" ? true : false);
 
             PrefPHP5 = usePHP5;
 
@@ -187,8 +188,13 @@
                 cmboCustomProjectChanged(null, null);
 
                 chkSupressAWB.Enabled = true;
-                chkUsingSecure.Enabled = chkUsingSecure.Checked = false;
 
+				// Custom server can be SSL enabled.
+				if (prj == ProjectEnum.wikia)
+				{
+					chkUsingSecure.Enabled = chkUsingSecure.Checked = false;
+				}
+
                 chkPHP5Ext.Enabled = (prj == ProjectEnum.custom);
 
                 return;
Index: WikiFunctions/API/ApiEdit.cs
===================================================================
--- WikiFunctions/API/ApiEdit.cs	(revision 7941)
+++ WikiFunctions/API/ApiEdit.cs	(working copy)
@@ -25,6 +25,7 @@
 using System.Xml;
 using System.Threading;
 using System.Text.RegularExpressions;
+using System.Security.Cryptography.X509Certificates;
 
 namespace WikiFunctions.API
 {
@@ -70,7 +71,7 @@
             : this()
         {
             if (string.IsNullOrEmpty(url)) throw new ArgumentException("Invalid URL specified", "url");
-            if (!url.StartsWith("http://")) throw new NotSupportedException("Only editing via HTTP is currently supported");
+            //if (!url.StartsWith("http://")) throw new NotSupportedException("Only editing via HTTP is currently supported");
 
             URL = url;
             PHP5 = usePHP5;
@@ -317,6 +318,14 @@
                                                                  Environment.OSVersion.VersionString,
                                                                  Environment.Version);
 
+
+        private static bool customXertificateValidation(object sender, X509Certificate cert, X509Chain chain, System.Net.Security.SslPolicyErrors error)
+        {
+            // TODO: Implement better validation. JOE 20110722
+			return true;
+        }
+
+        
         /// <summary>
         /// 
         /// </summary>
@@ -327,6 +336,7 @@
             if (Globals.UnitTestMode) throw new Exception("You shouldn't access Wikipedia from unit tests");
 
             ServicePointManager.Expect100Continue = false;
+            ServicePointManager.ServerCertificateValidationCallback += new System.Net.Security.RemoteCertificateValidationCallback(customXertificateValidation);
             HttpWebRequest res = (HttpWebRequest)WebRequest.Create(url);
             res.ServicePoint.Expect100Continue = false;
             res.Expect = "";
@@ -356,6 +366,17 @@
         {
             Request = req;
 
+            NetworkCredential login = new NetworkCredential();
+			login.UserName = Variables.HttpAuthUsername;
+			login.Password = Variables.HttpAuthPassword;
+            //login.Domain = "";
+			
+			CredentialCache myCache = new CredentialCache();
+			myCache.Add(new Uri(URL), "Basic", login);
+			req.Credentials = myCache;
+
+			req = (HttpWebRequest)SetBasicAuthHeader(req, login.UserName, login.Password);
+
             try
             {
                 using (WebResponse resp = req.GetResponse())
@@ -372,7 +393,10 @@
                 if (resp == null) throw;
                 switch (resp.StatusCode)
                 {
-                    case HttpStatusCode.NotFound /*404*/:
+					case HttpStatusCode.Unauthorized: // 401						
+						break;
+
+					case HttpStatusCode.NotFound: // 404
                         return ""; // emulate the behaviour of Tools.HttpGet()
                 }
 
@@ -395,6 +419,14 @@
         private string[,] lastPostParameters;
         private string lastGetUrl;
 
+		// Source: http://blog.kowalczyk.info/article/Forcing-basic-http-authentication-for-HttpWebReq.html
+		protected WebRequest SetBasicAuthHeader(WebRequest req, String userName, String userPassword) {
+			string authInfo = userName + ":" + userPassword;
+			authInfo = Convert.ToBase64String(Encoding.Default.GetBytes(authInfo));
+			req.Headers["Authorization"] = "Basic " + authInfo;
+			return req;
+		}
+
         /// <summary>
         /// 
         /// </summary>
Index: WikiFunctions/Session.cs
===================================================================
--- WikiFunctions/Session.cs	(revision 7941)
+++ WikiFunctions/Session.cs	(working copy)
@@ -22,6 +22,8 @@
 using System.Text.RegularExpressions;
 using System.Windows.Forms;
 using WikiFunctions.API;
+using System.Net;
+using System.Security.Authentication;
 
 namespace WikiFunctions
 {
@@ -192,8 +194,20 @@
             {
                 throw;
             }
-            catch
+            catch (WebException ex)
             {
+                // Check for HTTP 401 error.                
+                var resp = (HttpWebResponse)ex.Response;
+                if (resp == null) throw;
+                switch (resp.StatusCode)
+                {
+                    case HttpStatusCode.Unauthorized /*401*/:
+                        throw;
+                }
+                return false;
+            }
+            catch (Exception)
+            {                
                 Editor = CreateEditor("http://en.wikipedia.org/w/", false);
                 return false;
             }
@@ -407,14 +421,71 @@
             {
                 throw;
             }
+            catch (WebException ex)
+            {
+                string message = "";
+                
+                if (ex.InnerException != null)
+                {
+                    if (ex.InnerException is AuthenticationException)
+                    {
+
+                        if (message.Equals(""))
+                        {
+                            message = ex.Message;
+                        }
+                        else
+                        {
+                            message += " " + ex.Message;
+                        }
+                    }
+
+                    if (message.Equals(""))
+                    {
+                        message = ex.InnerException.Message;
+                    }
+                    else
+                    {
+                        message += " " + ex.InnerException.Message;
+                    }
+                }
+                else
+                {
+                    var resp = (HttpWebResponse)ex.Response;
+                    if (resp == null) throw;
+
+                    // Check for HTTP 401 error.
+                    switch (resp.StatusCode)
+                    {
+                        case HttpStatusCode.Unauthorized: // 401
+                            throw;
+                    }
+                    
+                    message = ex.Message;
+                }
+
+                MessageBox.Show(message, "Error connecting to wiki", MessageBoxButtons.OK, MessageBoxIcon.Error);
+                
+                throw ex;
+            }
             catch (Exception ex)
             {
                 //TODO:Better error handling
 
-                string message = ex is WikiUrlException && ex.InnerException != null
-                    ? ex.InnerException.Message
-                    : ex.Message;
+                string message = "";
 
+                if (ex is WikiUrlException)
+                {
+                    if (ex.InnerException != null)
+                    {
+                        message = ex.InnerException.Message;
+                    }
+                }
+                else
+                {
+                    message = ex.Message;
+                }
+
                 MessageBox.Show("An error occured while connecting to the server or loading project information from it. " +
                         "Please make sure that your internet connection works and such combination of project/language exist." +
                         "\r\nEnter the URL in the format \"en.wikipedia.org/w/\" (including path where index.php and api.php reside)." +
Index: WikiFunctions/SiteInfo.cs
===================================================================
--- WikiFunctions/SiteInfo.cs	(revision 7941)
+++ WikiFunctions/SiteInfo.cs	(working copy)
@@ -20,6 +20,7 @@
 using System.Collections.Generic;
 using System.Xml;
 using System.Xml.Serialization;
+using System.Net;
 using WikiFunctions.API;
 
 namespace WikiFunctions
@@ -71,6 +72,10 @@
             {
                 throw;
             }
+            catch (WebException)
+            {
+                throw;
+            }
             catch (Exception ex)
             {
                 throw new WikiUrlException(ex);
@@ -289,16 +294,20 @@
         #region Helpers
         public void OpenPageInBrowser(string title)
         {
-            if (!Variables.UsingSecure && ArticleUrl.Contains("$1"))
+			if (Variables.IsCustomProject && ArticleUrl.Contains("$1"))
             {
                 string url = ArticleUrl.Replace("$1", Tools.WikiEncode(title));
 
                 Tools.OpenURLInBrowser(url);
             }
-            else
-            {
-                Tools.OpenArticleInBrowser(title);
-            }
+			else if (!Variables.UsingSecure && ArticleUrl.Contains("$1")) {
+				string url = ArticleUrl.Replace("$1", Tools.WikiEncode(title));
+
+				Tools.OpenURLInBrowser(url);
+			}
+			else {
+				Tools.OpenArticleInBrowser(title);
+			}
         }
 
         public void OpenPageHistoryInBrowser(string title)
Index: WikiFunctions/Variables.cs
===================================================================
--- WikiFunctions/Variables.cs	(revision 7941)
+++ WikiFunctions/Variables.cs	(working copy)
@@ -156,7 +156,7 @@
         /// Gets a Index URL of the site, e.g. "http://en.wikipedia.org/w/index.php"
         /// </summary>
         public static string URLIndex
-        { get { return (UsingSecure ? URLSecure + URLEnd : URLLong) + IndexPHP; } }
+        { get { return (UsingSecure && !IsCustomProject ? URLSecure + URLEnd : URLLong) + IndexPHP; } }
 
         /// <summary>
         /// Gets a Index URL of the site, e.g. "http://en.wikipedia.org/w/api.php"
@@ -164,6 +164,16 @@
         public static string URLApi
         { get { return URLLong + ApiPHP; } }
 
+		public static string HttpAuthUsername {
+			get;
+			set;
+		}
+
+		public static string HttpAuthPassword {
+			get;
+			set;
+		}
+		
         /// <summary>
         /// true if current wiki uses right-to-left writing system
         /// </summary>
@@ -505,6 +515,7 @@
             if (IsCustomProject)
             {
                 LangCode = "en";
+				URLEnd = "";
                 int x = customProject.IndexOf('/');
 
                 if (x > 0)
@@ -513,7 +524,8 @@
                     CustomProject = customProject.Substring(0, x);
                 }
 
-                URL = "http://" + CustomProject;
+				URL = (usingSecure ? "https://" : "http://") + CustomProject;
+
             }
             else
             {
@@ -683,7 +695,10 @@
                     URLEnd = "/";
                     break;
                 case ProjectEnum.custom:
-                    //URLEnd = "";
+					// Make sure URL ends with / so ApiURL won't become www.customwiki.comapi.php.
+					if (!URL.EndsWith("/")) {
+						URLEnd = "/";
+					}
                     break;
             }
 
@@ -693,27 +708,26 @@
             //HACK:HACK:HACK:HACK:HACK:
             if (MainForm != null && MainForm.TheSession != null)
             {
-                try
-                {
-                    if (!MainForm.TheSession.UpdateProject(false))
-                    {
-                        LangCode = "en";
-                        Project = ProjectEnum.wikipedia;
-                        SetToEnglish();
-                    }
-                }
-                catch (ReadApiDeniedException)
-                {
-                    TryLoadingAgainAfterLogin = true;
-                    ReloadProjectSettings = new ProjectHoldArea
-                                                {
-                                                    projectName = projectName,
-                                                    customProject = customProject,
-                                                    langCode = langCode,
-                                                    usingSecure = usingSecure
-                                                };
-                    return;
-                }
+				try {
+					if (!MainForm.TheSession.UpdateProject(false)) {
+						LangCode = "en";
+						Project = ProjectEnum.wikipedia;
+						SetToEnglish();
+					}
+				}
+				catch (ReadApiDeniedException) {
+					TryLoadingAgainAfterLogin = true;
+					ReloadProjectSettings = new ProjectHoldArea {
+						projectName = projectName,
+						customProject = customProject,
+						langCode = langCode,
+						usingSecure = usingSecure
+					};
+					return;
+				}
+				catch (Exception ex) {
+					throw ex;
+				}
             }
 
             RegenerateRegexes();