FYI for a "Portable" version of XAMPP: (http://portableapps.com/apps/development/xampp)
XAMPP isn't in PortableApps.com Format, but it easily integrates with the PortableApps.com Suite by using the XAMPP Launcher 1.4 and installing XAMPP in the root directory of your portable device (as recommended).-http://portableapps.com/apps/development/xampp
Let's say I have a C# desktop WinForms application that wants to submit a 'DC_username' and 'User_website' just like the php code does in my above example. How can the C# app silently (in other words, without requiring the user to type in these values on a form) send these to my update.php page so that it is indistinguishable from the form submit, as far as the php is concerned?
For example, I put the following code in a button's click event, in a C# test program. It adds a row to the HTML table on my .../addressBook.php page, but both fields are blank:
string URI = "http://localhost/addressBook/update.php";
WebClient wc = new WebClient();
NameValueCollection NC = new NameValueCollection();
NC.Add("sampleUserName","sampleWebsiteURL");
byte[] response = wc.UploadValues(URI,NC);
string responseString = Encoding.ASCII.GetString(response);
MessageBox.Show(responseString);
[ You are not allowed to view attachments ]
The update.php page is expecting to receive POST data like this:
$Usr = $_POST['MyUsrName'];
$UsrUrl = $_POST['MyUsrWebsite'];
Is there some way I need to change the format of the data I'm passing into NC above? Obviously, my update.php page is receiving something, or it wouldn't update the HTML table with blank values. I need need to figure out how to make my C# app send data in a way that the php recognizes it as the two post variables shown above, right?
Here, code plus example-ish. You'll want to gut the stuff further.
XmlHelper.cs, gutted a fair amount and an excellent example of ugly code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Xml;
using System.IO;
using System.Reflection;
using System.ComponentModel;
using System.Threading;
namespace JottiQ
{
public enum HttpMethod
{
Get,
Post,
}
public class UserCanceledException : Exception
{
public UserCanceledException()
: base()
{
}
public UserCanceledException(string message)
: base(message)
{
}
public UserCanceledException(string message, Exception inner)
: base(message, inner)
{
}
}
class XmlHelper
{
static private NetworkCredential ApiCredentials = new NetworkCredential("Wouldn't_You", "Like_To_Know");
static private string userAgent = null;
public static string UserAgent
{
get
{
if (userAgent == null)
{
System.Reflection.Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly();
System.Reflection.AssemblyName name = assembly.GetName();
System.Version version = name.Version;
userAgent = name.Name + " v" + version.ToString();
}
return userAgent;
}
}
public class FileUploadData : INotifyPropertyChanged, Status.IProgressStatusReporter
{
public event PropertyChangedEventHandler PropertyChanged;
public delegate bool CanContinueDelegate();
private string filename;
private Stream stream;
private JottiItem ji; /* TODO: Temporary. Please fix! */
private CanContinueDelegate ccd;
public string FileName
{
get { return this.filename; }
}
public Stream Stream
{
get { return this.stream; }
}
public JottiItem JI
{
get { return ji; }
}
public CanContinueDelegate CanContinue
{
get { return ccd; }
}
public FileUploadData(string filename, Stream stream, JottiItem ji, CanContinueDelegate canContinue)
{
this.filename = filename;
this.stream = stream;
this.ji = ji;
this.ccd = canContinue;
}
protected void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
/* IProgressStatusReporter implementation */
public long Min
{
get
{
return 0;
}
}
public long Max
{
get
{
return (this.stream != null) ? this.stream.Length : 0;
}
}
public long Progress
{
get
{
return (this.stream != null) ? this.stream.Position : 0;//(this.stream.Position*10)/this.stream.Length : 0;
}
}
public void Update()
{
OnPropertyChanged("Progress");
}
}
static public XmlDocument getXmlResponse(string inURL, HttpMethod hm, Dictionary<string, object> fields)
{
/* HTTPWebRequest, XMLDocument */
HttpWebRequest myHttpWebRequest;
HttpWebResponse myHttpWebResponse;
XmlDocument myXMLDocument;
XmlTextReader myXMLReader;
try
{
if ((hm == HttpMethod.Get) && (fields != null))
{
StringBuilder sb = new StringBuilder();
sb.Append(inURL);
sb.Append("?");
foreach (KeyValuePair<string, object> pair in fields)
{
sb.Append(pair.Key);
sb.Append("=");
sb.Append(pair.Value);
sb.Append("&");
}
inURL = sb.ToString();
}
//Create Request
myHttpWebRequest = (HttpWebRequest)HttpWebRequest.Create(inURL);
myHttpWebRequest.Credentials = ApiCredentials;
myHttpWebRequest.PreAuthenticate = true;
myHttpWebRequest.UserAgent = UserAgent;
if (hm == HttpMethod.Get)
{
myHttpWebRequest.Method = "GET";
myHttpWebRequest.ContentType = "application/x-www-form-urlencoded; encoding='utf-8'"; //"text/xml; encoding='utf-8'";
}
else if (fields == null)
{
/* If there's no parameters to send, we don't need to be difficult either. */
myHttpWebRequest.Method = "POST";
}
else
{
myHttpWebRequest.Method = "POST";
// The value "multipart/form-data" should be used in combination with the INPUT element, type="file".
/* When we're uploading a good chunk of data, there is a huge chance of a
* KeepAlive bug crashing us halfway through. Disable those. */
myHttpWebRequest.KeepAlive = false;
myHttpWebRequest.Timeout = 6 * 60 * 60 * 1000; // 6 hours should be enough for everyone.
/* Since we are using multipart/form-data, we need to be difficult with boundaries. */
string boundary = "----------------------------" + DateTime.Now.Ticks.ToString("x");
myHttpWebRequest.ContentType = "multipart/form-data; boundary=" + boundary;
// Get the boundary in bytes
byte[] boundarybytes = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "\r\n");
// 1. Calculate total length of entire message (by fake-building it)
// 2. Send content-length.
// 3. Send actual multipart stuff. This avoids having a huge file upload in memory.
// --BOUNDARY
// Content-Disposition: form-data; name="<NAME>"
//
// sadfasdfasdf
// --BOUNDARY
// Content-Disposition: form-data; name="<NAME>"; filename="C:\file1.txt"
// Content-Type: application/octet-stream
// --BOUNDARY--
long contentLength = 0;
string fileTemplate = "Content-Disposition: form-data; name=\"{0}\";filename=\"{1}\"\r\nContent-Type: application/octet-stream\r\n\r\n";
string miscTemplate = "Content-Disposition: form-data; name=\"{0}\"\r\n\r\n";
foreach (KeyValuePair<string, object> pair in fields)
{
if (pair.Value is FileUploadData)
{
FileUploadData fud = (FileUploadData)pair.Value;
string fileHeader = string.Format(fileTemplate, pair.Key, fud.FileName);
//convert the header to a byte array
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(fileHeader);
contentLength += boundarybytes.Length + bytes.Length + fud.Stream.Length;
}
else
{
string miscHeader = string.Format(miscTemplate, pair.Key);
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(miscHeader);
contentLength += boundarybytes.Length + bytes.Length + pair.Value.ToString().Length;
}
}
contentLength += boundarybytes.Length + 2; /* extra 2 -- at the end of final boundary */
myHttpWebRequest.ContentLength = contentLength;
/* Step 3. Send the actual data and stuff. */
Stream requestStream = myHttpWebRequest.GetRequestStream();
foreach (KeyValuePair<string, object> pair in fields)
{
requestStream.Write(boundarybytes, 0, boundarybytes.Length);
if (pair.Value is FileUploadData)
{
FileUploadData fud = (FileUploadData)pair.Value;
string fileHeader = string.Format(fileTemplate, pair.Key, fud.FileName);
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(fileHeader);
requestStream.Write(bytes, 0, bytes.Length);
// Use 4096*2 for the buffer / make that *4 to streamline IO over UI updates
byte[] buffer = new byte[4096*4];
int bytesRead = 0;
int bytesTotal = 0;
// Loop through whole file uploading parts in a stream.
while ((bytesRead = fud.Stream.Read(buffer, 0, buffer.Length)) != 0)
{
requestStream.Write(buffer, 0, bytesRead);
requestStream.Flush();
/* Temporary solution for live updates. */
bytesTotal += bytesRead;
fud.JI.StatusMessage = "Upload " + ((bytesTotal * 100) / fud.Stream.Length).ToString() + "% complete.";
fud.Update();
if (!fud.CanContinue())
throw new UserCanceledException("Upload has been canceled by user.");
}
fud.JI.StatusMessage = "Upload done.";
}
else
{
string miscHeader = string.Format(miscTemplate, pair.Key);
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(miscHeader);
requestStream.Write(bytes, 0, bytes.Length);
bytes = System.Text.Encoding.UTF8.GetBytes(pair.Value.ToString());
requestStream.Write(bytes, 0, bytes.Length);
}
}
// Write out the final boundary
boundarybytes = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "--\r\n");
requestStream.Write(boundarybytes, 0, boundarybytes.Length);
// Close our stream.
requestStream.Close();
}
//Get Response
myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse();
//Now load the XML Document
myXMLDocument = new XmlDocument();
//Load response stream into XMLReader
myXMLReader = new XmlTextReader(myHttpWebResponse.GetResponseStream());
myXMLDocument.Load(myXMLReader);
}
catch (Exception myException)
{
throw new Exception("Error Occurred in getXMLResponse()", myException);
//return null;
}
finally
{
myHttpWebRequest = null;
myHttpWebResponse = null;
myXMLReader = null;
}
return myXMLDocument;
}
}
}
It works to the extent I need it to, which admittedly isn't much at all. :) You'll want the getXmlResponse() method, which I originally took from some online example and heavily adjusted to suit my own needs. For JottiQ fileuploads, I call it like this (in a seperate thread):
XmlHelper.FileUploadData fud = new XmlHelper.FileUploadData(
System.IO.Path.GetFileName(ji.FilePath),
fileStream, ji,
delegate() { return !_terminated; });
//getXmlResponseWrapper() wraps various error-handling stuffs.
XmlDocument xml = getXmlResponseWrapper(ji, true, inURL, HttpMethod.Post,
new Dictionary<string, object>()
{
{"token", token},
{"scanfile", fud}
}
);
The entire passing of ji to the function or its upload components is a bit of a hack I used for updating the display while uploading; I am actually in the process of taking that out which is why the code is very bi-polar in nature right now. The delegate serves the purpose of making the upload process cancelable.
In the above example, the token is a string. scanfile acts like like one of those file upload controls. I ended up reinventing the wheel of implementing the POST protocol because I couldn't find any builtin libraries that actually did that already. (Also, file upload things only work for POST method since the data is too much to fit in a GET request.) In your example, you can probably suffice with a single call like:
XmlDocument xml = getXmlResponse(inURL, HttpMethod.Post,
new Dictionary<string, object>()
{
{"user", getUsernameString()},
{"uid", getUniqueID()}
}
);
...and so forth. The code doesn't do much error-checking, or URL encoding of parameter names (& -> %26 or + -> %2B) and the sort since my needs were simple and very well-defined so that I knew I would not need to worry about those cases. That may not be the case for you however. Hell, you may not even want to implement your script to return XML data, so then you could gut the XML loading bit out too.
Code highlighting removed since the highlighter turns all the indenting into   repetitions. I'll gladly put it back in when mouser fixes the highlighter.