I'm looking for customer feedback regarding one or more of my apps (optional, and they would be notified), so I'd like to send some text to be appended to an online log file from my offline C# Winforms app.
If it makes a difference, the server is Linux based, and I don't mind if the public can access it too, so no need to worry about encryption or anything (no personal details or anything like that would be stored in them).
What would be the C# code required to do such a thing? (Pretend the website is: http://www.website.com/logfile.txt). Would I have to read the file wholesale, and write it back wholesale, or is there a more efficient 'append' operation I could use?
EDIT: Looks to be harder than I imagined. If I have to make a simple PHP script to help with this task, so be it, though code for that would be appreciated as well if that's the case.
You have a couple options.
First, you could check out a service like loggly which is an online log file. You would have a personal API key to post data to from your application.
If you don't want to do that, you could write your own API that has a simple Post with a string parameter. It would then be responsible for opening the file, adding the text and saving it. The Winform could just fire and forget, knowing that the API can handle it.
In C#, you could use either RestSharp or the HttpClient to send the data to the API.
I think the problem you're going to run into, is having the Winform app save the file. If it was a local file, you can just append to a file (without reading the whole thin into memory.) But on a remote machine, you'd first have to download the entire file. The next problem would be making sure that the Winform app can save the file. Without something like an API call, you could run into a lot of issues.
This may not be robust enough for your needs, but this my solution.
The easiest way I can think of doing this is to have your C# application send the text to a web script. Since you said you didn't care if the data was encrypted I thought why not just pass the text as a get parameter to a PHP script.
This example is very simplistic; you may want to add other checks to meet your needs:
The C# code would look like:
string loggerUrl = "http://www.YourDomainExample.com/Logger.php?text=";
string textToLog = WebUtility.UrlEncode("This text came from my C# desktop application");
HttpWebRequest myWebRequest = (HttpWebRequest)HttpWebRequest.Create(loggerUrl + textToLog);
HttpWebResponse myWebResponse = (HttpWebResponse)myWebRequest.GetResponse();
myWebResponse.Close();
The PHP script residing on your web server would look like:
<?php
$text = htmlspecialchars($_GET["text"]);
$log = "log.txt";
$fh = fopen($log, 'a') or die("can't open file"); // Open log in append mode
$textToWrite = "$text\n"; //Write each comment on a line
fwrite($fh, $textToWrite);
fclose($fh);
?>
By doing it this way, basically anything that can call the url can append text to your log. So your logger could be part of a desktop application, run on a mobile phone or a web application etc.
To test that your PHP script is working correctly, you can use your Web Browser as a client and just go to http://www.YourDomainExample.com/Logger.php?text=Test from webbrowser and check for log.txt on your web server
If you want to log into a remote destination, I see two solutions. Both are using log4net:
Solution 1:
You can set up log4net to log into a database. You can see here for the configuration.
Solution 2:
You can derive your logging class from AppenderSkeleton and configure the behaviour to log into anything you want.
internal class MyAppender : AppenderSkeleton
{
/// <summary>
/// Subclasses of <see cref="T:log4net.Appender.AppenderSkeleton"/> should implement this method
/// to perform actual logging.
/// </summary>
/// <param name="loggingEvent">The event to append.</param>
protected override void Append(LoggingEvent loggingEvent)
{
/* Here you can do whatever you want with your loggingEvent */
}
}
Related
i have a question, I have this code
foreach (string line in File.ReadLines(**#"C:\fis32v6\fis32.ini"**))
{
if (line.Contains("TEST1"))
{
Label1.Text="TEST1";
PdLine = "1";
}
}
DataSet ds;
ds = GetData(PdLine.ToString());
I want to read from txt file on client specific line with condition. When developing this and building the code it works, what ever i change in txt file can be read from my PC. But when I run the website on server it reads the txt file on that server instead of client I opened the website.
Is there any possibility to make the path relative?
As John mentioned that would be a huge security issue, mostly to make sure the website doesn't dig around in your system.
However it can be instigated from client side.
Just have search here on SO for 'upload file using asp.net' there are loads of hits with answers listed.
You didn't mention specific versions you use (MVC?, asp.net? / core?), and no context as to what workflow your code is running in (is it run on connection or during a specific process), is it configuration settings used for the web session itself? but it is possible to upload.
Should you require the file during startup that might be a bit trickier as you'd have to upload it somehow.
If however it is settings for the web session, why not save it in a cookie?
So I'm making a small chat application like ricochet, but then in C# and I succesfully connect to the tor controlport and create a hidden service id and private key, however after that point i got stuck, I send ADD_ONION NEW:BEST Port=8946,127.0.0.1:8946\r\n to the tor control port and it answers with hidden service id and private key and code 250. But what should I do to make it automatically run the service? I tried googling it but couldnt find anything and all examples are python or c++ if someone could point me in the right direction that would be great. Also, im using Knapcode.TorSharp, so the tor installation is NOT persistent, the user has a profile file where the key etc are saved and it should start from there.
Thanks in advance
When you call ADD_ONION, the hidden service starts running immediately (accessible once it can publish the Hidden Service descriptors and establish circuits [usually within a minute or two]).
If you want those services to start again automatically on subsequent runs (for a non-persisting Tor installation), then you'll need to programatically make similar calls to ADD_ONION when your application restarts and detects that private keys are saved to the profile.
You can re-create hidden services using existing keys with syntax like:
ADD_ONION RSA1024:*PKEY_GOES_HERE* Flags=DiscardPK Port=8946,8946
When you call ADD_ONION the first time, the response should look something like:
250-ServiceID=abcdefg123456
250-PrivateKey=RSA1024:MIICXAIBAAKBgQC91z4mjpNF5ddRL6jm7rnmgwSiQ6dNXF1Fo8sz1wOsGqWKgE4C6Bd3KT+zgQgXJlioIJOCEP9D0b/qlPCvEiGG3/fPEn1+Zpf5N4oNRI+in7J2m3xihhgAinbscJ0vM+1vfnRLlMrtYdE9J5aKle+t+OC6ZoXTxzPZRZkmXtqVpp8QIDAQPXAoGBAK7oh8zChBJch5u3i6jpvsIRaM2QA68VMKKfHPOwYSPKkUcgm7+10xjpGlXqxmd93yVYjk/CFU6JDIe3nmHPFK82BtPgyEMRtmVmcunS262Ead/ffpzAErBSdihOF7zO/wGjGgIaMW9Bhy69aK5LcNUB30Iu9+MWG62xz8tTgcEhAkEA8QNKMyKdRUbgGc9Gv1n8JtMs0Af/a/OHozdn1ywvHxw7mzahF936gqHIdg67XLtIj5TaUSM/44OoEvvURnG7QJBAMmlVttRd8y+/FnA6dPkesQMpPw+ipHLNUrf7qPrX3py670vLbprWDNYCOn6oaxoRtl/iRXPI5CgjMXmnu356pUCQDnWD0VMJi+MvZSUACbZXwP2ApP1bHfla3I7Xaezh5oDxtoAd0PS4STh1+HQUPvQW4WfLUcSsz9UaMAg2NI+fFUCQc7D1PVW7sqSGBth3jXE+3+H6WY2iy8Z1Ji+l2KRdJ8IiIOWdfcgUpMNzZV8jc7Y9Cm5p5l2wy7kjfGADyYBCXkCQD9fnmVMlUO1xITfW8K+pAf6FPcvfo8J0rpWHEhG4CxjFw4s4s9Mzjme1e17YnfK21CNIOxd2bkqVI4j4o=
250 OK
You'll want to save what it gave back in PrivateKey, and use this value to restart the hidden services the next time you run your application.
We have an internal error reporting system (inside our functions dll) and one of the info pieces we send is the name of the application that caused it.
Current code:
string applicationname= Assembly.GetCallingAssembly().GetName().Name;
The problem is when the error is send from one of our websites as it sends application names like "App_Code.6p_c415d".
One possible way was determining if the app is an executable or a website dinamically (how do we do that?) and in the case of being a website get the folder containing it or so...
But if you have better ways we are open to any idea ^^
You can use a key in the AppSettings to identify your application.
We ended up creating the following function and it works fine:
public static string getApplicationName()
{
return string.IsNullOrEmpty(HttpRuntime.AppDomainAppId)?
Assembly.GetEntryAssembly().GetName().Name :
new DirectoryInfo(HttpContext.Current.Server.MapPath("")).Name;
}
The problem:
My company puts out a monthly newsletter which I host on our internal website. I have a page for the author of the newsletter to upload the latest version. Once the author has uploaded the latest newsletter, he sends a broadcast email to announce the new newsletter. Employees invariably check the new newsletter and send feedback to the author with corrections that need to be made.
Once the author has made the necessary corrections (typically within an hour of sending the broadcast email), he revisits my page and replaces the latest version with the updated newsletter.
Immediately following the replacement (or update, if you will) of the newsletter, anyone attempting to access it gets a 500 - Internal Server Error.
My IT guy who maintains the server cannot delete/rename/move the file because of a permissions error and has to do a lot of convoluted things to get the file deleted (and once the file is deleted, the author of the newsletter can re-upload the corrected copy and it works fine.
My IT guy and I are pretty sure that the problem stems from that I'm trying to replace the file while IIS is actively serving it to users (which I thought of and thought that I had coded against happening).
The code that runs the replacement is as follows:
Protected Sub ReplaceLatestNewsletter()
Dim dr As DataRow
Dim sFile As String
Dim mFileLock As Mutex
Try
If Me.Archives.Rows.Count > 0 Then
dr = Me.Archives.Rows(0)
sFile = dr("File").ToString
If dr("Path").ToString.Length > 0 Then
mFileLock = New Mutex(True, "MyMutexToPreventReadsOnOverwrite")
Try
mFileLock.WaitOne()
System.IO.File.Delete(dr("Path").ToString)
Catch ex As Exception
lblErrs.Text = ex.ToString
Finally
mFileLock.ReleaseMutex()
End Try
End If
fuNewsletter.PostedFile.SaveAs(Server.MapPath("~/Newsletter/archives/" & sFile))
End If
Catch ex As Exception
lblErrs.Text = ex.ToString
End Try
dr = Nothing
sFile = Nothing
mFileLock = Nothing
End Sub
I thought the Mutex would take care of this (although after re-reading documentation I'm not sure I can actually use it like I'm trying to). Other comments on the code above:
Me.Archives is a DataTable stored in ViewState
dr("File").ToString is the filename (no path)
dr("Path").ToString is the full local machine path and filename (i.e., 'C:\App_Root\Newsletters\archives\20120214.pdf')
The filenames of the newsletters are set to "YYYYMMDD.pdf" where YYYYMMDD is the date (formatted) of the upload.
In any case, I'm pretty sure that the code above is not establishing an exclusive lock on the file so that the file can be overwritten safely.
Ultimately, I would like to make sure that the following happens:
If IIS is currently serving the file, wait until IIS has finished serving it.
Before IIS can serve the file again, establish an exclusive lock on the file so that no other process, thread, user (etc.) can read from or write to the file.
Either delete the file entirely and write a new file to replace it or overwrite the existing file with the new content.
Remove the exclusive lock so that users can access the file again.
Suggestions?
Also, can I use a Mutex to get a mutually exclusive lock on a file in the Windows filesystem?
Thank you in advance for your assistance and advice.
EDIT:
The way that the links for the newsletter are generated is based on the physical filename. The method used is:
Get all PDF files in the "archives" directory. For each file:
Parse the date of publication from the filename.
Store the date, the path to the file, the filename, and a URL to each file in a DataRow in a DataTable
Sort the DataTable by date (descending).
Output the first row as the current issue.
Output all subsequent rows as "archives" organized by year and month.
UPDATE:
In lieu of not being able to discern when all existing requests for that file have completed, I took a closer look at the first part of #Justin's answer ("your mutex will only have an effect if the process that reads from the file also obtains the same mutex.")
This led me to Configure IIS7 to server static content through ASP.NET Runtime and the linked article in the accepted answer.
To that end, I have implemented a handler for all PDF files which implements New Mutex(True, "MyMutexToPreventReadsOnOverwrite") to ensure that only one thread is doing something with the PDF at any given time.
Thank you for you answer, #Justin. While I did not wind up using the implementation you suggested, your answer pointed me towards an acceptable solution.
Your mutex will only have an effect if the process that reads from the file also obtains the same mutex. What is the method used to serve up the file? Is ASP.Net used or is this just a static file?
My workflow would be a little different:
Write the new newsletter to a new file
Have IIS start serving up the new file instead of the old one for the given Newsletter url
Delete the old file once all existing requests for that file have completed
This requires no locking and also means that we don't need to wait for requests for the current file be completed (something which could potentially take an indefinite amount of time if people keep on making new requests). The only interesting bit is step 2 which will depend on how the file is served - the easiest way would probably be to either set up a HTTP redirect or use URL rewriting
HTTP Redirect
A HTTP Redirect is where the server tells the client to look in a different place when it gets a request for a given resource so that the browser URL is automatically updated to match the new location. For example if the user requested http://server/20120221.pdf then they could be automatically redirected to another URL such as http://server/20120221_v2.pdf (the URL shown in the browser would change however the URL they need to type in would not).
You can do this in IIS 7 using the httpRedirect configuration element, for example:
<configuration>
<system.webServer>
<httpRedirect enabled="true" exactDestination="true" httpResponseStatus="Found">
<!-- Note that I needed to add a * in for IIS to accept the wildcard even though it isn't used in this case -->
<add wildcard="*20120221.pdf" destination="20120221_v2.pdf" />
</httpRedirect>
</system.webServer>
</configuration>
The linked page shows how to change these settings from ASP.Net
Url Rewriting
Alternatively IIS can be set up to automatically serve up the content of a different file for a given URL without the client (the browser) ever knowing the difference. This is called URL rewriting and can be done in IIS using something like this however it does require that additional components be installed to IIS to work.
Using a HTTP Redirect is probably the easiest method.
I have written an ASP.NET web page with C# behind that runs an existing vb script.
The idea is that the user uploads an Excel spreadsheet (.xls) using the web page, the C# does a few basic checks on the file (file type, file name etc) then saves the .xls to a network location.
The C# then passes the network path of the .xls to the vb script, which gets the required information from the .xls to create a .csv file.
Finally the .csv is passed into a stored procedure and uploaded to a database table.
The problem is that all this runs perfectly when I run the webpage locally on my machine. However when I upload the page to the webserver it does not seem to execute the vb script; instead it just sits there waiting for the script to exit.
Some quick info:
Excel is installed on the web server
The website is set to execute scripts and executables
The script is currently set to 'run as' my personal domain login (this has to change) which has admin on the web server
If I run the script on the webserver using the cmd prompt it works
I'd really appreciate any ideas on what might be going wrong... seriously, I'm pulling my hair out over this one and will consider any idea, no matter how crazy... but, and it's a big one, despite the fact that that there are many other ways of achieving the same result, I'm afraid that for a number of reasons this is what I have to work with :)
Edit
Here is how I call the script
try
{
System.Security.SecureString password = new System.Security.SecureString();
string uspw = "mypassword";
foreach (char c in uspw)
{
password.AppendChar(c);
}
Process scriptProc = new Process();
scriptProc.StartInfo.FileName = #"cscript";
scriptProc.StartInfo.Arguments = scriptPath + " //Nologo " + uploadPath + xlsFileName;
scriptProc.StartInfo.WindowStyle = ProcessWindowStyle.Minimized;
scriptProc.StartInfo.UserName = "myusername";
scriptProc.StartInfo.Password = password;
scriptProc.StartInfo.UseShellExecute = false;
scriptProc.Start();
scriptProc.WaitForExit();
scriptProc.Close();
}
catch...
All of the file paths are relative.
The code seems to fail when the script is called. You can clearly see the page waiting for the script to finish. However if you watch the task manager on the web server neither cscript or excel start to run.
I've also stuck a message box right at the start of the script which does not get displayed
Edit 2
It turns out that cscript is running, I just needed to tick the 'All Users' check box in the task manager... I'm still none the wiser though!
Thanks so much in advance
Sounds like you are using automation to control the Excel application itself?
Some quick info:
Excel is installed on the web server
That is generally a bad idea, because the Excel application is not an application that is intended to be automated by a server. Thing might hang because the application is waiting for user input in a dialog somewhere. And it's not scalable for handling operations from multiple users simultaneously.
If the final goal is to extract the data from the excel file and put it in an sql server, I would rather suggest that you use the Jet OLEDB provider to retrieve the data from the excel file, either from your web application, and letting that feed the data into sql server, or let the sql server do it directly. If there is a lot of data in the excel file, the latter might be the best choice
Without seeing the code this is a blind guess - I suggest you check how you are specifying the path to the vb script - make sure you are not using an absolute path, and that the file is in the same location relative to the C# page on the server as it is on your machine.