74
84
return "Failed to compare versions A = %s, B = %s (%s)" % (self.a, self.b, str(self.e))
87
def _open(filename, mode):
88
if sys.version_info[0] < 3:
89
return codecs.open(filename, mode, 'utf-8')
90
return open(filename, mode)
77
92
def mkstemp_copy(path):
78
93
'''Make a copy of a file to a temporary file, and return the path'''
79
94
(outfd, outpath) = tempfile.mkstemp()
80
outfile = os.fdopen(outfd, 'w')
81
infile = open(path, 'r')
95
outfile = os.fdopen(outfd, 'wb')
96
infile = open(path, 'rb')
467
492
self._profile_path = profile_path
469
# Report non-default preferences = ie, those from syspref.js, prefs.js,
470
# user.js and extension prefs. The load order is important
494
# Read all preferences. Note that we hide preferences that are considered
495
# default (ie, all of those set by the Firefox package or bundled addons,
496
# unless any of the pref files have been modified by the user).
497
# The load order is *very important*
471
498
if profile_path != None:
473
%%ifdef MOZ_NEW_SYSPREF
474
"/etc/@MOZ_APP_NAME@/syspref.js",
476
"/etc/@MOZ_APP_NAME@/pref",
500
"/@MOZ_LIBDIR@/omni.ja:greprefs.js",
501
"/@MOZ_LIBDIR@/omni.ja:defaults/pref/*.js",
502
"/@MOZ_LIBDIR@/defaults/pref/*.js",
503
"/@MOZ_LIBDIR@/defaults/pref/unix.js",
504
"/@MOZ_LIBDIR@/omni.ja:defaults/preferences/*.js"
505
"/@MOZ_LIBDIR@/defaults/preferences/*.js"
508
append_dirs = [ 'defaults/preferences/*.js' ]
509
%%ifndef MOZ_NEW_SYSPREF
510
locations.append('/@MOZ_LIBDIR@/defaults/syspref/*.js')
511
append_dirs.append('defaults/syspref/*.js')
478
os.path.join(profile_path, "prefs.js"),
479
os.path.join(profile_path, "user.js")
513
if os.path.isdir('/@MOZ_LIBDIR@/distribution/bundles'):
514
bundles = os.listdir('/@MOZ_LIBDIR@/distribution/bundles')
515
bundles.sort(reverse=True)
516
for d in append_dirs:
517
for bundle in bundles:
518
path = os.path.join('/@MOZ_LIBDIR@/distribution/bundles', bundle)
519
if path.endswith('.xpi'):
520
locations.append(path + ':' + d)
521
elif os.path.isdir(path):
522
locations.append(os.path.join(path, d))
524
locations.append(os.path.join(profile_path, "prefs.js"))
525
locations.append(os.path.join(profile_path, "user.js"))
482
527
extensions = ExtensionINIParser(profile_path)
483
528
for extension in extensions:
484
if not extension.endswith('.xpi'):
485
extension = os.path.join(extension, "defaults", "preferences")
486
locations.append(extension)
529
if extension.endswith('.xpi'):
530
locations.append(extension + ':defaults/preferences/*.(J|j)(S|s)')
531
elif os.path.isdir(extension):
532
locations.append(os.path.join(extension, 'defaults/preferences/*.js'))
534
locations.append(os.path.join(profile_path, 'preferences/*.js'))
487
535
else: locations = []
489
537
if extra_paths != None:
491
539
locations.append(extra)
493
541
for location in locations:
494
if location.endswith('.js'):
495
self._parse_file(location)
496
elif location.endswith('.xpi'):
497
self._parse_xpi(location)
542
m = re.match(r'^([^:]*):?(.*)', location)
544
files = glob(location)
545
files.sort(reverse=True)
499
self._parse_dir(location)
549
self._parse_jar(m.group(1), m.group(2))
551
def _should_ignore_file(self, filename):
552
realpath = os.path.realpath(filename)
553
package = apport.packaging.get_file_package(realpath)
554
if package and apport.packaging.is_distro_package(package) and \
555
package in DISTRO_ADDONS and \
556
realpath[1:] not in apport.packaging.get_modified_files(package):
501
561
def _parse_file(self, filename):
502
anonsrc = anonymize_path(filename, self._profile_path)
504
563
self._state = Prefs.STATE_READY
565
f = _open(filename, 'r')
510
569
for line in f.readlines():
511
state = self._parseline(line, anonsrc, linenum, state)
570
state = self._parseline(line, filename, linenum, state)
513
572
except Exception as e:
514
self.errors[anonsrc] = str(e)
573
self.errors[filename] = str(e)
520
if anonsrc not in self.errors:
521
self.pref_sources.append(anonsrc)
523
def _parse_dir(self, dirname):
524
for filename in glob(os.path.join(dirname, '*.js')):
525
self._parse_file(filename)
527
def _parse_xpi(self, xpi):
579
if filename not in self.errors \
580
and not self._should_ignore_file(filename):
581
self.pref_sources.append(filename)
583
def _parse_jar(self, jar, match):
530
xpifile = zipfile.ZipFile(xpi)
531
entries = xpifile.namelist()
586
jarfile = zipfile.ZipFile(jar)
587
entries = jarfile.namelist()
588
entries.sort(reverse=True)
532
589
for entry in entries:
533
if re.match(r'^defaults/preferences/*.js$', entry):
534
source = '%s:%s' % (xpi, entry)
535
anonsrc = anonymize_path(source, self._profile_path)
590
if re.match(r'^' + match + '$', entry):
591
source = '%s:%s' % (jar, entry)
537
f = xpifile.open(entry, 'r')
593
f = jarfile.open(entry, 'r')
540
596
for line in f.readlines():
541
state = self._parseline(line, anonsrc, linenum, state)
597
state = self._parseline(line.decode('utf-8'),
598
source, linenum, state)
543
600
except Exception as e:
544
self.errors[anonsrc] = str(e)
601
self.errors[source] = str(e)
546
if anonsrc not in self.errors:
547
self.pref_sources.append(anonsrc)
603
if source not in self.errors \
604
and not self._should_ignore_file(jar):
605
self.pref_sources.append(source)
554
612
def _maybe_add_pref(self, key, value, source, default, locked):
556
614
class Pref(object):
615
def __init__(self, profile_path):
558
616
self._default = None
559
617
self._value = None
560
618
self._default_source = None
561
619
self._value_source = None
562
620
self.locked = False
621
self._profile_path = profile_path
634
699
state.state = Prefs.STATE_COMMENT_MAYBE_START
636
701
state.state = Prefs.STATE_PARSE_UNTIL_EOL
637
elif line[index:].startswith('pref'):
702
elif line.startswith('pref', index):
638
703
state.default == True
639
704
state.next_state = Prefs.STATE_PARSE_UNTIL_OPEN_PAREN
640
705
state.state = Prefs.STATE_SKIP
642
elif line[index:].startswith('user_pref'):
707
elif line.startswith('user_pref', index):
643
708
state.next_state = Prefs.STATE_PARSE_UNTIL_OPEN_PAREN
644
709
state.state = Prefs.STATE_SKIP
646
elif line[index:].startswith('lockPref'):
711
elif line.startswith('lockPref', index):
647
712
state.default = True
648
713
state.locked = True
649
714
state.next_state = Prefs.STATE_PARSE_UNTIL_OPEN_PAREN
650
715
state.state = Prefs.STATE_SKIP
717
elif not c.isspace():
718
raise PrefParseError("Unexpected character '%s' before pref" % c,
719
anonymize_path(source, self._profile_path),
653
722
elif state.state == Prefs.STATE_SKIP:
726
801
state.next_state = state.state
727
802
state.state = Prefs.STATE_COMMENT_MAYBE_START
728
803
elif not c.isspace():
729
raise PrefParseError("Unexpected character before comma", source, linenum)
804
raise PrefParseError("Unexpected character '%s' before comma" % c,
805
anonymize_path(source, self._profile_path),
731
808
elif state.state == Prefs.STATE_PARSE_UNTIL_VALUE:
732
809
if c == '"' or c == '\'':
734
811
state.state = Prefs.STATE_PARSE_STRING
735
812
state.next_state = Prefs.STATE_PARSE_UNTIL_CLOSE_PAREN
736
elif line[index:].startswith('true'):
813
elif line.startswith('true', index):
738
815
state.next_state = Prefs.STATE_PARSE_UNTIL_CLOSE_PAREN
739
816
state.state = Prefs.STATE_SKIP
741
elif line[index:].startswith('false'):
818
elif line.startswith('false', index):
742
819
state.tmp = False
743
820
state.next_state = Prefs.STATE_PARSE_UNTIL_CLOSE_PAREN
744
821
state.state = Prefs.STATE_SKIP
978
1077
if not hasattr(self, '_running'):
979
1078
# We detect if this profile is running or not by trying to lock the lockfile
980
1079
# If we can't lock it, then Thunderbird is running
981
fd = os.open(os.path.join(self.path, ".parentlock"), os.O_WRONLY|os.O_CREAT|os.O_TRUNC, 0666)
1080
fd = os.open(os.path.join(self.path, ".parentlock"), os.O_WRONLY|os.O_CREAT|os.O_TRUNC, 0o666)
982
1081
lock = struct.pack("hhqqi", 1, 0, 0, 0, 0)
984
1083
fcntl.fcntl(fd, fcntl.F_SETLK, lock)
1006
1105
if type(self.prefs[pref].value) == int:
1007
1106
value = str(self.prefs[pref].value)
1008
1107
elif type(self.prefs[pref].value) == bool:
1009
value = 'true' if True else 'false'
1108
value = 'true' if self.prefs[pref].value == True else 'false'
1011
1110
value = "\"%s\"" % self.prefs[pref].value
1012
ret += pref + ': ' + value + ' (' + self.prefs[pref].source + ')\n'
1111
ret += pref + ': ' + value + ' (' + self.prefs[pref].anon_source + ')\n'
1015
1114
def dump_pref_sources(self):
1017
1116
for source in self.prefs.pref_sources:
1018
ret += source + '\n'
1117
ret += anonymize_path(source, self.path) + '\n'
1021
1120
def dump_pref_errors(self):
1236
1338
report['Tags'] = tags + tag
1238
ddproc = Popen(['dpkg-divert', '--truename', '/usr/bin/@MOZ_APP_NAME@'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
1340
ddproc = Popen(['dpkg-divert', '--truename', '/usr/bin/@MOZ_APP_NAME@'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True)
1239
1341
truename = ddproc.communicate()
1240
1342
if ddproc.returncode == 0 and truename[0].strip() != '/usr/bin/@MOZ_APP_NAME@':
1241
ddproc = Popen(['dpkg-divert', '--listpackage', '/usr/bin/@MOZ_APP_NAME@'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
1343
ddproc = Popen(['dpkg-divert', '--listpackage', '/usr/bin/@MOZ_APP_NAME@'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True)
1242
1344
diverter = ddproc.communicate()
1243
1345
report['UnreportableReason'] = "/usr/bin/@MOZ_APP_NAME@ has been diverted by a third party package (%s)" % diverter[0].strip()