3 ##############################################################################################
4 # uagen (http://www.fabiankeil.de/sourcecode/uagen/)
6 # $Id: uagen.pl,v 1.12 2011/06/29 18:35:23 fabiankeil Exp $
8 # Generates a pseudo-random Firefox user agent and writes it into a Privoxy action file
9 # and optionally into a Mozilla prefs file. For documentation see 'perldoc uagen(.pl)'.
11 # Examples (created with v1.0):
13 # Mozilla/5.0 (X11; U; NetBSD i386; en-US; rv:1.8.0.2) Gecko/20060421 Firefox/1.5.0.2
14 # Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-CA; rv:1.8.0.2) Gecko/20060425 Firefox/1.5.0.2
15 # Mozilla/5.0 (X11; U; SunOS i86pc; no-NO; rv:1.8.0.2) Gecko/20060420 Firefox/1.5.0.2
16 # Mozilla/5.0 (X11; U; Linux x86_64; de-AT; rv:1.8.0.2) Gecko/20060422 Firefox/1.5.0.2
17 # Mozilla/5.0 (X11; U; NetBSD i386; en-US; rv:1.8.0.2) Gecko/20060415 Firefox/1.5.0.2
18 # Mozilla/5.0 (X11; U; OpenBSD sparc64; pl-PL; rv:1.8.0.2) Gecko/20060429 Firefox/1.5.0.2
19 # Mozilla/5.0 (X11; U; Linux i686; en-CA; rv:1.8.0.2) Gecko/20060413 Firefox/1.5.0.2
21 # Copyright (c) 2006-2009 Fabian Keil <fk@fabiankeil.de>
23 # Permission to use, copy, modify, and distribute this software for any
24 # purpose with or without fee is hereby granted, provided that the above
25 # copyright notice and this permission notice appear in all copies.
27 # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
28 # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
29 # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
30 # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
31 # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
32 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
33 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
34 ##############################################################################################
43 UAGEN_VERSION => 'uagen 1.0.10',
45 UAGEN_LOGFILE => '/var/log/uagen.log',
46 ACTION_FILE => '/etc/privoxy/user-agent.action',
47 MOZILLA_PREFS_FILE => '',
54 # As of Firefox 4, the "Gecko token" has been frozen
55 # http://hacks.mozilla.org/2010/09/final-user-agent-string-for-firefox-4/
56 RANDOMIZE_RELEASE_DATE => 0,
58 # These variables belong together. If you only change one of them, the generated
59 # User-Agent might be invalid. If you're not sure which values make sense,
60 # are too lazy to check, but want to change them anyway, take the values you
61 # see in the "Help/About Mozilla Firefox" menu.
63 BROWSER_VERSION => "5.0",
64 BROWSER_REVISION => '5.0',
65 BROWSER_RELEASE_DATE => '20100101',
68 use constant LANGUAGES => qw(
69 en-AU en-GB en-CA en-NZ en-US en-ZW es-ES de-DE de-AT de-CH fr-FR sk-SK nl-NL no-NO pl-PL
72 #######################################################################################
74 sub generate_creation_time($) {
75 my $release_date = $_ = shift;
77 my ($rel_year, $rel_mon, $rel_day);
78 my ($c_day, $c_mon, $c_year);
80 my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) =
86 log_error("Invalid release date format: $release_date. Using "
87 . BROWSER_RELEASE_DATE . " instead.");
88 $release_date = BROWSER_RELEASE_DATE;
90 $rel_year = substr $release_date, 0, 4;
91 $rel_mon = substr $release_date, 4, 2;
92 $rel_day = substr $release_date, 6, 2;
95 die "release year in the future" if ( $year < $rel_year );
96 die "release month in the future"
97 if ( ( $year == $rel_year ) and ( $mon < $rel_mon ) );
98 die "release day in the future"
99 if ( ( $year == $rel_year )
100 and ( $mon == $rel_mon )
101 and ( $mday < $rel_day ) );
103 my @c_time = (0, 0, 0, $rel_day, $rel_mon - 1, $rel_year - 1900, 0, 0, 0);
104 my $c_seconds = &timelocal( @c_time );
106 $c_seconds = $now - (int rand ($now - $c_seconds));
107 @c_time = localtime $c_seconds;
108 ($sec, $min, $hour, $c_day, $c_mon, $c_year, $wday, $yday, $isdst) = @c_time;
113 die "Compilation year in the future" if ( $year < $c_year );
114 die "Compilation month in the future"
115 if ( ( $year == $c_year ) and ( $mon < $c_mon ) );
116 die "Compilation day in the future"
117 if ( ( $year == $c_year ) and ( $mon == $c_mon ) and ( $mday < $c_day ) );
119 return sprintf "%.2i%.2i%.2i", $c_year, $c_mon, $c_day;
122 sub generate_language_settings() {
126 my $language_i = int rand (@languages);
127 my $accept_language = $languages[$language_i];
128 $accept_language =~ tr/[A-Z]/[a-z]/;
130 return ($languages[$language_i], $accept_language);
133 sub generate_platform_and_os() {
139 architectures => [ 'i386', 'amd64', 'sparc64', 'alpha' ],
140 order_is_inversed => 0,
145 architectures => [ 'i386', 'amd64', 'sparc64', 'alpha' ],
146 order_is_inversed => 0,
151 architectures => [ 'i386', 'amd64', 'sparc64', 'alpha' ],
152 order_is_inversed => 0,
157 architectures => [ 'i586', 'i686', 'x86_64' ],
158 order_is_inversed => 0,
163 architectures => [ 'i86pc', 'sun4u' ],
164 order_is_inversed => 0,
168 platform => 'Macintosh',
169 architectures => [ 'PPC', 'Intel' ],
170 order_is_inversed => 1,
174 platform => 'Windows',
175 architectures => [ 'NT 5.1' ],
176 order_is_inversed => 0,
182 foreach my $os_name ( keys %os_data ) {
183 push @os_names, ($os_name) x $os_data{$os_name}{'karma'}
184 if $os_data{$os_name}{'karma'};
187 my $os_i = int rand(@os_names);
188 my $os = $os_names[$os_i];
189 my $arch_i = int rand( @{ $os_data{$os}{'architectures'} } );
190 my $arch = $os_data{$os}{'architectures'}[$arch_i];
192 my $platform = $os_data{$os}{'platform'};
195 $os_or_cpu = sprintf "%s %s",
196 $os_data{$os}{'order_is_inversed'} ? ( $arch, $os ) : ( $os, $arch );
198 return $platform, $os_or_cpu;
201 sub generate_firefox_user_agent() {
204 our $browser_version;
205 our $browser_revision;
206 our $browser_release_date;
207 our $randomize_release_date;
209 my $mozillaversion = '5.0';
212 my $creation_time = $randomize_release_date ?
213 generate_creation_time($browser_release_date) : $browser_release_date;
214 my ( $locale, $accept_language ) = generate_language_settings();
215 my ( $platform, $os_or_cpu ) = generate_platform_and_os;
217 my $firefox_user_agent =
218 sprintf "Mozilla/%s (%s; %s; %s; %s; rv:%s) Gecko/%s Firefox/%s",
219 $mozillaversion, $platform, $security, $os_or_cpu, $locale, $browser_revision,
220 $creation_time, $browser_version;
222 return $accept_language, $firefox_user_agent;
232 my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) =
236 my $logtime = sprintf "%i/%.2i/%.2i %.2i:%.2i", $year, $mon, $mday, $hour,
239 return if $no_logging;
241 open(my $log_fd, ">>" . $logfile) || die "Writing " . $logfile . " failed";
242 printf $log_fd UAGEN_VERSION . " ($logtime) $message\n";
250 $message = "Error: $message";
251 log_to_file($message);
257 sub write_action_file() {
261 our $accept_language;
262 our $no_hide_accept_language;
263 our $action_injection;
265 my $action_file_content = '';
267 if ($action_injection){
268 open(my $actionfile_fd, "<", $action_file)
269 or log_error "Reading action file $action_file failed!";
270 while (<$actionfile_fd>) {
271 s@(hide-accept-language\{).*?(\})@$1$accept_language$2@;
272 s@(hide-user-agent\{).*?(\})@$1$user_agent$2@;
273 $action_file_content .= $_;
275 close($actionfile_fd);
277 $action_file_content = "{";
278 $action_file_content .= sprintf "+hide-accept-language{%s} \\\n",
279 $accept_language unless $no_hide_accept_language;
280 $action_file_content .= sprintf " +hide-user-agent{%s} \\\n}\n/\n",
283 open(my $actionfile_fd, ">" . $action_file)
284 or log_error "Writing action file $action_file failed!";
285 print $actionfile_fd $action_file_content;
286 close($actionfile_fd);
291 sub write_prefs_file() {
293 our $mozilla_prefs_file;
295 our $accept_language;
298 my $prefs_file_content = '';
301 if (open($prefsfile_fd, $mozilla_prefs_file)) {
303 while (<$prefsfile_fd>) {
304 s@user_pref\(\"general.useragent.override\",.*\);\n?@@;
305 s@user_pref\(\"intl.accept_languages\",.*\);\n?@@;
306 $prefs_file_content .= $_;
308 close($prefsfile_fd);
310 log_error "Reading prefs file $mozilla_prefs_file failed. Creating a new file!";
313 $prefs_file_content .=
314 sprintf("user_pref(\"general.useragent.override\", \"%s\");\n", $user_agent) .
315 sprintf("user_pref(\"intl.accept_languages\", \"%s\");\n", $accept_language)
318 open($prefsfile_fd, ">" . $mozilla_prefs_file)
319 or log_error "Writing prefs file $mozilla_prefs_file failed!";
320 print $prefsfile_fd $prefs_file_content;
321 close($prefsfile_fd);
325 sub VersionMessage() {
326 printf UAGEN_VERSION . "\n" . 'Copyright (C) 2006-2009 Fabian Keil <fk@fabiankeil.de> ' .
327 "\nhttp://www.fabiankeil.de/sourcecode/uagen/\n";
334 our $browser_version;
335 our $browser_revision;
336 our $browser_release_date;
339 our $mozilla_prefs_file;
341 my $comma_separated_languages;
343 $loop = $loop ? ' ' . $loop : '';
344 $mozilla_prefs_file = $mozilla_prefs_file ? ' ' . $mozilla_prefs_file : '';
346 $comma_separated_languages .= $_ . ",";
348 chop $comma_separated_languages;
354 Options and their default values if there are any:
355 [--action-file $action_file]
357 [--browser-release-date $browser_release_date]
358 [--browser-revision $browser_revision]
359 [--browser-version $browser_version]
362 [--language-overwrite $comma_separated_languages]
366 [--no-hide-accept-language]
368 [--prefs-file$mozilla_prefs_file]
369 [--randomize-release-date]
372 [--sleeping-time $sleeping_time]
374 see "perldoc $0" for more information
383 my $no_action_file = NO_ACTION_FILE;
385 our $silent = SILENT;
386 our $no_logging = NO_LOGGING;
387 our $logfile = UAGEN_LOGFILE;
388 our $action_file = ACTION_FILE;
389 our $randomize_release_date = RANDOMIZE_RELEASE_DATE;
390 our $browser_version = BROWSER_VERSION;
391 our $browser_revision = BROWSER_REVISION;
392 our $browser_release_date = BROWSER_RELEASE_DATE;
393 our $sleeping_time = SLEEPING_TIME;
395 our $no_hide_accept_language = 0;
396 our $action_injection = 0;
399 our ( $accept_language, $user_agent );
400 our $mozilla_prefs_file = MOZILLA_PREFS_FILE;
401 our $clean_prefs = 0;
403 GetOptions('logfile=s' => \$logfile,
404 'action-file=s' => \$action_file,
405 'language-overwrite=s@' => \@languages,
406 'silent|quiet' => \$silent,
407 'no-hide-accept-language' => \$no_hide_accept_language,
408 'no-logfile' => \$no_logging,
409 'no-action-file' => \$no_action_file,
410 'randomize-release-date' => \$randomize_release_date,
411 'browser-version=s' => \$browser_version,
412 'browser-revision=s' => \$browser_revision,
413 'browser-release-date=s' => \$browser_release_date,
414 'action-injection' => \$action_injection,
416 'sleeping-time' => \$sleeping_time,
417 'prefs-file=s' => \$mozilla_prefs_file,
418 'clean-prefs-file' => \$clean_prefs,
420 'version' => sub {VersionMessage() && exit(0)}
424 @languages = split(/,/,join(',',@languages));
426 @languages = LANGUAGES;
429 srand( time ^ ( $$ + ( $$ << 15 ) ) );
433 ( $accept_language, $user_agent ) = generate_firefox_user_agent();
435 print "$user_agent\n" unless $silent;
437 write_action_file() unless $no_action_file;
438 write_prefs_file() if $mozilla_prefs_file;
440 log_to_file "Generated User-Agent: $user_agent";
442 } while ($loop && sleep($sleeping_time * 60));
450 B<uagen> - A Firefox User-Agent generator for Privoxy and Mozilla browsers
454 B<uagen> [B<--action-file> I<action_file>] [B<--action-injection>]
455 [B<--browser-release-date> I<browser_release_date>]
456 [B<--browser-revision> I<browser_revision>]
457 [B<--browser-version> I<browser_version>]
458 [B<--clean-prefs-file>]
459 [B<--help>] [B<--language-overwrite> I<language(s)>]
460 [B<--logfile> I<logfile>] [B<--loop>] [B<--no-action-file>] [B<--no-logfile>]
461 [B<--prefs-file> I<prefs_file>] [B<--randomize-release-date>]
462 [B<--quiet>] [B<--sleeping-time> I<minutes>] [B<--silent>] [B<--version>]
466 B<uagen> generates a fake Firefox User-Agent and writes it into a Privoxy action file
467 as parameter for Privoxy's B<hide-user-agent> action. Operating system, architecture,
468 platform, language and, optionally, the build date are randomized.
470 The generated language is also used as parameter for the
471 B<hide-accept-language> action which is understood by Privoxy since
474 Additionally the User-Agent can be written into prefs.js files which are
475 used by many Mozilla browsers.
479 B<--action-file> I<action_file> Privoxy action file to write the
480 generated actions into. Default is /etc/privoxy/user-agent.action.
482 B<--action-injection> Don't generate a new action file from scratch,
483 but read an old one and just replace the action values. Useful
484 to keep custom URL patterns. For this to work, the action file
485 has to be already present. B<uagen> neither checks the syntax
486 nor cares if all actions are present. Garbage in, garbage out.
488 B<--browser-release-date> I<browser_release_date> Date to use.
489 The format is YYYYMMDD. Some sanity checks are done, but you
490 shouldn't rely on them.
492 B<--browser-revision> I<browser_revision> Use a custom revision.
493 B<uagen> will use it without any sanity checks.
495 B<--browser-version> I<browser_version> Use a custom browser version.
496 B<uagen> will use it without any sanity checks.
498 B<--clean-prefs-file> The I<prefs_file> is read and the variables
499 B<general.useragent.override> and B<intl.accept_languages> are removed.
500 Only effective if I<prefs_file> is set, and only useful if you want
501 to use the browser's defaults again.
503 B<--help> List command line options and exit.
505 B<--language-overwrite> I<language(s)> Comma separated list of language codes
506 to overwrite the default values. B<uagen> chooses one of them for the generated
507 User-Agent, by default the chosen language in lower cases is also used as
508 B<hide-accept-language> parameter.
510 B<--logfile> I<logfile> Logfile to save error messages and the generated
511 User-Agents. Default is /var/log/uagen.log.
513 B<--loop> Don't exit after the generation of the action file. Sleep for
514 a while and generate a new one instead. Useful if you don't have cron(8).
516 B<--no-logfile> Don't log anything.
518 B<--no-action-file> Don't write the action file.
520 B<--no-hide-accept-language> Stay compatible with Privoxy 3.0.3
521 and don't generate the B<hide-accept-language> action line. You should
522 really update your Privoxy version instead.
524 B<--prefs-file> I<prefs_file> Use the generated User-Agent to set the
525 B<general.useragent.override> variable in the Mozilla preference file
526 I<prefs_file>, The B<intl.accept_languages> variable will be set as well.
528 Firefox's preference file is usually located in
529 ~/.mozilla/firefox/*.default/prefs.js. Note that Firefox doesn't reread
530 the file once it is running.
532 B<--randomize-release-date> Randomly pick a date between the configured
533 release date and the actual date. Note that Firefox versions after 4.0
534 no longer provide the build date in the User-Agent header, so if you
535 randomize the date anyway, it will be obvious that the generated User-Agent
538 B<--quiet> Don't print the generated User-Agent to the console.
540 B<--sleeping-time> I<minutes> Time to sleep. Only effective if used with B<--loop>.
542 B<--silent> Don't print the generated User-Agent to the console.
544 B<--version> Print version and exit.
546 The second dash is optional, options can be shortened, as long as there are
549 =head1 PRIVOXY CONFIGURATION
551 In Privoxy's configuration file the line:
553 actionsfile user-agent.action
555 should be added after:
557 actionfile default.action
561 actionfile user.action
563 This way the user can still use custom User-Agents
564 in I<user.action>. I<user-agent> has to be the name
565 of the generated action file.
567 If you are using Privoxy 3.0.6 or earlier, don't add the ".action" extension.
571 Without any options, B<uagen> creates an action file like:
573 {+hide-accept-language{en-ca} \
574 +hide-user-agent{Mozilla/5.0 (X11; U; OpenBSD i386; en-CA; rv:1.8.0.4) Gecko/20060628 Firefox/1.5.0.4} \
578 with the --no-accept-language option the generated file
579 could look like this one:
581 {+hide-user-agent{Mozilla/5.0 (X11; U; FreeBSD i386; de-DE; rv:1.8.0.4) Gecko/20060720 Firefox/1.5.0.4} \
587 If the browser opens an encrypted connection, Privoxy can't inspect
588 the content and the browser's headers reach the server unmodified.
589 It is the user's job to use Privoxy's limit-connect action to make sure
590 there are no encrypted connections to untrusted sites.
592 Mozilla users can alter the browser's User-Agent with the
593 B<--prefs-file> option. But note that the preference file is only read
594 on startup. If the browser is already running, B<uagen's> changes will be ignored.
596 Hiding the User-Agent is pointless if the browser accepts all
597 cookies or even is configured for remote maintenance through Flash,
598 JavaScript, Java or similar security problems.
602 Some parameters can't be specified at the command line.
610 Fabian Keil <fk@fabiankeil.de>
612 http://www.fabiankeil.de/sourcecode/uagen/
614 http://www.fabiankeil.de/blog-surrogat/2006/01/26/firefox-user-agent-generator.html (German)