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 @@

ExitQuestion.cs

+

+ Form

+

+

+ Login.cs

+

Form

@@ -184,6 +190,9 @@

Designer

CustomModule.cs

+

+ Login.cs

+

Designer

TalkMessage.cs

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

+ {

+ ///

+ /// Required designer variable.

+ ///

+ private System.ComponentModel.IContainer components = null;

+

+ ///

+ /// Clean up any resources being used.

+ ///

+ /// true if managed resources should be disposed; otherwise, false.

+ protected override void Dispose(bool disposing)

+ {

+ if (disposing && (components != null))

+ {

+ components.Dispose();

+ }

+ base.Dispose(disposing);

+ }

+

+ #region Windows Form Designer generated code

+

+ ///

+ /// Required method for Designer support - do not modify

+ /// the contents of this method with the code editor.

+ ///

+ 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 @@

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+ text/microsoft-resx

+

+

+ 2.0

+

+

+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089

+

+

+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089

+

+

\ 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;

+ }

+

+

///

///

///

@@ -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;

+ }

+

///

///

///

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"

///

public static string URLIndex

- { get { return (UsingSecure ? URLSecure + URLEnd : URLLong) + IndexPHP; } }

+ { get { return (UsingSecure && !IsCustomProject ? URLSecure + URLEnd : URLLong) + IndexPHP; } }

///

/// 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;

+ }

+

///

/// true if current wiki uses right-to-left writing system

///

@@ -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();