wkhtmltopdf and forms based authentication - pdf-generation

I have an application that uses forms based authentication. I'm trying to take a copy of a page within the application and save it to PDF using wkhtmltopdf.
wkhtmltopdf is installed and works fine. I can call it successfully from within the application to generate a PDF of another website without authentication (i.e. bbc.co.uk).
I cannot get it to pass username/password to the application in order to generate a copy of the desired page. Instead I get a PDF of the signon screen.
Both client and server are Windows (7 and 2008 respectively). I'm using the command:
wkhtmltopdf.exe --post userid=xxx --post pwd=yyy --ignore-load-errors http://url.com/blah/blah/ test.pdf
Where xxx is my username and yyy is my password. I've been through the source of the logon page and the ids of the form fields are userid and pwd so I believe I have the post parameters in command line correct.
After thoroughly googling I can see mentions to a --cookie-jar parameter, but haven't managed to work out its usage, or even if it'll help.
thanks
Duncan

If you're using ASP.NET Forms Authentication, you can forward the current user's authentication cookies along to wkhtmltopdf via a series of --cookie args. This is assuming that an authenticated use (from whom you can obtain auth cookies) is triggering the PDF printout.
Note: This is not complete working code for launching wkhtmltopdf.exe from a .NET app, but merely an example of how to pass along cookies to wkhtmltopdf.
For an example of launching wkhtmltopdf.exe from .NET, see: how to pass html as a string using wkhtmltopdf?
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = "wkhtmltopdf.exe";
string cookieArgs = "";
var cookies = HttpContext.Current.Request.Cookies;
if (cookies != null)
{
var sb = new System.Text.StringBuilder();
// you probably only need the ".ASPXFORMSAUTH"
// and "ASP.NET_SessionId" cookies
// but I pass everything just in case
foreach (string key in cookies.AllKeys)
{
string value = cookies[key].Value;
sb.AppendFormat("--cookie {0} {1} ", key, value);
}
cookieArgs = sb.ToString();
}
psi.Arguments = urlToPrint + " -q " + cookieArgs + " -";
Process.Start(psi);

This issue is now resolved. I was using version 0.9.9 and couldn't get it to work. As soon as I moved to version 0.10.0 rc2 it worked fine.
If anyone else is trying the same thing, the line of code I used was as above, I didn't need the --cookie-jar parameter.

Related

Xamarin Android share PDF. Permission denied for the attachment [duplicate]

My app creates mails with attachments, and uses an intent with Intent.ACTION_SEND to launch a mail app.
It works with all the mail apps I tested with, except for the new Gmail 5.0 (it works with Gmail 4.9), where the mail opens without attachment, showing the error: "Permission denied for the attachment".
There are no useful messages from Gmail on logcat. I only tested Gmail 5.0 on Android KitKat, but on multiple devices.
I create the file for the attachment like this:
String fileName = "file-name_something_like_this";
FileOutputStream output = context.openFileOutput(
fileName, Context.MODE_WORLD_READABLE);
// Write data to output...
output.close();
File fileToSend = new File(context.getFilesDir(), fileName);
I'm aware of the security concerns with MODE_WORLD_READABLE.
I send the intent like this:
public static void compose(
Context context,
String address,
String subject,
String body,
File attachment) {
Intent emailIntent = new Intent(Intent.ACTION_SEND);
emailIntent.setType("message/rfc822");
emailIntent.putExtra(
Intent.EXTRA_EMAIL, new String[] { address });
emailIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
emailIntent.putExtra(Intent.EXTRA_TEXT, body);
emailIntent.putExtra(
Intent.EXTRA_STREAM,
Uri.fromFile(attachment));
Intent chooser = Intent.createChooser(
emailIntent,
context.getString(R.string.send_mail_chooser));
context.startActivity(chooser);
}
Is there anything I do wrong when creating the file or sending the intent? Is there a better way to start a mail app with attachment? Alternatively - has someone encountered this problem and found a workaround for it?
Thanks!
I was able to pass a screenshot .jpeg file from my app to GMail 5.0 through an Intent. The key was in this answer.
Everything I have from #natasky 's code is nearly identical but instead, I have the file's directory as
context.getExternalCacheDir();
Which "represents the external storage directory where you should save cache files" (documentation)
GMail 5.0 added some security checks to attachments it receives from an Intent. These are unrelated to unix permissions, so the fact that the file is readable doesn't matter.
When the attachment Uri is a file://, it'll only accept files from external storage, the private directory of gmail itself, or world-readable files from the private data directory of the calling app.
The problem with this security check is that it relies on gmail being able to find the caller app, which is only reliable when the caller has asked for result. In your code above, you do not ask for result and therefore gmail does not know who the caller is, and rejects your file.
Since it worked for you in 4.9 but not in 5.0, you know it's not a unix permission problem, so the reason must be the new checks.
TL;DR answer:
replace startActivity with startActivityForResult.
Or better yet, use a content provider.
Use getExternalCacheDir() with File.createTempFile.
Use the following to create a temporary file in the external cache directory:
File tempFile = File.createTempFile("fileName", ".txt", context.getExternalCacheDir());
Then copy your original file's content to tempFile,
FileWriter fw = new FileWriter(tempFile);
FileReader fr = new FileReader(Data.ERR_BAK_FILE);
int c = fr.read();
while (c != -1) {
fw.write(c);
c = fr.read();
}
fr.close();
fw.flush();
fw.close();
now put your file to intent,
emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(tempFile));
You should implement a FileProvider, which can create Uris for your app's internal files. Other apps are granted permission to read these Uris. Then, simply instead of calling Uri.fromFile(attachment), you instantiate your FileProvider and use:
fileProvider.getUriForFile(attachment);
Google have an answer for that issue:
Store the data in your own ContentProvider, making sure that other apps have the correct permission to access your provider. The preferred mechanism for providing access is to use per-URI permissions which are temporary and only grant access to the receiving application. An easy way to create a ContentProvider like this is to use the FileProvider helper class.
Use the system MediaStore. The MediaStore is primarily aimed at video, audio and image MIME types, however beginning with Android 3.0 (API level 11) it can also store non-media types (see MediaStore.Files for more info). Files can be inserted into the MediaStore using scanFile() after which a content:// style Uri suitable for sharing is passed to the provided onScanCompleted() callback. Note that once added to the system MediaStore the content is accessible to any app on the device.
Also you can try set permissions for your file:
emailIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
And finally you can copy/store your files in external storage - permissions not needed there.
I tested it and I found out that it was definitely private storage access problem.
When you attach some file to Gmail (over 5.0) do not use the file from private storage such as /data/data/package/. Try to use /storage/sdcard.
You can successfully attach your file.
Not sure why GMail 5.0 doesn't like certain file paths (which I've confirmed it does have read access to), but an apparently better solution is to implement your own ContentProvider class to serve the file. It's actually somewhat simple, and I found a decent example here: http://stephendnicholas.com/archives/974
Be sure to add the tag to your app manifest, and include a "android:grantUriPermissions="true"" within that. You'll also want to implement getType() and return the appropriate MIME type for the file URI, otherwise some apps wont work with this... There's an example of that in the comment section on the link.
I was having this problem and finally found an easy way to send email with attachment. Here is the code
public void SendEmail(){
try {
//saving image
String randomNameOfPic = Calendar.DAY_OF_YEAR+DateFormat.getTimeInstance().toString();
File file = new File(ActivityRecharge.this.getCacheDir(), "slip"+ randomNameOfPic+ ".jpg");
FileOutputStream fOut = new FileOutputStream(file);
myPic.compress(Bitmap.CompressFormat.JPEG, 100, fOut);
fOut.flush();
fOut.close();
file.setReadable(true, false);
//sending email
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_EMAIL, new String[]{"zohabali5#gmail.com"});
intent.putExtra(Intent.EXTRA_SUBJECT, "Recharge Account");
intent.putExtra(Intent.EXTRA_TEXT, "body text");
//Uri uri = Uri.parse("file://" + fileAbsolutePath);
intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(file));
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivityForResult(Intent.createChooser(intent, "Send email..."),12);
}catch (Exception e){
Toast.makeText(ActivityRecharge.this,"Unable to open Email intent",Toast.LENGTH_LONG).show();
}
}
In this code "myPic" is bitmap which was returned by camera intent
Step 1: Add authority in your attached URI
Uri uri = FileProvider.getUriForFile(context, ""com.yourpackage", file);
Same as your manifest file provide name
android:authorities="com.yourpackage"
Step 2`; Add flag for allow to read
myIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

ASP.NET Core in Docker /app/Views/Email not found

I have an ASP.NET Core and I am using template to sent email notification for user registration and reset password. Everything is working fine when I run the application on localhost, but when I build the image and run in a Linux Ubuntu Server I get the error /app/Views/Email not found
This is how I a getting the view:
var razorPage = GetRazorPage("Views/Email");
private RazorLightEngine GetRazorPage(string view)
{
string currentDirectory = Directory.GetCurrentDirectory();
var fullPath = Path.Combine(currentDirectory, view);
return new RazorLightEngineBuilder().UseFilesystemProject(fullPath).UseMemoryCachingProvider().Build();
}
The problem was that the file was not available in Docker. I modify the file properties and it solved the issue.

Google Drive SDK 1.8.1 RedirectURL

Is there any way to provide RedirectURL then using GoogleWebAuthorizationBroker?
Here is the sample code in C#:
Task<UserCredential> credential = GoogleWebAuthorizationBroker.AuthorizeAsync(secrets, scopes, GoogleDataStore.User, cancellationToken, dataStore);
Or we have to use different approach?
I have an "installed application" that runs on a user's desktop, not a website. By default, when I create an "installed application" project in the API console, the redirect URI seems to be set to local host by default.
What ends up happening is that after the authentication sequence the user gets redirected to localhost and receives a browser error. I would like to prevent this from happening by providing my own redirect URI: urn:ietf:wg:oauth:2.0:oob:auto
This seems to be possible using Python version of the Google Client API, but I find it difficult to find any reference to this with .NET.
Take a look in the implementation of PromptCodeReceiver, as you can see it contains the redirect uri.
You can implement your own ICodeReceiver with your prefer redirect uri, and call it from a WebBroker which should be similar to GoogleWebAuthorizationBroker.
I think it would be great to understand why can't you just use PrompotCodeReceiver or LocalServerCodeReceiver.
And be aware that we just released a new library last week, so you should update it to 1.9.0.
UPDATE (more details, Nov 25th 2014):
You can create your own ICodeReceiver. You will have to do the following:
* The code was never tested... sorry.
public class MyNewCodeReceiver : ICodeReceiver
{
public string RedirectUri
{
get { return YOU_REDIRECT_URI; }
}
public Task<AuthorizationCodeResponseUrl> ReceiveCodeAsync(
AuthorizationCodeRequestUrl url,
CancellationToken taskCancellationToken)
{
// YOUR CODE HERE FOR RECEIVING CODE FROM THE URL.
// TAKE A LOOK AT THE FOLLOWING:
// PromptCodeReceiver AND LocalServerCodeReceiver
// FOR EXAMPLES.
}
}
PromptCodeReceiver
and LocalServerCodeReceiver.
Then you will have to do the following
(instead of using the GoogleWebAuthorizationBroker.AuthorizeAsync method):
var initializer = new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = secrets,
Scopes = scopes,
DataStore = new FileDataStore("Google.Apis.Auth");
};
await new AuthorizationCodeInstalledApp(
new GoogleAuthorizationCodeFlow(initializer),
new MyNewCodeReceiver())
.AuthorizeAsync(user, taskCancellationToken);
In addition:
I'll be happy to understand further why you need to set a different redirect uri, so we will be able to improve the library accordingly.
When I create an installed application the current PromptCodeReceiver and LocalServerCodeReceiver work for me, so I'm not sure what's the problem with your code.

WP7 browser hangs when returning after successful WebBrowserTask.Show

I have a WP7 application that uses WebBrowserTask.Show to launch a page in the browser. The WebBrowserTask.URL value is escaped using Uri.EscapeDataString (there are multiple query string parameters in the URL) and the page is displayed correctly in the browser.
On WP OS 7.0, after leaving the browser, and then starting the browser again from the Start page, the page load progress indicator stops about half way and the page does not load correctly.
If the application is returned to and the WebBrowserTask.Show is used to launch the same page in the browser again, the browser load also hangs.
On WP OS 7.1, after leaving the browser and then starting the browser again, the page loads correctly.
Any insight into what the issue is with WP OS 7.0? Are there any fixes or work arounds? I have an important segment of users who won't be on Mango for a few months.
I don't think Uri.EscapeDataString() for the entire URL is the right method to use here. See for example:
string uriString =
"https://www.google.com/search?gcx=w&sourceid=chrome&ie=UTF-8&q=test query";
string escapedDataString = Uri.EscapeDataString(uriString);
"https%3A%2F%2Fwww.google.com%2Fsearch%3Fgcx%3Dw%26sourceid%3Dchrome%26ie%3DUTF-8%26q%3Dtest%20query"
string escapedUriString = Uri.EscapeUriString(uriString);
"https://www.google.com/search?gcx=w&sourceid=chrome&ie=UTF-8&q=test%20query"
It seems like EscapeDataString completely breaks the URL by escaping everything including the "https://" prefix while EscapeUriString just escapes what needs to be escaped.
According to the documentation you should "Use the EscapeUriString method to prepare an unescaped URI string to be a parameter to the Uri constructor.".
Personally - I had always only escaped the parameters of the Uri I was building, so for example if a parameter value included an ampersand - I could force escaping it instead of relying on EscapeUriString to know everything.
Apparently - "https%3A%2F%2Fwww.google.com%2Fsearch%3Fgcx%3Dw%26sourceid%3Dchrome%26ie%3DUTF-8%26q%3Dtest%20query" is not a valid URL.
*EDIT
I must have missed that WP7.0 only had a string URL property now obsolete on WP7.1 and that property indeed requires escaping the entire URL using EscapeDataString. I tried it in both project types - targetting 7.0 and 7.1 and did not notice any hang. The only difference was that 7.0 would always tombstone my app, while 7.1 would not. Perhaps there is some other code in your application that blocks when you come back from being tombstoned. Here is my code:
string uriString =
"https://www.google.com/search?gcx=w&sourceid=chrome&ie=UTF-8&q=test query";
string escapedDataString = Uri.EscapeDataString(uriString);
var wbt = new WebBrowserTask();
wbt.URL = escapedDataString;
wbt.Show();
Another idea - escape the uri string twice - first using the EscapeUriString call for entire address or calling EscapeDataString just for the parameters when building the string and then escape the entire resulting uri again using EscapeDataString.

Using Pgp.exe from a MVC application

I've been tasked with converting a legacy application to mvc. The app used pgp.exe to pgp sign user input and send it as an email. The application works locally and on a test server but won't run on a live server. I've had to jump though hoops such as running a specified user in the application pool so that we can set the keys in the users profile BUT it worked.
For some reason on the live server which is windows 2003 IIS 6 and identical to the testing server it fails. The problem is pgp.exe just wont seem to sign and create files the message I get from the console out put is. "Signature Error"?? When I put the command into a shell window logged in as the app pool user it runs no problem (after a fight with some permissions) but when running through the mvc application/IIS server it fails. The code used to call the process is below.
var startInfo = new ProcessStartInfo();
startInfo.FileName = _pgpexeLocation;
//startInfo.FileName = "pgp.exe";
startInfo.Arguments = string.Format("-sta \"{0}\" -u keyuser-z keypass +COMPATIBLE +FORCE", _tempFilePath);
startInfo.UseShellExecute = false;
startInfo.RedirectStandardOutput = true;
startInfo.LoadUserProfile = true;
using (Process exeProcess = Process.Start(startInfo))
{
// TODO: set limit to wait for and deal with exit
exeProcess.WaitForExit();
//var stringItem = exeProcess.StandardOutput.ReadToEnd();
//Logger.Info(stringItem);
}
I'm clutching at straws here hoping somebody has done something similar before and can help. I'm guessing it's key location or file location not being picked up somewhere but not sure what else to try?
Turns out that even though the app pool was using a specific user and I'd set the keys up in that users appdata folder when I checked the underlying process call it was actually trying to pick the keys up from the Default User profile. Not sure if this was an IIS config or something similar but moving the keys and pgp folder to this appdata instead worked?

Resources