php-cgi POST request turns into GET request for no reason - c#

I'm developping a windows web server in php and I want to use php-cgi.
It works well but I'm having problems with GET and POST request. When I set enviroment variable "REQUEST_METHOD" to "POST", I don't get anything in the $_POST variable but I get the content in $_GET :/ It doesn't make ANY sense...
Here is my code:
Process php = new Process();
php.StartInfo.FileName = "C:/PHP-7.1/php-cgi.exe";
php.StartInfo.RedirectStandardOutput = true;
php.StartInfo.UseShellExecute = false;
php.StartInfo.EnvironmentVariables["GATEWAY_INTERFACE"] = "CGI/1.1";
php.StartInfo.EnvironmentVariables["SCRIPT_FILENAME"] = page;
php.StartInfo.EnvironmentVariables["REQUEST_METHOD"] = "POST";
php.StartInfo.EnvironmentVariables["REDIRECT_STATUS"] = "true";
php.StartInfo.EnvironmentVariables["SERVER_PROTOCOL"] = "HTTP/1.1";
php.StartInfo.EnvironmentVariables["SERVER_SOFTWARE"] = ServerVersion;
php.StartInfo.EnvironmentVariables["REMOTE_HOST"] = "127.0.0.1";
//php.StartInfo.EnvironmentVariables["CONTENT_LENGTH"] = "3"; // Causes crash
php.StartInfo.EnvironmentVariables["HTTP_ACCEPT"] = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
php.StartInfo.EnvironmentVariables["CONTENT_TYPE"] = "text/html";
String QueryString = "";
if (request.GET_Variables.Count() > 0)
{
int i = 0;
foreach (String variable in request.GET_Variables)
{
QueryString += variable + "=" + request.GET_Values[i];
i++;
if (i != request.GET_Variables.Count())
{
QueryString += "&";
}
}
}
php.StartInfo.EnvironmentVariables["QUERY_STRING"] = QueryString;
php.StartInfo.Arguments = "-f \"" + page + "\"";
Console.WriteLine("php-cgi.exe " + php.StartInfo.Arguments); // Debug
php.Start();
For example, If:
QUERY_STRING="fox=brown&dog=lazy"
The var_dump(); will look like this:
GET: array(2) { ["fox"]=> string(5) "brown" ["dog"]=> string(4) "lazy" }
POST: array(0) { }
I hope someone can help me, have a nice day !

Related

c# - How to call/link a workflow on a web application from a c# program?

I have a C# (WinForms) application that can scan documents via a printer. After scanning, I will be able to enter document details on it and have a button to finalize the documents. The documents details and info will be stored in my database ABC in certain tables.
Now, I have another web application written in Java(IntelliJ) that has some button functionality to upload documents and then start a workflow and route it to another user to approve the document. I won't go into detail on the specifics. This application also connects to the same database ABC.
So now comes the tougher part, I need to link these two applications in a way that when I finalize my document
on the C# application, it has to auto trigger the workflow on the web application side. Rather than manually starting the workflow on the web application, it would just call or trigger the workflow, so I do not need to access the web application at all for the process to start.
private void FinButton_Click(object sender, EventArgs e)
{
int count = 0;
var txtBoxFields = new List<TextBox>
{
textBox1,
textBox2,
textBox3,
textBox4,
textBox5,
textBox6,
textBox7,
textBox8,
textBox9,
textBox10,
textBox11,
textBox12,
textBox13,
textBox14,
textBox15
};
var templateFields = new List<String>
{
"T1",
"T2",
"T3",
"T4",
"T5",
"T6",
"T7",
"T8",
"T9",
"T10",
"T11",
"T12",
"T13",
"T14",
"T15"
};
//long tid = 0;
//Start insert query into templatebatch table in db
var dbConnection2 = DBConnection.Instance();
dbConnection2.DatabaseName = ConfigurationManager.AppSettings["dbName"];
if (dbConnection2.IsConnect())
{
bool test = true;
for (int i = 1; i <= 15; i++)
{
var input = txtBoxFields[i - 1].Text;
var insertQuery = "INSERT INTO templateinfo(TID, THEADER, " + templateFields[i - 1] + ") VALUES(#tid, #theader,#t" + i + ")";
var insertCmd = new MySqlCommand(insertQuery, dbConnection2.Connection);
insertCmd.Parameters.AddWithValue("#tid", tid);
insertCmd.Parameters.AddWithValue("#theader", "N");
if (String.IsNullOrEmpty(input))
{
count = 1;
insertCmd.Parameters.AddWithValue("#t" + i, String.Empty);
break;
}
else
{
if (test)
{
insertCmd.Parameters.AddWithValue("#t" + i, txtBoxFields[i - 1].Text);
insertCmd.ExecuteNonQuery();
test = false;
var selectQuery = "select TINFOID from templateinfo where TID=" + tid + " and THEADER = 'N'";
var selectCmd = new MySqlCommand(selectQuery, dbConnection2.Connection);
var selectReader = selectCmd.ExecuteReader();
using (MySqlDataReader dr = selectReader)
{
while (dr.Read())
{
tinfoid = Convert.ToInt32(dr["TINFOID"]);
}
}
}
else
{
var updateQuery = "update templateinfo set " + templateFields[i - 1] + "='" + txtBoxFields[i - 1].Text + "' where TINFOID = '" + tinfoid + "' and TID=" + tid + " and THEADER='N'";
var updateCmd = new MySqlCommand(updateQuery, dbConnection2.Connection);
var updateReader = updateCmd.ExecuteReader();
using (var reader = updateReader)
{
}
}
}
}
}
if (count == 1)
{
//MessageBox.Show("Input field(s) cannot be left empty.");
}
//Finalize here
var client = new LTATImagingServiceClient();
client.Finalize(userID, tid, tinfoid, batchID);
Debug.WriteLine(userID + ", " + tid + ", " + tinfoid + ", " + batchID);
var batchName = templateView.SelectedNode.Text;
var folderPath = #"C:\temp\batches\" + mastertemplatename + #"\" + subtemplatename + #"\" + batchName + #"\";
ThumbnailLists.Items.Clear();
// var img = Image.FromFile(#"C:\temp\batch-done.png");
if (ImageBox.Image != null)
{
ImageBox.Image.Dispose();
}
ImageBox.Image = null;
try
{
using (new Impersonation(_remoteDomain, _remoteUser, _remotePassword))
{
// MessageBox.Show(_remoteUser);
// MessageBox.Show(_remotePassword);
var tPath = #"\\126.32.3.178\PantonSys\SAM\Storage\3\" + mastertemplatename + #"\" + subtemplatename + #"\" + batchName + #"\";
bool exists = System.IO.Directory.Exists(tPath);
if (!exists)
{
System.IO.Directory.CreateDirectory(tPath);
}
string[] fileList = Directory.GetFiles(folderPath, "*");
foreach (var file in fileList)
{
File.Copy(file, tPath + Path.GetFileName(file));
}
CurrentPageBox.Text = "";
NumberPageBox.Text = "";
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
MessageBox.Show(ex.Message);
}
var dbConnection = DBConnection.Instance();
dbConnection.DatabaseName = ConfigurationManager.AppSettings["dbName"];
if (dbConnection.IsConnect())
{
var deleteBatchQuery = "DELETE FROM templatebatch WHERE batchname ='" + templateView.SelectedNode.Text + "'";
var deleteBatchCmd = new MySqlCommand(deleteBatchQuery, dbConnection.Connection);
var deleteBatchReader = deleteBatchCmd.ExecuteReader();
using (var reader = deleteBatchReader)
{
while (reader.Read())
{
}
}
templateView.Nodes.Remove(templateView.SelectedNode);
Directory.Delete(folderPath, true);
MessageBox.Show("Successfully Transferred.");
foreach (var txtFields in txtBoxFields)
{
txtFields.Text = "";
txtFields.Enabled = false;
}
finButton.Visible = false;
finButton.Enabled = false;
}
bindButton.Visible = false;
}
Would this be possible to achieve or just being far-fetched?
I would appreciate any suggestions or pointers on this. Do let me know if there is anything unclear in my explanation.
EDIT:
Request URL: http://126.32.3.178:8111/process/taskmanager/start/start.jsp
Request Method: POST
Status Code: 200 OK
Remote Address: 126.32.3.178:8111
Referrer Policy: no-referrer-when-downgrade
Is there a way I could call this from the C# application?
You can send your file directly from your C# app with use of Http client. Here is code sample:
private async Task<bool> Upload(string filePath)
{
const string actionUrl = #"http://126.32.3.178:8111/process/taskmanager/start/start.jsp";
var fileName = Path.GetFileName(filePath);
var fileBytes = File.ReadAllBytes(filePath);
var fileContent = new ByteArrayContent(fileBytes);
using (var client = new HttpClient())
using (var formData = new MultipartFormDataContent())
{
formData.Add(fileContent, fileName);
var response = await client.PostAsync(actionUrl, formData);
return response.IsSuccessStatusCode;
}
}
Also, note that there maybe some sort of authentication should be performed before you can post a request.

Make the Link NOT Editable asp.net c# Page

I have a page there is a button that generates a Link like this.
private string GenerateLINK(string NameID)
{
string NameID= ds.Tables[0].Rows[0]["FName"] + " " + ds.Tables[0].Rows[0]["LName"];
string sQS = ID+ "|" + ClientName;
var xCrypto = new CryptoServer();
string Vector= null;
string sEncrypted = null;
xCrypto.Encrypt3DES(sQS, ref sEncrypted, ref Vector);
string sURL = sEncrypted + "#######" + Vector;
sURL = Server.UrlEncode(sURL);
sURL = "https://www.Page.aspx?s=" + sURL;
return sURL;
}
This then gets sent to a user who clicks on it and goes to a page.
Now the issue is I take the link like this and DCode it.
private void DecryptQuerystring()
{
var sQS = Request.QueryString["s"];
sQS = Server.UrlDecode(sQS);
var idelim = sQS.IndexOf("###X####", StringComparison.Ordinal);
var sIv = sQS.Substring(idelim + 8);
sQS = sQS.Substring(0, idelim);
var xCrypto = new ICECrypto.CryptoServer();
sQS = xCrypto.Decrypt3DES(sQS, sIv);
string sID = sQS.Substring(0, sQS.IndexOf("|"));
studentID = sID;
Name = sQS.Substring(sQS.IndexOf("|") + 1);
Welcome.InnerText = "Welcome " + sQS.Substring(sQS.IndexOf("|") + 1);
}
The Problem is when the User gets there and if he puts in any word in the link it breaks the whole page showing the Server Error. I want user to NOT to be able to Edit the Link insert any thing in it. Any clue? Thanks in advance!
This is funny but I am answering my own question maybe someone else could use it.
So where I am doing the Decryption of the QueryString() i put in the word
Try {
// Do the Decryption here
}
Catch(Exception ex) {
// if any thing goes wrong in that Try it will hit here and then i will show error 404
}

How to add a contact to Yahoo using Oauth-2 in Asp.Net C#

I've been wrestling with this for a while now and can't seem to find a solution. The closest I've come is PHP code for Oauth-1 by Joe Chung (https://github.com/joechung/oauth_yahoo), but I can't get my head wrapped around it.
I'm using Asp.Net, and this code is in the ContactController. I have no trouble Getting contacts from Yahoo. The problem is Adding a contact to a user's Yahoo address book. The program proceeds through the Yahoo login process, and there are no errors. But the contact is not saved. I hope someone can take a look at this and tell me what I'm missing.
Thanks.
private string AddYahooContact(string responseFromServer, string contactIdForYahoo)
{
// Some of this from http://www.yogihosting.com/implementing-yahoo-contact-reader-in-asp-net-and-csharp/
responseFromServer = responseFromServer.Substring(1, responseFromServer.Length - 2);
string accessToken = "", xoauthYahooGuid = "", refreshToken = "";
string[] splitByComma = responseFromServer.Split(',');
foreach (string value in splitByComma)
{
if (value.Contains("access_token"))
{
string[] accessTokenSplitByColon = value.Split(':');
accessToken = accessTokenSplitByColon[1].Replace('"'.ToString(), "");
}
else if (value.Contains("xoauth_yahoo_guid"))
{
string[] xoauthYahooGuidSplitByColon = value.Split(':');
xoauthYahooGuid = xoauthYahooGuidSplitByColon[1].Replace('"'.ToString(), "");
}
else if (value.Contains("refresh_token"))
{
string[] refreshTokenSplitByColon = value.Split(':');
refreshToken = refreshTokenSplitByColon[1].Replace('"'.ToString(), "");
}
}
// How to build contactUrl from https://developer.yahoo.com/social/rest_api_guide/contacts-resource.html#contacts-xml_request_put
// This is Yahoo's address to add a contact
string contactUrl = "https://social.yahooapis.com/v1/user/" + xoauthYahooGuid + "/contacts";
// Much of this from https://developer.yahoo.com/dotnet/howto-rest_cs.html
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(contactUrl);
webRequest.Method = "POST";
webRequest.Headers["Authorization"] = "Bearer " + accessToken;
webRequest.ContentType = "application/x-www-form-urlencoded"; // Tried "application/x-www-form-urlencoded & application/xml" & "text/xml".
// Create the data we want to send
string yahooContact = BuildYahooContact(contactIdForYahoo);
byte[] byteData = UTF8Encoding.UTF8.GetBytes(yahooContact);
webRequest.ContentLength = byteData.Length;
using (Stream postStream = webRequest.GetRequestStream())
{
postStream.Write(byteData, 0, byteData.Length);
}
responseFromServer = "";
try
{
using (HttpWebResponse response = webRequest.GetResponse() as HttpWebResponse)
{
StreamReader reader = new StreamReader(response.GetResponseStream());
responseFromServer = reader.ReadToEnd();
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
return yahooContact;
}
private string BuildYahooContact(string contactIdForYahoo)
{
Guid contactId = Guid.Parse(contactIdForYahoo);
Models.Contact contact = GetContactForId(contactId); // And find the contact
string firstName = "";
string lastName = "";
if (contact.FullName != null)
{
int index = contact.FullName.IndexOf(" ");
if (index > 0)
{
firstName = contact.FullName.Substring(0, index);
lastName = (contact.FullName.Substring(index + 1));
}
else
{
lastName = contact.FullName;
}
}
StringBuilder data = new StringBuilder();
data.Append("<contact>");
data.Append("<fields><type>name</type><value>");
data.Append("<givenName>" + firstName + "</givenName><middleName/>");
data.Append("<familyName>" + lastName + "</familyName>");
data.Append("<prefix/><suffix/><givenNameSound/><familyNameSound/>");
data.Append("</value></fields>");
data.Append("<fields><type>address</type>");
data.Append("<value><street>" + contact.Address + "</street>");
data.Append("<city>" + contact.City + "</city>");
data.Append("<stateOrProvince>" + contact.State + "</stateOrProvince>");
data.Append("<postalCode>" + contact.Zip + "</postalCode>");
data.Append("<country>United States</country>");
data.Append("<countryCode>US</countryCode>");
data.Append("</value></fields>");
data.Append("<fields><type>notes</type><value>" + contact.Note + "</value></fields>");
data.Append("<fields><type>link</type><value>" + contact.Website + "</value></fields>");
data.Append("<fields><type>email</type><value>" + contact.Email + "</value></fields>");
data.Append("<fields><type>phone</type><value>" + contact.BusinessPhone + "</value><flags>WORK</flags></fields>");
data.Append("<fields><type>phone</type><value>" + contact.BestPhone + "</value><flags>MOBILE</flags></fields>");
data.Append("<fields><type>phone</type><value>" + contact.SecondPhone + "</value><flags>PERSONAL</flags></fields>");
data.Append("<categories><category><name>GoGoContract</name></category></categories>");
data.Append("</contact>");
return data.ToString();
}

Serializing a manually written code

I am having a problem receiving files from the client. Someone suggested that I should use binary serialization to send and receive messages in stream. Can you give me ideas on how I should serialize this? I just learned about serialization not long ago so I am quite confused on how I should associate it with my program.
This is the client that 'should' be serialize
public void sendthedata()
{
if (!_timer.Enabled) // If timer is not running send data and start refresh interval
{
SendData();
_timer.Enabled = true;
}
else // Stop timer to prevent further refreshing
{
_timer.Enabled = false;
}
}
private List<int> listedProcesses = new List<int>();
private void SendData()
{
String processID = "";
String processName = "";
String processPath = "";
String processFileName = "";
String processMachinename = "";
listBox1.BeginUpdate();
try
{
piis = GetAllProcessInfos();
for (int i = 0; i < piis.Count; i++)
{
try
{
if (!listedProcesses.Contains(piis[i].Id)) //placed this on a list to avoid redundancy
{
listedProcesses.Add(piis[i].Id);
processID = piis[i].Id.ToString();
processName = piis[i].Name.ToString();
processPath = piis[i].Path.ToString();
processFileName = piis[i].FileName.ToString();
processMachinename = piis[i].Machinename.ToString();
output.Text += "\n\nSENT DATA : \n\t" + processFileName + "\n\t" + processMachinename + "\n\t" + processID + "\n\t" + processName + "\n\t" + processPath + "\n";
}
}
catch (Exception ex)
{
wait.Abort();
output.Text += "Error..... " + ex.StackTrace;
}
NetworkStream ns = tcpclnt.GetStream();
String data = "";
data = "--++" + processFileName + " " + processMachinename + " " + processID + " " + processPath;
if (ns.CanWrite)
{
byte[] bf = new ASCIIEncoding().GetBytes(data);
ns.Write(bf, 0, bf.Length);
ns.Flush();
}
}
}
finally
{
listBox1.EndUpdate();
}
}
And deserializing in the server
private void recieveData()
{
NetworkStream nStream = tcpClient.GetStream();
ASCIIEncoding ascii = null;
while (!stopRecieving)
{
if (nStream.CanRead)
{
byte[] buffer = new byte[1024];
nStream.Read(buffer, 0, buffer.Length);
ascii = new ASCIIEncoding();
recvDt = ascii.GetString(buffer);
/*Received message checks if it has +##+ then the ip is disconnected*/
bool f = false;
f = recvDt.Contains("+##+");
if (f)
{
string d = "+##+";
recvDt = recvDt.TrimStart(d.ToCharArray());
clientDis();
stopRecieving = true;
}
//else if (recvDt.Contains("^^"))
//{
// new Transmit_File().transfer_file(file, ipselected);
//}
/* ++-- shutsdown/restrt/logoff/abort*/
else if (recvDt.Contains("++--"))
{
string d = "++--";
recvDt = recvDt.TrimStart(d.ToCharArray());
this.Invoke(new rcvData(addToOutput));
clientDis();
}
/*--++ Normal msg*/
else if (recvDt.Contains("--++"))
{
string d = "--++";
recvDt = recvDt.TrimStart(d.ToCharArray());
this.Invoke(new rcvData(addToOutput));
}
}
Thread.Sleep(1000);
}
}
public void addToOutput()
{
if (recvDt != null && recvDt != "")
{
output.Text += "\n Received Data : " + recvDt;
recvDt = null;
}
}
Thank you.
There are a couple of rules to follow when serialising a piece of data.
It's easy to convert data to bytes, but consider how to reconstruct the data on the other side. Assume that the server can't have any knowledge on what you sended.
In your serialiser you just convert a couple of strings into a byte[] and send it over. Example:
string x = "abcdef";
string y = "ghijk";
var bytes = Encoding.Ascii.GetBytes(x + y);
the server receives: "abcdefghijk";
Is it possible for the server to determine and reconstruct strings x and y?
Since the server has no knowledge of the length of either x and y: no.
There are ways to solve this:
Use fixed length fields. In my example x should always be 6 chars and y should always be 5 chars in length. decoding on the server then becomes as trivial as
string x = data.Substring(0, 6)
string y = data.Substring(6, 5)
Use delimiters between the fields. If you are familiar with cvs, the ',' splits the fields. This however has it drawbacks, how to handle a ',' somewhere in a string? The data send over would be like "abcdef,ghijk"
Send the size of each field before the content of the field.
A naive approach just to clarify: string x would be send as '6abcdef' and y as '5ghijk'
Doing all this things by hand can get really hairy and is something that I would consider only if really needed.
I would resort to existing frameworks that do an excellent job on this subject:
Json.net
protobuf ported by Jon skeet
In this case I would first create a class to define the data send to the server instead of a bunch of strings:
class ProcessInfo{
public string ProcessID {get;set;}
public string ProcessName {get;set;}
public string ProcessPath {get;set;}
public string ProcessFileName {get;set;}
public string ProcessMachinename {get;set;}
};
the using Json to serialise this:
var procinfo = new ProcessInfo{
ProcessId = "1",
...
};
var serialised = JsonConvert.SerializeObject(procinfo);
var bytes = Encoding.Utf8.GetBytes(serialised);
ns.Write(bytes, 0, bytes.Length);
And restore it on the server just by:
var procInfo = JsonConvert.DeserializeObject<ProcessInfo>(json);

Twitter POST problems using api 1.1

We've just changed to Twitter api 1.1, and now Tweeting doesn't work & returns an error "The remote server returned an error: (400) Bad Request." Researching on SO about this suggests that it's something to do with authentication, but we are sending the accessToken & secret which we've just got from the login page. It all worked fine with api 1.0. The code is -
public void Tweet(Action<string> response, string message)
{
StringBuilder sb = new StringBuilder();
sb.Append("POST&");
sb.Append(Uri.EscapeDataString(_postUrl));
sb.Append("&");
string oauthNonce = Convert.ToBase64String(new ASCIIEncoding().GetBytes(DateTime.Now.Ticks.ToString()));
string timeStamp = MakeTimestamp();
var dict = new SortedDictionary<string, string>
{
{ "oauth_consumer_key", _oAuthConfig.ConsumerKey },
{ "oauth_nonce", oauthNonce },
{ "oauth_signature_method", "HMAC-SHA1" },
{ "oauth_timestamp", timeStamp },
{ "oauth_token", _accessToken },
{ "oauth_version", "1.0" },
};
foreach (var keyValuePair in dict)
{
sb.Append(Uri.EscapeDataString(string.Format("{0}={1}&", keyValuePair.Key, keyValuePair.Value)));
}
string encodedMessage = EscapeAdditionalChars(Uri.EscapeDataString(message));
sb.Append(Uri.EscapeDataString("status=" + encodedMessage));
string signatureBaseString = sb.ToString();
// create the signature
string signatureKey = Uri.EscapeDataString(_oAuthConfig.ConsumerSecret) + "&" + Uri.EscapeDataString(_accessTokenSecret);
var hmacsha1 = new HMACSHA1(new ASCIIEncoding().GetBytes(signatureKey));
string signatureString = Convert.ToBase64String(hmacsha1.ComputeHash(new ASCIIEncoding().GetBytes(signatureBaseString)));
// create the headers
string authorizationHeaderParams = String.Empty;
authorizationHeaderParams += "OAuth ";
authorizationHeaderParams += "oauth_consumer_key=\"" + _oAuthConfig.ConsumerKey + "\", ";
authorizationHeaderParams += "oauth_nonce=\"" + oauthNonce + "\", ";
authorizationHeaderParams += "oauth_signature=\"" + Uri.EscapeDataString(signatureString) + "\", ";
authorizationHeaderParams += "oauth_signature_method=\"" + "HMAC-SHA1" + "\", ";
authorizationHeaderParams += "oauth_timestamp=\"" + timeStamp + "\", ";
authorizationHeaderParams += "oauth_token=\"" + _accessToken + "\", ";
authorizationHeaderParams += "oauth_version=\"" + "1.0" + "\"";
string messageToPost = EscapeAdditionalChars(SpacesToPlusSigns(message));
// initialise the WebClient
WebClient client = new WebClient();
client.Headers [HttpRequestHeader.Authorization] = authorizationHeaderParams;
client.UploadDataCompleted += (s, eArgs) =>
{
if (eArgs.Error == null)
response(DefaultSuccessMessage());
else
response(eArgs.Error.Message);
};
try
{
Uri uri = new Uri(_postUrl);
try
{
client.UploadDataAsync(uri, "POST", Encoding.UTF8.GetBytes("status=" + messageToPost));
}
catch (WebException e)
{
Log.Info("TwitterService->Tweet web error: " + e.Message);
response(DefaultErrorMessage());
}
catch (Exception e)
{
// Can happen if we had already favorited this status
Log.Info("TwitterService->Tweet error: " + e.Message);
response(DefaultErrorMessage());
}
}
catch (WebException e)
{
Log.Info("TwitterService->Tweet web error 2: " + e.Message);
response(DefaultErrorMessage());
}
catch (Exception e)
{
Log.Info("TwitterService->Tweet error 2: " + e.Message);
response(DefaultErrorMessage());
}
}
Basically, I'd like to be able to Tweet without using any 3rd party libraries such as Twitterizer (even TweetStation seems to be broken with api 1.1) - surely it can't be that difficult!
Any help much appreciated, as it feels a bit like a brick wall at the moment - I'm also fairly new to c#, which doesn't help...
Edited to show code which wasn't clear previously.
Finally found the solution, as usual with most of these things, it was pretty simple. Code below -
public void Tweet(Action<string> response, string message)
{
StringBuilder sb = new StringBuilder();
sb.AppendFormat ("status={0}", PercentEncode(message));
string content = sb.ToString();
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(_postUrl);
request.Headers.Add("Authorization", AuthorizeRequest(_accessToken, _accessTokenSecret, "POST", new Uri(_postUrl), content));
request.ContentType = "application/x-www-form-urlencoded";
request.ServicePoint.Expect100Continue = false;
request.Method = "POST";
try
{
try
{
using (Stream stream = request.GetRequestStream())
{
Byte[] streamContent = Encoding.UTF8.GetBytes("status=" + PercentEncode(message));
stream.Write(streamContent, 0, streamContent.Length);
}
HttpWebResponse webResponse = (HttpWebResponse)request.GetResponse();
string contents = "";
using (Stream stream = webResponse.GetResponseStream())
using (StreamReader reader = new StreamReader(stream))
{
contents = reader.ReadToEnd();
}
Console.WriteLine("Twitter response: " + contents);
response(DefaultSuccessMessage());
}
catch (WebException e)
{
Log.Info("TwitterService->Tweet web error: " + e.Message);
response(DefaultErrorMessage());
}
catch (Exception e)
{
// Can happen if we had already favorited this status
Log.Info("TwitterService->Tweet error: " + e.Message);
response(DefaultErrorMessage());
}
}
catch (WebException e)
{
Log.Info("TwitterService->Tweet web error 2: " + e.Message);
response(DefaultErrorMessage());
}
catch (Exception e)
{
Log.Info("TwitterService->Tweet error 2: " + e.Message);
response(DefaultErrorMessage());
}
}
private string AuthorizeRequest(string oauthToken, string oauthTokenSecret, string method, Uri uri, string data)
{
string oauthNonce = Convert.ToBase64String(new ASCIIEncoding().GetBytes(DateTime.Now.Ticks.ToString()));
var headers = new Dictionary<string, string>()
{
{ "oauth_consumer_key", _oAuthConfig.ConsumerKey },
{ "oauth_nonce", oauthNonce },
{ "oauth_signature_method", "HMAC-SHA1" },
{ "oauth_timestamp", MakeTimestamp() },
{ "oauth_token", oauthToken },
{ "oauth_verifier", PercentEncode(_authorizationVerifier) },
{ "oauth_version", "1.0A" }
};
var signatureHeaders = new Dictionary<string,string>(headers);
// Add the data and URL query string to the copy of the headers for computing the signature
if (data != null && data != "")
{
var parsed = HttpUtility.ParseQueryString(data);
foreach (string k in parsed.Keys)
{
signatureHeaders.Add(k, PercentEncode(parsed [k]));
}
}
var nvc = HttpUtility.ParseQueryString(uri.Query);
foreach (string key in nvc)
{
if (key != null)
signatureHeaders.Add(key, PercentEncode(nvc [key]));
}
string signature = MakeSignature (method, uri.GetLeftPart(UriPartial.Path), signatureHeaders);
string compositeSigningKey = MakeSigningKey(_oAuthConfig.ConsumerSecret, oauthTokenSecret);
string oauth_signature = MakeOAuthSignature(compositeSigningKey, signature);
headers.Add ("oauth_signature", PercentEncode(oauth_signature));
return HeadersToOAuth(headers);
}
private static string PercentEncode (string s)
{
var sb = new StringBuilder ();
foreach (byte c in Encoding.UTF8.GetBytes (s))
{
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-' || c == '_' || c == '.' || c == '~')
sb.Append ((char) c);
else
{
sb.AppendFormat ("%{0:X2}", c);
}
}
return sb.ToString ();
}
private static string MakeTimestamp ()
{
return ((long) (DateTime.UtcNow - _unixBaseTime).TotalSeconds).ToString ();
}
private static string MakeSignature (string method, string base_uri, Dictionary<string,string> headers)
{
var items = from k in headers.Keys orderby k
select k + "%3D" + PercentEncode (headers [k]);
return method + "&" + PercentEncode (base_uri) + "&" +
string.Join ("%26", items.ToArray ());
}
private static string MakeSigningKey (string consumerSecret, string oauthTokenSecret)
{
return PercentEncode (consumerSecret) + "&" + (oauthTokenSecret != null ? PercentEncode (oauthTokenSecret) : "");
}
private static string MakeOAuthSignature (string compositeSigningKey, string signatureBase)
{
var sha1 = new HMACSHA1 (Encoding.UTF8.GetBytes (compositeSigningKey));
return Convert.ToBase64String (sha1.ComputeHash (Encoding.UTF8.GetBytes (signatureBase)));
}
private static string HeadersToOAuth (Dictionary<string,string> headers)
{
return "OAuth " + String.Join (",", (from x in headers.Keys select String.Format ("{0}=\"{1}\"", x, headers [x])).ToArray ());
}
With Twitter api 1.0, I used a WebClient to post, that doesn't work with api 1.1, and it seems that the reason for this is that you can't set the ContentType or the ServicePoint.Expect100Continue properties - without these set as I've set them, the request is sent back as (401) unauthorized. Nothing to do with encoding problems in the end.
Thanks to others for the various helper methods.
I had exactly the same problem:
This is exactly what you need to do here:
Authenticate and request a user's timeline with Twitter API 1.1 oAuth
I have created a project for this at : https://github.com/andyhutch77/oAuthTwitterTimeline
It also includes an MVC, Web app and console demo.
I ran into this problem, or at least one striking similiar (from my noob perspective), recently for an app I am building. What seemed to solve it for me (after looking at the tool at dev.twitter.com) was simply to get rid of the quotes around the parameter names, so that (in your case):
I notice that you do in fact not have quotes around your parameter names. However, it confuses me that you send authentication details twice (hence my wrongheaded post.) It works for me without doing this, and I googled it briefly and found: https://dev.twitter.com/discussions/12322#comment-27120, which confirms this can be a problem generating an Authetication Error.
400 means you are not authenticated. I recommend getting user context.
https://dev.twitter.com/docs/auth/oauth#user-context

Categories