January 5th, 2009
Microsoft has some security protections to prevent direct access to an input field of type file. The most common solution I’ve come across is to either just click the browse button, which fails if you are trying to automatically fill something in, or to use SendKeys, which fails if the user doesn’t have focus on the application at the moment. Taking a route similar to SendKeys, we can hook in at a lower level, and instead of simulating key presses to the current application, we can send the key presses directly to the WebBrowser document. This solves the problem of the application having to be focused.
public class ExtendedWebBrowser : WebBrowser
{
const int WM_KEYDOWN = 0x100;
const int WM_KEYUP = 0x101;
const int WM_CHAR = 0x102;
[DllImport("user32.dll")]
static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd);
[DllImport("user32.dll")]
static extern byte VkKeyScan(char ch);
[DllImport("user32.dll")]
static extern bool PostMessage(IntPtr hWnd, uint msg, int wParam, int lParam);
/// <summary>
/// Fills in an input type="file" form field.
/// </summary>
/// <param name="fileElement">The html file input element.</param>
/// <param name="file">The path to the file to be uploaded.</param>
public void FillFileInput(HtmlElement fileElement, string file)
{
uint child = 5;
//Obtain the handle to the WebBrowser's document.
IntPtr documentHandle = GetWindow(GetWindow(GetWindow(Handle, child), child), child);
fileElement.Focus();
foreach (char c in file)
{
PostMessage(documentHandle, WM_KEYDOWN, VkKeyScan(c), 0);
PostMessage(documentHandle, WM_CHAR, (int)c, 0);
PostMessage(documentHandle, WM_KEYUP, VkKeyScan(c), 0);
}
Application.DoEvents();
}
}
Now filling in an input of type file is as easy as calling FillFileInput with the input HtmlElement, and the path to the file.
Categories: C#
December 25th, 2008
I’ve read a lot about this topic over the last couple hours, and all information I found seemed to be going about it the wrong way. Many people suggest changing the registry, but from what I tried it seemed to only allow you to change certain parts of the user agent, instead of the whole string. If you’re interested in this, you’ll want to change:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\5.0\User Agent
Changing part of the User Agent wasn’t the goal though. The next idea I came across was that the WebBrowser.Navigate method took a headers string option that let you inject your own user agent into it. This would be useful if I was using the Navigate method instead of allowing users to click links and submit forms on pages. Luckily I ran into a COM method that lets you intercept all navigations, by form submits, link clicks, or anything else. Using this method, we can cancel the current navigation before it happens, edit the headers, and start the navigation again. I have extended the WebBrowser class so it allows simply setting of the UserAgent property to allow this.
public class ExtendedWebBrowser : WebBrowser
{
bool renavigating = false;
public string UserAgent { get; set; }
public ExtendedWebBrowser()
{
DocumentCompleted += SetupBrowser;
//this will cause SetupBrowser to run (we need a document object)
Navigate("about:blank");
}
void SetupBrowser(object sender, WebBrowserDocumentCompletedEventArgs e)
{
DocumentCompleted -= SetupBrowser;
SHDocVw.WebBrowser xBrowser = (SHDocVw.WebBrowser)ActiveXInstance;
xBrowser.BeforeNavigate2 += BeforeNavigate;
DocumentCompleted += PageLoaded;
}
void PageLoaded(object sender, WebBrowserDocumentCompletedEventArgs e)
{
}
void BeforeNavigate(object pDisp, ref object url, ref object flags, ref object targetFrameName,
ref object postData, ref object headers, ref bool cancel)
{
if (!string.IsNullOrEmpty(UserAgent))
{
if (!renavigating)
{
headers += string.Format("User-Agent: {0}\r\n", UserAgent);
renavigating = true;
cancel = true;
Navigate((string)url, (string)targetFrameName, (byte[])postData, (string)headers);
}
else
{
renavigating = false;
}
}
}
}
Note: To use the method above you’ll need to add a COM reference to “Microsoft Internet Controls”.
I also stumbled across a PInvoke method that had a simpler solution. The following code shows how to change the user agent for the WebBrowser control to GoogleBot’s user agent.
[DllImport("urlmon.dll", CharSet = CharSet.Ansi)]
private static extern int UrlMkSetSessionOption(int dwOption, string pBuffer, int dwBufferLength, int dwReserved);
const int URLMON_OPTION_USERAGENT = 0x10000001;
public void ChangeUserAgent()
{
string ua = "Googlebot/2.1 (+http://www.google.com/bot.html)";
UrlMkSetSessionOption(URLMON_OPTION_USERAGENT, ua, ua.Length, 0);
}
After calling that method, you can just use your WebBrowser as normal. All pages it navigates to will use the user agent you specified. For more information, you can check out the documentation on UrlMkSetSessionOption. The only problem with this method is that the WebBrowser control seems to cache this user agent string, so it will not change the user agent without restarting the process.
Categories: C#
October 14th, 2008
So you may be getting an error like this after installing openssh and trying to build ruby again from source:
/usr/local/lib/ruby/1.8/i686-linux/digest/md5.so: /usr/local/lib/ruby/1.8/i686-linux/digest/md5.so: undefined symbol: rb_Digest_MD5_Init - /usr/local/lib/ruby/1.8/i686-linux/digest/md5.so (LoadError)
The problem as I soon figured out is that you forgot to run
make clean
That way the md5 lib will be rebuilt with openssh support since it is built fine without openssh support.
Categories: Ruby
September 23rd, 2008
Well, it took me awhile to figure this out so hopefully it will help somebody else.
git reset --hard 9bb0d4ff290d651d1299efd48b420decfc3c90c6
Where 9bb0d4ff290d651d1299efd48b420decfc3c90c6 is the hash of the commit you want to go back to (listed in git log).
Categories: Git
July 30th, 2008
When deploying with capistrano, using git, and rails as a submodule in vendor/rails, you might run into the following error:
** [out :: domain] (in /var/www/apps/domain/releases/20080730043012)
** [out :: domain] rake aborted!
** [out :: domain] no such file to load –/var/www/apps/domain/releases/20080730043012/config/../vendor/rails/railties/lib/initializer
** [out :: domain] /var/www/apps/domain/releases/20080730043012/Rakefile:4
** [out :: domain] (See full trace by running task with –trace)
When you check to see why it can’t find the file you’ll see /vendor/rails is empty. Once you understand why it’s empty (because it’s a git submodule), you’ll realize that capistrano isn’t doing the necessary commands to initialize that submodule. These commands are:
git submodule init
git submodule update
Luckily, capistrano has added support for this by using the following in your deploy.rb.
set :git_enable_submodules, true
That will cause capistrano to execute the necessary commands for submodules:
if configuration[:git_enable_submodules]
execute << "#{git} submodule #{verbose} init"
execute << "#{git} submodule #{verbose} update"
end
Categories: Rails
July 30th, 2008
I had an issue a few hours ago where I was getting the following message when running script/server after trying to do a deployment.
super: no superclass method `require' (NoMethodError)
The problem seemed to be a mix of haml and rails 2.1. It worked on my development machine though, so there is probably something more to it than that.
After spending some time trying to find the issue I eventually found that this was the reason of failure :
(/usr/lib/ruby/gems/1.8/gems/haml-2.0.1/lib/haml.rb:1033):
%w[haml/template sass sass/plugin].each(&method(:require))
I wasn’t totally sure what that is expected to do. I figured at first maybe it was supposed to be a Symbol#to_proc conversion, but method isn’t a symbol, so I’m still not totally sure what that is supposed to do. Anyway, what’s important is that I replaced it with:
%w[haml/template sass sass/plugin].each { |x| require x }
After that everything worked just fine.
The full trace I received with the error is listed below:
=> Booting Mongrel (use 'script/server webrick' to force WEBrick)
=> Rails 2.1.0 application starting on http://0.0.0.0:1200
=> Call with -d to detach
=> Ctrl-C to shutdown server
** Starting Mongrel listening at 0.0.0.0:1200
** Starting Rails with production environment...
Exiting
/var/www/apps/domain/releases/20080730050116/vendor/rails/activesupport/lib/active_support/dependencies.rb:144:in `require': super: no superclass method `require' (NoMethodError)
from /var/www/apps/domain/releases/20080730050116/vendor/rails/activesupport/lib/active_support/dependencies.rb:503:in `new_constants_in'
from /var/www/apps/domain/releases/20080730050116/vendor/rails/activesupport/lib/active_support/dependencies.rb:144:in `require'
from /var/www/apps/domain/releases/20080730050116/vendor/rails/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb:36:in `to_proc'
from /usr/lib/ruby/gems/1.8/gems/haml-2.0.1/lib/haml.rb:1033:in `each'
from /usr/lib/ruby/gems/1.8/gems/haml-2.0.1/lib/haml.rb:1033:in `init_rails'
from /var/www/apps/domain/releases/20080730050116/vendor/plugins/haml/init.rb:7:in `evaluate_init_rb'
from ./script/../config/../vendor/rails/railties/lib/rails/plugin.rb:103:in `evaluate_init_rb'
from /var/www/apps/domain/releases/20080730050116/vendor/rails/activesupport/lib/active_support/core_ext/kernel/reporting.rb:11:in `silence_warnings'
from ./script/../config/../vendor/rails/railties/lib/rails/plugin.rb:99:in `evaluate_init_rb'
from ./script/../config/../vendor/rails/railties/lib/rails/plugin.rb:44:in `load'
from ./script/../config/../vendor/rails/railties/lib/rails/plugin/loader.rb:33:in `load_plugins'
from ./script/../config/../vendor/rails/railties/lib/rails/plugin/loader.rb:32:in `each'
from ./script/../config/../vendor/rails/railties/lib/rails/plugin/loader.rb:32:in `load_plugins'
from ./script/../config/../vendor/rails/railties/lib/initializer.rb:311:in `load_plugins'
from ./script/../config/../vendor/rails/railties/lib/initializer.rb:150:in `process'
from ./script/../config/../vendor/rails/railties/lib/initializer.rb:105:in `send'
from ./script/../config/../vendor/rails/railties/lib/initializer.rb:105:in `run'
from /var/www/apps/domain/releases/20080730050116/config/environment.rb:13
from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require'
from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
from /var/www/apps/domain/releases/20080730050116/vendor/rails/activesupport/lib/active_support/dependencies.rb:144:in `require'
from /var/www/apps/domain/releases/20080730050116/vendor/rails/activesupport/lib/active_support/dependencies.rb:503:in `new_constants_in'
from /var/www/apps/domain/releases/20080730050116/vendor/rails/activesupport/lib/active_support/dependencies.rb:144:in `require'
from /usr/lib/ruby/gems/1.8/gems/mongrel-1.1.5/bin/../lib/mongrel/rails.rb:147:in `rails'
from /usr/lib/ruby/gems/1.8/gems/mongrel-1.1.5/bin/mongrel_rails:113:in `cloaker_'
from /usr/lib/ruby/gems/1.8/gems/mongrel-1.1.5/lib/mongrel/configurator.rb:149:in `call'
from /usr/lib/ruby/gems/1.8/gems/mongrel-1.1.5/lib/mongrel/configurator.rb:149:in `listener'
from /usr/lib/ruby/gems/1.8/gems/mongrel-1.1.5/bin/mongrel_rails:99:in `cloaker_'
from /usr/lib/ruby/gems/1.8/gems/mongrel-1.1.5/lib/mongrel/configurator.rb:50:in `call'
from /usr/lib/ruby/gems/1.8/gems/mongrel-1.1.5/lib/mongrel/configurator.rb:50:in `initialize'
from /usr/lib/ruby/gems/1.8/gems/mongrel-1.1.5/bin/mongrel_rails:84:in `new'
from /usr/lib/ruby/gems/1.8/gems/mongrel-1.1.5/bin/mongrel_rails:84:in `run'
from /usr/lib/ruby/gems/1.8/gems/mongrel-1.1.5/lib/mongrel/command.rb:212:in `run'
from /usr/lib/ruby/gems/1.8/gems/mongrel-1.1.5/bin/mongrel_rails:281
from /var/www/apps/domain/releases/20080730050116/vendor/rails/activesupport/lib/active_support/dependencies.rb:137:in `load_without_new_constant_marking'
from /var/www/apps/domain/releases/20080730050116/vendor/rails/activesupport/lib/active_support/dependencies.rb:137:in `load'
from /var/www/apps/domain/releases/20080730050116/vendor/rails/activesupport/lib/active_support/dependencies.rb:503:in `new_constants_in'
from /var/www/apps/domain/releases/20080730050116/vendor/rails/activesupport/lib/active_support/dependencies.rb:137:in `load'
from /var/www/apps/domain/releases/20080730050116/vendor/rails/railties/lib/commands/servers/mongrel.rb:64
from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require'
from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
from /var/www/apps/domain/releases/20080730050116/vendor/rails/activesupport/lib/active_support/dependencies.rb:144:in `require'
from /var/www/apps/domain/releases/20080730050116/vendor/rails/activesupport/lib/active_support/dependencies.rb:503:in `new_constants_in'
from /var/www/apps/domain/releases/20080730050116/vendor/rails/activesupport/lib/active_support/dependencies.rb:144:in `require'
from /var/www/apps/domain/releases/20080730050116/vendor/rails/railties/lib/commands/server.rb:49
from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require'
from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
from script/server:3
Categories: Rails
June 21st, 2008
I just ran into this issue when starting a new rails project, and attempting to use the script/generate resource generator. You might get an error like this:
/Library/Ruby/Gems/1.8/gems/haml-2.0.0/lib/haml/helpers/action_view_mods.rb:46:in `alias_method': undefined method `capture_erb_with_buffer' for module `ActionView::Helpers::CaptureHelper' (NameError)
If you do, the solution is to open up that file (action_view_mods.rb) in a text editor and find the lines:
alias_method :capture_erb_with_buffer_without_haml, :capture_erb_with_buffer
alias_method :capture_erb_with_buffer, :capture_erb_with_buffer_with_haml
And change them to:
alias_method :capture_erb_with_buffer, :capture_erb_with_buffer_with_haml
alias_method :capture_erb_with_buffer_without_haml, :capture_erb_with_buffer
For some reason it was referencing an aliased method before it aliased it.
Categories: Rails