I have some code that uses the WEBDAV 'SEARCH' method to retrieve emails from an an Exchange Mailboxs 'Inbox' folder - my code takes the the innerXML of the HTTPWebRquests WEBresponse.
Using 'selectingSingleNode' on these namsspaces:
'urn:schemas:httpmail' & urn:schemas:mailheader
Alows me to extract the elements:
f:textdescription
d:fromd:subject
f:datareceived...and so on
I then create a collection of these details in a list and using the 'Subject' along with the URI use the 'PUT' method to recreate these messages in the 'draft's folder before using the 'MOVE' to send the mails (puts them in the sent items using '/##DavMailSubmissionURI##/"' statement.
The problem i have is the nature of the emails i am dealing with tend to come in with the same subject line so it gets confused with which emails have been sent/or not.
Does anyone know of a way around this, I dont know why the 'PUT' relies on the Subject line for the URI to the mail resource rather than say the HREF tag which is unique. Any ideas:
Code is below:
public class EmailReaderWebDav
{
public enum enmHTTPType
{
HTTP,
HTTPS,
}
private String strServer { get; set; } //"mail1" ------ Exchange server name
public String strPassword { get; set; } //"password" ------ Account Domain Password
public String strDomain { get; set; } //"mydocmian" ------ Domain
public String strMailBox { get; set; } //"mymailbox" ------ UserName
public String mailFolder { get; set; } //"inbox" ------ Mail Folder
private String httpProtocol { get; set; } //http:// ? or https://
private String mailboxURI { get; set; } //httpprotocol// + strserver + "/exhange/" + strmailbox
public List<MailStruct > ListOfEmailDetails { get; private set; }
private String strQuerySearch { get; set; }
public EmailReaderWebDav(String serverName, String domain, String mailBox, String password, String mailmailFolder,enmHTTPType HTTPType)
{
strServer = serverName;
strDomain = domain;
strMailBox = mailBox;
strPassword = password;
mailFolder = mailmailFolder;
httpProtocol = (HTTPType == enmHTTPType.HTTPS) ? "https://" : "http://";
mailboxURI = httpProtocol + strServer + "/exchange/" + strMailBox + "/inbox/";
}
public void forwardEmails(List<MailStruct> emailsToSend)
{
emailsToSend.ForEach(x => SendEmail(x,enmHTTPType.HTTP ));
}
public void MakeListofEmailsToForward()
{
String tmpQuery =
"<?xml version=\"1.0\"?>"
+ "<D:searchrequest xmlns:D = \"DAV:\" >"
+ "<D:sql>"
+ " SELECT "
+ "\"urn:schemas:mailheader:to\","
+ "\"urn:schemas:mailheader:from\","
+ "\"urn:schemas:mailheader:subject\","
+ "\"urn:schemas:httpmail:datereceived\","
+ "\"urn:schemas:httpmail:textdescription\""
+ " FROM \"" + mailboxURI + "\""
+ " WHERE \"DAV:ishidden\" = false AND \"DAV:isfolder\" = false"
+ "</D:sql>"
+ "</D:searchrequest>";
// Search Request to get emails from target folder.
HttpWebRequest SearchRequest = MakeWebRequest("SEARCH", "text/xml", mailboxURI);
Byte[] bytes = Encoding.UTF8.GetBytes((String)tmpQuery);
SearchRequest.ContentLength = bytes.Length;
Stream SearchRequestStream = SearchRequest.GetRequestStream();
SearchRequestStream.Write(bytes, 0, bytes.Length);
SearchRequestStream.Close();
// get the webresponse from the searchrequest.
WebResponse SearchResponse = MakeWebResponse(SearchRequest);
String EmailsInXML = extractXMLFromWebResponse(SearchResponse);
ListOfEmailDetails = extractMailPropertiesFromXMLString(EmailsInXML);
}
public void SendEmail(MailStruct mailToForward, enmHTTPType HTTPType)
{
String submissionUri = httpProtocol + strServer + "/" + "exchange" + "/" + strMailBox + "/##DavMailSubmissionURI##/";
String draftsUri = httpProtocol + strServer + "/" +"exchange" + "/" + strMailBox + "/Drafts/" + mailToForward.Subject + ".eml";
String message = "To: " + mailToForward.To + "\n"
+ "Subject: " + mailToForward.Subject + "\n"
+ "Date: " + mailToForward.Received
+ "X-Mailer: mailer" + "\n"
+ "MIME-Version: 1.0" + "\n"
+ "Content-Type: text/plain;" + "\n"
+ "Charset = \"iso-8859-1\"" + "\n"
+ "Content-Transfer-Encoding: 7bit" + "\n"
+ "\n" + mailToForward.MailBody;
// Request to put an email the drafts folder.
HttpWebRequest putRequest = MakeWebRequest("PUT", "message/rfc822",draftsUri );
Byte[] bytes = Encoding.UTF8.GetBytes((String)message);
putRequest.Headers.Add("Translate", "f");
putRequest.Timeout = 300000;
putRequest.ContentLength = bytes.Length;
Stream putRequestStream = putRequest.GetRequestStream();
putRequestStream.Write(bytes, 0, bytes.Length);
putRequestStream.Close();
// Put the message in the Drafts folder of the sender's mailbox.
HttpWebResponse putResponse = MakeWebResponse(putRequest);
putResponse.Close();
// Request to move the email from the drafts to the mail submission Uri.
HttpWebRequest moveRequest = MakeWebRequest("MOVE", "text/xml", draftsUri);
moveRequest.Headers.Add("Destination", submissionUri);
// Put the message in the mail submission folder.
HttpWebResponse moveResponse = MakeWebResponse(moveRequest);
moveResponse.Close();
}
private CredentialCache getCredentials(String URI)
{
CredentialCache tmpCreds = new CredentialCache();
tmpCreds.Add(new Uri(URI), "NTLM", new NetworkCredential(strMailBox, strPassword,strDomain ));
ServicePointManager.ServerCertificateValidationCallback = delegate(object sender, System.Security.Cryptography.X509Certificates.X509Certificate pCertificate, System.Security.Cryptography.X509Certificates.X509Chain pChain, System.Net.Security.SslPolicyErrors pSSLPolicyErrors)
{
return true;
};
return tmpCreds;
}
private HttpWebRequest MakeWebRequest(String method,String contentType,String URI)
{
HttpWebRequest tmpWebRequest;
tmpWebRequest = (HttpWebRequest)HttpWebRequest.Create(URI);
tmpWebRequest.Credentials = getCredentials (URI);
tmpWebRequest.Method = method;
tmpWebRequest.ContentType = contentType ;
return tmpWebRequest ;
}
private HttpWebResponse MakeWebResponse(HttpWebRequest webRequest)
{
HttpWebResponse tmpWebresponse = (HttpWebResponse)webRequest.GetResponse();
return tmpWebresponse;
}
WebResponse getMailsFromWebRequest(String strRootURI, String strQuerySearch)
{
HttpWebRequest SEARCHRequest;
WebResponse SEARCHResponse;
CredentialCache MyCredentialCache;
Byte[] bytes = null;
Stream SEARCHRequestStream = null;
try
{
MyCredentialCache = new CredentialCache();
MyCredentialCache.Add(new Uri(strRootURI ), "NTLM", new NetworkCredential(strMailBox.ToLower(), strPassword, strDomain));
SEARCHRequest = (HttpWebRequest)HttpWebRequest.Create(strRootURI );
ServicePointManager.ServerCertificateValidationCallback = delegate(object sender, System.Security.Cryptography.X509Certificates.X509Certificate pCertificate, System.Security.Cryptography.X509Certificates.X509Chain pChain, System.Net.Security.SslPolicyErrors pSSLPolicyErrors)
{
return true;
};
SEARCHRequest.Credentials = MyCredentialCache;
SEARCHRequest.Method = "SEARCH";
SEARCHRequest.ContentType = "text/xml";
bytes = Encoding.UTF8.GetBytes((string)strQuerySearch);
SEARCHRequest.ContentLength = bytes.Length;
SEARCHRequestStream = SEARCHRequest.GetRequestStream();
SEARCHRequestStream.Write(bytes, 0, bytes.Length);
SEARCHResponse =(HttpWebResponse ) SEARCHRequest.GetResponse();
SEARCHRequestStream.Close();
SEARCHRequest.Timeout = 300000;
System.Text.Encoding enc = System.Text.Encoding.Default;
if (SEARCHResponse == null)
{
Console.WriteLine("Response returned NULL!");
}
else
{
Console.WriteLine(SEARCHResponse.ContentLength);
}
return SEARCHResponse;
}
catch (Exception ex)
{
Console.WriteLine("Problem: {0}", ex.Message);
return null;
}
}
private String extractXMLFromWebResponse(WebResponse SearchResponse)
{
String tmpStream;
using(StreamReader strmReader = new StreamReader(SearchResponse.GetResponseStream(), System.Text.Encoding.ASCII))
{
tmpStream = strmReader.ReadToEnd();
strmReader.Close();
}
return tmpStream;
}
private List<MailStruct > extractMailPropertiesFromXMLString(String strXmlStream)
{
List<MailStruct> tmpListOfMailProperties = new List<MailStruct>();
XmlDocument doc = new XmlDocument();
doc.InnerXml = strXmlStream ;
XmlNamespaceManager xmlNameSpaces = new XmlNamespaceManager(doc.NameTable);
xmlNameSpaces.AddNamespace("a", "DAV:");
xmlNameSpaces.AddNamespace("f", "urn:schemas:httpmail:");
xmlNameSpaces.AddNamespace("d", "urn:schemas:mailheader:");
XmlNodeList mailNodes = doc.SelectNodes("//a:propstat[a:status='HTTP/1.1 200 OK']/a:prop", xmlNameSpaces);
foreach (XmlElement node in mailNodes)
{
tmpListOfMailProperties.Add(new MailStruct()
{
MailBody = node.SelectSingleNode("//f:textdescription",xmlNameSpaces ).InnerText ,
from = node.SelectSingleNode ("//d:from",xmlNameSpaces ).InnerText ,
To = "dfoster#liquidcapital.com",
Subject = node.SelectSingleNode("//d:subject",xmlNameSpaces ).InnerText.ToString () ,
Received = node.SelectSingleNode ("//f:datereceived",xmlNameSpaces ).InnerText.ToString ()
}
);
}
return tmpListOfMailProperties;
}
public struct MailStruct
{
public String To { get; set; }
public String from { get; set; }
public String Subject { get; set; }
public String Received { get; set; }
public String MailBody { get; set; }
}
}
}
Using just the subject to identify email is, indeed, not a good way. If I remember it's correct, there are no automatic / obvious email id for exchange / webdav?
If you parse the subject to pick message, I would also pick more information of the email envelope - Like it size, length to create my own id. The best step should be that you create some sort of hash (check Cryptography) out of the whole message body OR i.e. first character of first xx word in the email body (heavier processing though). That will end up in same hash value everytime you call it on same email envelope. Of until the email content is leaved unchanged.
Looks like you're working with Exchange 2003. Be aware that WebDAV is no longer supported in Exchange Version 2010... and with 2007 and newer you can use a WSDL to do everything you need. All you need is an Exchange 2007 CAS to do this.
What I mentioned is a better longer term approach and has less headaches than parsing unsupported WEBDAV XML
To answer your question, there is a property that is unique per message. Use MFCMapi (on codeplex) to look for it. The property will be named "MessageURL" or something like that. Use that property in the URL for your webdav calls.
Related
I am trying to send a file to a VM on a ESXi Server from an outside Client.
I am using the VMware SDK VMWare.Vim.dll.
When i try to use the URL given by the FileTransfer Request it Fails.
Code:
public static string ServerIP
{
get { return ConfigurationManager.AppSettings["ServerIP"].ToString(); }
}
private string ServiceUrl = "https://" + ServerIP + "/sdk";
private string UserName = "ESXIUser";
ServiceContent sc = new ServiceContent();
VimClient client = new VimClientImpl();
public VmOps()
{
}
public void Connect(string password)
{
// connect to vSphere web service
sc = client.Connect(ServiceUrl);
// Login using username/password credentials
client.Login(UserName, password);
}
public void copyDatastoreFile(/*string VMName, string Folder, string sourceFilePath*/)
{
string hostpass = "ESXIUserPAssword";
string VMName = "VMname";
string Folder = "Test";
string FileName = "test.zip";
//File destination info
string BasePath = "D:";
String targetDir = BasePath + "/" + Folder;
string srcPath = BasePath + "/" + Folder + "/" + FileName;
//Connect to the ESXi
Connect(hostpass);
NameValueCollection nvcFilter = new NameValueCollection();
nvcFilter.Add("Name", VMName);
var vm = (VirtualMachine)client.FindEntityView(typeof(VirtualMachine), null, nvcFilter, null);
GuestOperationsManager gom = new GuestOperationsManager(client, sc.GuestOperationsManager);
gom.UpdateViewData();
if (gom.FileManager == null)
{
return;
}
GuestFileManager gfm = new GuestFileManager(client, gom.FileManager);
var Auth = new NamePasswordAuthentication { Password = "VMPassword", Username = "VMUSER", InteractiveSession = false };
bool exists = false;
GuestListFileInfo fInfo = gfm.ListFilesInGuest(vm.MoRef, Auth, BasePath, null,null, Folder);
exists = fInfo.Files != null;
if (!exists)
{
// Create directory where the files will be copied to
gfm.MakeDirectoryInGuest(vm.MoRef, Auth, targetDir, true);
}
// Copy Virtual Machine file To
string URL = gfm.InitiateFileTransferToGuest(vm.MoRef,
Auth,
targetDir + "/" + FileName,
new GuestFileAttributes(),
new FileInfo(srcPath).Length,
true);
UploadFile(srcPath, URL.Replace("*", ServerIP));
}
private void UploadFile(string from, string to)
{
using (WebClient wc = new WebClient())
{
string cookie = ((VimApi_60.VimService)client.VimService).CookieContainer.GetCookies(new Uri(((VimApi_60.VimService)client.VimService).Url))[0].ToString();
wc.Credentials = new NetworkCredential("ESXiUSER", "ESXIUserPAssword");
wc.Headers.Add(HttpRequestHeader.Cookie, cookie);
try
{
wc.UploadFile(to, "PUT", from);
}
catch (WebException wex)
{
if (wex.Response != null)
{
string pageContent = new StreamReader(wex.Response.GetResponseStream()).ReadToEnd().ToString();
}
}
wc.Dispose();
}
}
URL Looks like:
"https://199.199.0.1:443/guestFile?id=22&token=23e2deef-7979-b1a8-75e5-413440d8c1377"
Cookie Looks like:
"vmware_soap_session="5e24eaa312s1245de2gg213456m23b4bd87c8e1ca"
WebException:
The remote Server returned an error:(500) Internal Server Error
ResponseStream:
\n 22\n The file Name is not valid
Anyone any experience on file Transfers with VMware SDK? Thank you for your help!
btw. it was a simple mistake, all slashs have to be switched with a double backslash. The VMWare sdk can handle the slash, but the webclient put can't.
example:
string targetDir = BasePath + "\\" + Folder;
string srcPath = BasePath + *"\\" + Folder + "\\" + FileName;
I've been having an issue with downloading webpages automatically with WebClient. Here are the steps my code runs through:
Retrieve HTML as string
Iterate through string, retrieving valid content urls (.js, .css, .png, etc.)
Download the content
Replace urls in HTML string with the content's local file path.
Save new HTML string to "main.html".
Everything is downloaded just fine. When I try to open up the html file in Chrome, it's a blank loading screen (varies from 10 seconds to 30 seconds). When the page finally loads, it looks like basic text and broken content.
The errors in the Chrome's Developer Tools suggest a lot of the .js and .css files aren't there, even though I've verified most of them are in the specified directory.
I have tried multiple sites, each with the same result.
Here is the code to retrieve the html data:
public string ScanPage(string url)
{
Console.WriteLine("Scanning url [" + url + "].");
WebClient client = new WebClient();
client.Headers.Add("user-agent", userAgent);
client.Headers.Add(HttpRequestHeader.ContentType, "text/html");
string page = string.Empty;
try
{
page = client.DownloadString(url);
Console.WriteLine("Webpage has been scanned.");
}
catch (Exception e)
{
Console.WriteLine("Error scanning page: " + e.Message);
}
client.Dispose();
return page;
}
Begin downloading data. This method is called first.
public void DownloadPageContent(string url, string contentDirectory, params string[] customExtensions)
{
//PathSafeURL(url) takes the url and removes unsafe characters
contentDirectory += PathSafeURL(url);
if (Directory.Exists(contentDirectory))
Directory.Delete(contentDirectory, true);
if (!Directory.Exists(contentDirectory))
Directory.CreateDirectory(contentDirectory);
Uri uri = new Uri(url);
string host = uri.Host;
//PageResponse is used to check for valid URLs. Irrelevant to the issue.
PageResponse urlResponse = CheckHttpPageResponse(url);
if (urlResponse.IsSuccessful())
{
//Get the html page as a string.
string data = ScanPage(url);
if (!string.IsNullOrEmpty(data))
{
//Download files with ".js" extension.
DownloadByExtension(ref data, ".js", contentDirectory + "/", "scripts/", host);
//Same as above, but with .css files.
DownloadByExtension(ref data, ".css", contentDirectory + "/", "css/", host);
//Iterate through custom extensions (.png, .jpg, .webm, etc.)
for (int i = 0; i < customExtensions.Length; i++)
DownloadByExtension(ref data, customExtensions[i], contentDirectory + "/", "resources/", host);
string documentDirectory = contentDirectory + "/main.html";
File.Create(documentDirectory).Dispose();
File.AppendAllText(documentDirectory, data);
Console.WriteLine("Page download has completed.");
}
else
Console.WriteLine("Error retrieving page data. Data was empty.");
}
else
Console.WriteLine("Page could not be loaded. " + urlResponse.ToString());
}
public void DownloadByExtension(ref string data, string extension, string contentDirectory, string subDirectory, string host)
{
List<HtmlContent> content = new List<HtmlContent>();
IterateContentLinks(data, extension, ref content, host);
CreateContent(contentDirectory, subDirectory, content);
for (int i = 0; i < content.Count; i++)
data = data.Replace(content[i].OriginalText + content[i].Extension, content[i].LocalLink);
Console.WriteLine("Downloaded " + content.Count + " " + extension + " files.");
Console.WriteLine();
}
private void IterateContentLinks(string data, string extension, ref List<HtmlContent> content, string host)
{
int totalCount = data.TotalCharacters(extension + "\"");
for (int i = 1; i < totalCount + 1; i++)
{
int extensionIndex = data.IndexOfNth(extension + "\"", i);
int backTrackIndex = extensionIndex - 1;
//Backtrack index from the extension index until you reach the first quotation mark.
while (data[backTrackIndex] != '"')
{
backTrackIndex -= 1;
}
string text = data.Substring(backTrackIndex + 1, (extensionIndex - backTrackIndex) - 1);
string link = text;
if (link.StartsWith("//"))
link = link.Insert(0, "http:");
if (link.StartsWith("/"))
link = link.Insert(0, "http://" + host);
if (!link.Contains("/")) //Assume it's in a "test.jpg" format.
link = link.Insert(0, "http://" + host + "/");
content.Add(new HtmlContent(text, link, extension));
}
//Remove repeating links
for (int i = 0; i < content.Count; i++)
{
for (int j = i + 1; j < content.Count; j++)
{
if (content[i].OriginalText == content[j].OriginalText)
content.Remove(content[i]);
}
}
}
private void CreateContent(string contentDirectory, string subDirectory, List<HtmlContent> content)
{
if (!Directory.Exists(contentDirectory + subDirectory))
Directory.CreateDirectory(contentDirectory + subDirectory);
Random random = new Random(Guid.NewGuid().GetHashCode());
for (int i = 0; i < content.Count; i++)
{
content[i].RandomName = Extensions.RandomSymbols(random, 20, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890");
content[i].LocalLink = contentDirectory + subDirectory + content[i].RandomName + content[i].Extension;
bool isSuccessful = false;
DownloadFile(content[i].DownloadLink + content[i].Extension, content[i].LocalLink, ref isSuccessful);
if (isSuccessful == false)
content.Remove(content[i]);
}
}
private void DownloadFile(string url, string filePath, ref bool isSuccessful)
{
using (WebClient client = new WebClient())
{
client.Headers.Add("user-agent", userAgent);
//client.Headers.Add(HttpRequestHeader.ContentType, "image/jpg");
try
{
client.DownloadFile(url, filePath);
isSuccessful = true;
}
catch
{
isSuccessful = false;
Console.WriteLine("File [" + url + "] could not be downloaded.");
}
}
}
HTMLContent class:
public class HtmlContent
{
public string OriginalText { get; private set; }
public string DownloadLink { get; private set; }
public string Extension { get; private set; }
public string LocalLink { get; set; }
public string RandomName { get; set; }
public HtmlContent(string OriginalText, string DownloadLink, string Extension)
{
this.OriginalText = OriginalText;
this.DownloadLink = DownloadLink;
this.Extension = Extension;
}
}
As a file downloader, this works good. For HTML downloading, it's good too. But as a complete offline webpage downloader, it does not.
EDIT:
Not sure if it matter, but I forgot to show what the userAgent variable looks like:
private const string userAgent = "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.33 Safari/537.36";
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();
}
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);
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