c# api....

16 views
Skip to first unread message

Tim

unread,
May 1, 2008, 5:32:00 PM5/1/08
to PBwiki API Hackers
Check this out.. here's a robust c# class that you can use in order to
use the pbwiki api...
by default it uses the non-educational wiki...

it has support for the v1 and the v2 apis... in order to make changes
to a v2 wiki you need to already be logged into your pbwiki account
through internet explorer.

You also need to supply your own PHP deserializer (which can be
downloaded pretty quickly and easily)

here's a sample program using the api:

pbWiki.API.SetPHPSerializer(new Serializer());
object o = pbWiki.API.GetPage("FrontPage");

running this in the debugger, you will see that it's pretty easy to
get data out of the returned object.. (a thin wrapper around the hash
table returned by the api)

__________________________________________________________________________


using System.Collections;
using System.IO;
using System.Net;
using System.Text;
using System.Web;

namespace pbWiki
{
public delegate void DisplayErrorDelegate(string s);
public delegate void DisplayWarningDelegate(string s);
public delegate void DisplayDebugDelegate(int level, string s);

struct wikis
{
public static wikiInfo noneducational = new
pbWiki.wikiInfo(pbWiki.eApiVersion.v1, "non-educational",
"c64278c55a235654530b4b81cedd19f520d14a9b");
}

#region Enums
public enum eApi
{
php,
json,
xml
}

public enum eGetPageFormat
{
rendered,
raw,
none
}

public enum eApiVersion
{
v1,
v2
}
#endregion

#region Structs and Classes
public struct wikiCommand
{
public wikiCommand(string a, string b) { cmd = a; cmdParams =
b; }
public string cmd;
public string cmdParams;
}

public struct wikiInfo
{
public wikiInfo(eApiVersion v, string name, string key)
{
version = v; wikiname = name; apikey = key;
}
public eApiVersion version;
public string wikiname;
public string apikey;
}

public class PageClass
{
public static string pageDataStr
{
get
{
return API.curwiki.version == eApiVersion.v1 ?
"data" : "html";
}
}

public PageClass(Hashtable t, string pageName)
{
Name = pageName;
pageHash = t;
pageHash["pagename"] = pageName;
pageHash["links"] = new Hashtable();
pageHash["brokenlinks"] = new ArrayList();
}
public string GetCommand()
{
return (string)pageHash["command"];
}
public string GetRawData()
{
return (string)pageHash["rawdata"];
}
public bool NeedsFurtherProcessing()
{
return pageHash["needsfurtherprocessing"] != null;
}
public int GetIntFlag(string flag)
{
object o = GetFlag(flag);
if (o is string)
return int.Parse(o.ToString());
else
return (o != null) ? (int)o : 0;
}
public string GetStringFlag(string flag)
{
object o = GetFlag(flag);
return o != null ? (string)o : "";
}
public object GetFlag(string flag)
{
return GetFlags()[flag];
}
public Hashtable GetFlags()
{
Hashtable flags = (Hashtable)pageHash["flags"];
if (flags == null)
{
flags = new Hashtable();
pageHash["flags"] = flags;
}
return flags;
}
public void InitPostProcessingCommand(string command, string
rawdata)
{
string pageName = GetName();
pageHash["rawdata"] = rawdata;
pageHash["needsfurtherprocessing"] = true;
pageHash["command"] = command;
}
public string GetName()
{
return Name;
}
public Hashtable GetLinks()
{
return (Hashtable)pageHash["links"];
}
public ArrayList GetBrokenLinks()
{
return (ArrayList)pageHash["brokenlinks"];
}
public bool IsBroken()
{
object o = pageHash["broken"];
return (o != null) ? (bool)o : false;
}
public bool IsValid()
{
return pageHash != null;
}
public bool HasData()
{
return pageHash[pageDataStr] != null;
}
public string GetData()
{
return pageHash[pageDataStr].ToString();
}
public Hashtable pageHash;
string Name = "";
}
#endregion

public class API
{
#region Public Static Variables
public static string automated_user_name = "Mindless Otto";
public static DisplayErrorDelegate DisplayError = new
DisplayErrorDelegate(System.Console.WriteLine);
public static DisplayWarningDelegate DisplayWarning = new
DisplayWarningDelegate(System.Console.WriteLine);
public static DisplayDebugDelegate DisplayDebug = new
DisplayDebugDelegate(DebugStub);
public static wikiInfo curwiki = wikis.noneducational;
public static bool wiki_readonly = false;
#endregion

#region Public Methods

public static void SetPHPSerializer(object o)
{
if (o.GetType().GetMethod("Serialize") == null ||
o.GetType().GetMethod("Deserialize") == null)
{
DisplayWarning("php serialization object of type '" +
o.GetType().Name + "' does not have Serialize/Deserialize methods");
return;
}
sm_PHPSerializer = o;
}

public static string RenderContent(string content, bool post)
{
string URL = GCS_RenderContent();
string response;
if (post)
response = HttpRequest(URL, content);
else
response = HttpRequest(URL + "&data=" +
HttpUtility.UrlEncode(content));
Hashtable o = (Hashtable)PHPDeserialize(response);
if (o != null && o["html"] != null)
return (string)o["html"];
return content;
}

public static string RenderContent(string content)
{
return RenderContent(content, true);
}

public static Hashtable GetAllPages()
{
DisplayDebug(1,"GetAllPages...");
string URL = GCS_GetAllPages();
string response = HttpRequest(URL);
return (Hashtable)Deserialize(response);
}

public static void ChangePage(string pageName, string
fullText)
{
if (wiki_readonly)
{
DisplayWarning(curwiki.wikiname + " wiki is
readonly.");
return;
}

if (curwiki.version == eApiVersion.v2)
{
XMLHttpPostEditPage(pageName, fullText);
return;
}

string urlName = HttpUtility.UrlEncode(pageName);
string url = GCS_ChangePage(urlName);
string pageText = HttpRequest(url, fullText);
Hashtable t = (Hashtable)Deserialize(pageText);
if(t!=null && t[errCodeStr]!=null)
{
DisplayError("Could not change page '" + pageName +
"' (error: " + t[errCodeStr] + " - " + t["errMsg"] + ")");
}
else if (t==null)
{
DisplayWarning("Possible strangeness changing '" +
pageName + "'");
}
}

public static Hashtable GetOps()
{
if (curwiki.version == eApiVersion.v1)
return null;
string url = GCS_GetOps();
string pageText = HttpRequest(url);
Hashtable t =
(Hashtable)sm_v2Serializer.Deserialize(pageText);
return t;
}

public static string GetRawPageText(string urlName)
{
string url = GCS_GetPage(urlName, eGetPageFormat.raw);
string pageText = HttpRequest(url);
//let's parse XML!
//System.IO.StringReader reader = new
System.IO.StringReader(pageText);
//System.Xml.XmlReader r =
System.Xml.XmlReader.Create(reader);
Hashtable h;
h = (Hashtable)Deserialize(pageText);
if (h == null)
return pageText;
return (string)h[PageClass.pageDataStr];
}

public static PageClass GetPage(string pageName)
{
string urlName = HttpUtility.UrlEncode(pageName);
urlName = urlName.Replace("+", "%20");
string url = GCS_GetPage(urlName);
string pageText = HttpRequest(url);
object pageObj = null;
bool tryAlternate = false;
try
{
pageObj = pbWiki.API.Deserialize(pageText);
}
catch (System.Exception)
{
DisplayDebug(2, "Bad or malformed output in " + url);
tryAlternate = true;
}
Hashtable h = (Hashtable)pageObj;
if (h != null)
{
if (h[pbWiki.API.errCodeStr] != null)
{
DisplayDebug(1, "Page returned error (" +
h[pbWiki.API.errCodeStr] + "): " + url);
tryAlternate = true;
}
}
if (tryAlternate)
{
DisplayDebug(3, "Trying to fetch alternate " +
urlName);
url = GCS_GetPageRawBare(urlName);
pageText = HttpRequest(url);
// now we need to remove the "security concerns" (page
tracking?) that come after the close html tag
int closehtml = pageText.IndexOf("</html>");
if (closehtml != -1)
{
pageText = pageText.Substring(0, closehtml + 7);
}
h = new Hashtable();
h[pbWiki.PageClass.pageDataStr] = pageText;
}
return new PageClass(h,
HttpUtility.UrlDecode(urlName)); // how does decoded urlName compare
to pageName?
}

#endregion

#region Private Methods
static string errCodeStr
{
get
{
return curwiki.version == eApiVersion.v1 ? "errCode" :
"error_status";
}
}

static void DebugStub(int i, string s) { }

static object Deserialize(string s)
{
if (curwiki.version == eApiVersion.v2)
{
return sm_v2Serializer.Deserialize(s);
}

return PHPDeserialize(s);
}

static object PHPDeserialize(string s)
{
System.Type t = sm_PHPSerializer.GetType(); // call
SetPHPSerializer with a valid serializer before calling this function
return t.InvokeMember(
"Deserialize",
System.Reflection.BindingFlags.InvokeMethod,
null,
sm_PHPSerializer,
new object[] { s }
);
//return sm_PHPSerializer.Deserialize(s);
}

static string PHPSerialize(object o)
{
System.Type t = sm_PHPSerializer.GetType();
return (string)t.InvokeMember(
"Serialize",
System.Reflection.BindingFlags.InvokeMethod,
null,
sm_PHPSerializer,
new object[] { o }
);
//return sm_PHPSerializer.Serialize(s);
}

static string HttpRequest(string URL)
{
return HttpRequest(URL, null);
}

static void XMLHttpRequest(string url, string post)
{
XMLHttpRequest(url, post, true);
}

static void XMLHttpRequest(string url, string post, bool
retry)
{
string method = (post==null)?"GET":"POST";
sm_XMLHttpRequest.open(method, url, false, null, null);
if (post != null)
{
sm_XMLHttpRequest.setRequestHeader("Content-Type",
"application/x-www-form-urlencoded");
DisplayDebug(1, "XMLHttpRequest post to " + url);
}
else
post = "";

try
{
sm_XMLHttpRequest.send(post);
}
catch (System.SystemException e)
{
if (retry) // sometimes this fails the first time
{
DisplayWarning("Edit failed (" + e.Message + "):
Retrying edit for '" + url + "'");
XMLHttpRequest(url, post, false);
}
}

}

static void XMLHttpPostEditPage(string pageName, string
fullText)
{
string urlName = HttpUtility.UrlEncode(pageName);
string thisUrl = "http://" + curwiki.wikiname +
".pbwiki.com/" + urlName;
string strParam = "process=edit_page&content=" +
HttpUtility.UrlEncode(fullText) + "&tags=";
XMLHttpRequest(thisUrl, strParam);
}

static string HttpRequest(string URL, string postData)
{
DisplayDebug(4, "REQUEST: " + URL);

// used to build entire input
StringBuilder sb = new StringBuilder();

// used on each read operation
byte[] buf = new byte[8192];

// prepare the web page we will be asking for
HttpWebRequest request = (HttpWebRequest)
WebRequest.Create(URL);

if (postData != null)
{
UTF8Encoding encoding = new UTF8Encoding();
byte[] data = encoding.GetBytes(postData);
request.Method = "POST";

request.ContentLength = data.Length;
Stream newStream = request.GetRequestStream();
DisplayDebug(1, "Posting " + data.Length + " bytes to
" + URL + ".");
if (data.Length > 100 * 1024)
DisplayError("Cannot post more than 100k. URL '"
+ URL + "' attempting to post " + data.Length);
// Send the data.
newStream.Write(data, 0, data.Length);
newStream.Flush(); // just in case?
newStream.Close();
}

// execute the request
HttpWebResponse response;
try
{
response = (HttpWebResponse)
request.GetResponse();
}
catch (System.Net.WebException e)
{
DisplayDebug(2, e.Message);
string msg = e.Message;
int paren = msg.IndexOf("(");
int closeParen = msg.IndexOf(")");
int errCode = 0;
string errMsg = "unknown";

if (paren != -1)
{
string ret = msg.Substring(paren + 1, closeParen -
paren - 1);
errCode = int.Parse(ret);
errMsg = msg.Substring(msg.IndexOf(")") + 2);
}
Hashtable t = new Hashtable();
t[errCodeStr] = errCode;
t["errMsg"] = errMsg;

if (curwiki.version == eApiVersion.v2)
{
DisplayWarning("(" + errCode + ") " + errMsg + " -
" + URL);
}

return PHPSerialize(t);
}

// we will read data via the response stream
Stream resStream = response.GetResponseStream();

string tempString = null;
int count = 0;

do
{
// fill the buffer with data
count = resStream.Read(buf, 0, buf.Length);

// make sure we read some data
if (count != 0)
{
// translate from bytes to ASCII text // is this
sufficient?
tempString = Encoding.UTF8.GetString(buf, 0,
count);

// continue building the string
sb.Append(tempString);
}
}
while (count > 0); // any more data to read?

string retString = sb.ToString();
DisplayDebug(4, retString);
// print out page source
return retString;
}

static string GenerateCommandString(wikiCommand cmd)
{
return GenerateCommandString(cmd, eApi.php);
}

static string GenerateCommandString(wikiCommand cmd, eApi api)
{
StringBuilder sb = new StringBuilder();

if (curwiki.version == eApiVersion.v1)
{
string base_url = "http://" + curwiki.wikiname +
".pbwiki.com/";
sb.Append(base_url + "api/");
sb.Append(api.ToString() + "/");
sb.Append(cmd.cmd);
sb.Append("?apikey_v1=" + curwiki.apikey);
if (cmd.cmdParams.Length != 0 && cmd.cmdParams[0] !=
'&')
sb.Append("&");
sb.Append(cmd.cmdParams);
}
else // version == eApiVersion.v2
{
string base_url = "http://" + curwiki.wikiname +
".pbwiki.com/";
sb.Append(base_url + "api_v2/op/");
sb.Append(cmd.cmd);
sb.Append("/admin_key/" + curwiki.apikey);
if (cmd.cmdParams.Length != 0 && cmd.cmdParams[0] !=
'/')
sb.Append("/");
sb.Append(cmd.cmdParams.Replace('&', '/').Replace('=',
'/'));
sb.Append("/_type/php");
}
return sb.ToString();
}

static string GCS_GetPageRawBare(string urlName)
{
StringBuilder sb = new StringBuilder();

string base_url = "http://" + curwiki.wikiname +
".pbwiki.com/";
sb.Append(base_url);
if (curwiki.version == eApiVersion.v1)
sb.Append(urlName + "?raw=bare");
else
sb.Append(urlName + "?mode=print");

return sb.ToString();
}

static string GCS_RenderContent()
{
wikiCommand cmd;
cmd = new wikiCommand("RenderContent", "");

eApiVersion ver = curwiki.version;
curwiki.version = eApiVersion.v1; // this command only
makes sense with a 1.0 wiki
string cmdStr = GenerateCommandString(cmd, eApi.php);
curwiki.version=ver;
return cmdStr;
}

static string GCS_GetAllPages()
{
wikiCommand cmd;
if (curwiki.version == eApiVersion.v1)
cmd = new wikiCommand("GetAllPages", "");
else
cmd = new wikiCommand("GetPages", "");
return GenerateCommandString(cmd, eApi.php);
}

static string GCS_GetPage(string page)
{
return GCS_GetPage(page, eGetPageFormat.rendered);
}

static string GCS_GetPage(string page, eGetPageFormat fmt)
{
wikiCommand cmd;
cmd.cmd = "GetPage";
cmd.cmdParams = "page=" + page;
if (curwiki.version == eApiVersion.v1)
cmd.cmdParams += "&format=" + fmt.ToString();
return GenerateCommandString(cmd);

}

static string GCS_GetOps()
{
wikiCommand cmd = new wikiCommand("GetOps", "");
return GenerateCommandString(cmd);
}

static string GCS_ChangePage(string page)
{
wikiCommand cmd;
cmd.cmd = "ChangePage";
cmd.cmdParams = "page=" + page + "&name=" +
HttpUtility.UrlEncode(automated_user_name); // &data=thing (this is in
the POST data)
return GenerateCommandString(cmd);
}
#endregion

#region Private Static Members
static object sm_PHPSerializer;
static v2Serializer sm_v2Serializer = new v2Serializer();
static MSXML2.XMLHTTP sm_XMLHttpRequest = new
MSXML2.XMLHTTP();
#endregion
}

class v2Serializer
{
int pos = 0;
public object Deserialize(string str)
{
pos = 0;
return deserialize(str);
}
object deserialize(string str)
{
if (str.Substring(pos).StartsWith("array ("))
{
Hashtable tmpHash = new Hashtable();
ArrayList tmpArray = new ArrayList();
int lastIndex = -1;
pos += 7;
do
{
if (pos >= str.Length)
{
// bug!?
}
object obj1 = deserialize(str);
if (obj1 == null)
{
return (tmpArray == null) ? (object)tmpHash :
(object)tmpArray; // we're done
}
pos += 4; // " => "
object obj2 = deserialize(str);
if (obj1 is int)
{
if (tmpArray != null)
{
int curIndex = (int)obj1;
if (curIndex == lastIndex + 1)
{
tmpArray.Add(obj2);
lastIndex++;
}
else
tmpArray = null;
}
}
else
{
tmpArray = null;
}
tmpHash[obj1] = obj2;
for (int i = pos; i < str.Length; i++)
{
pos = i;
if (str[i] == ',')
{
pos++;
break;
}
if (str[i] == ')')
return (tmpArray == null) ?
(object)tmpHash : (object)tmpArray; ;
}
} while (true);
}
else
{
// string?
bool inquote = false;
int startquot = 0;
for (int i = pos; i < str.Length; i++)
{
pos = i;
if (str[i] == '\'')
{
if (inquote)
{
if (str[i - 1] != '\\')
{
string ret = str.Substring(startquot,
i - startquot);
inquote = false;
pos++;
return ret.Replace("\\'", "'");
}
}
else
{
startquot = i + 1;
inquote = true;
}
}
if (inquote)
continue;

if (str[i] == ')')
{
// probably an empty delimiter in an array:
( item=>item, )
pos++;
return null;
}

bool digit = false;
while (char.IsDigit(str[i]))
{
digit = true;
i++;
}
if (digit)
{
int ret = int.Parse(str.Substring(pos, i -
pos));
pos = i;
return ret;
}
if (str[i] == 't')
{
if (str.Substring(i, 4) == "true")
{
pos += 4;
return true;
}
else
{
return null; // don't know what to do
here!
}
}
else if (str[i] == 'f')
{
if (str.Substring(i, 5) == "false")
{
pos += 5;
return false;
}
else
{
return null; // don't know what to do
here!
}
}
else if (str[i] == 'a')
{
return deserialize(str); // probaby an array
}
}
}
return null;
}
}
}
Reply all
Reply to author
Forward
0 new messages