debian/0000775000000000000000000000000012311704716007172 5ustar debian/thunderbird.manpages.in0000664000000000000000000000003012311704701013611 0ustar debian/@MOZ_APP_NAME@.1 debian/README.source0000664000000000000000000000076312311704701011351 0ustar 1) Even though we are using source format 3.0 (quilt), we don't build or extract the source packages with patches applied. This is because we preprocess the series file to allow for per-arch and per-release patchsets, from a single packaging branch 2) To create the fully patched source, just run "debian/rules apply-patches" in the source directory 3) To remove any changes applied to the packaging, run "fakeroot debian/rules clean". This will remove all patches and delete auto-generated files debian/rules0000775000000000000000000001443312311704701010251 0ustar #!/usr/bin/make -f MOZ_PKG_BASENAME := thunderbird include $(CURDIR)/debian/build/config.mk MOZ_APP := mail MOZ_VENDOR := MOZ_MOZDIR := mozilla MOZ_BRANDING := $(CHANNEL) ifneq (,$(filter release beta, $(MOZ_BRANDING))) MOZ_BRANDING := official endif ifeq (1,$(MOZ_FORCE_UNOFFICIAL_BRANDING)) ifneq (,$(filter official aurora, $(MOZ_BRANDING))) MOZ_BRANDING := nightly endif endif ifeq (official,$(MOZ_BRANDING)) MOZ_BRANDING_DIR := other-licenses/branding/thunderbird MOZ_BRANDING_OPTION := --enable-official-branding else MOZ_BRANDING_DIR := $(MOZ_APP)/branding/$(MOZ_BRANDING) MOZ_BRANDING_OPTION := --with-branding=$(MOZ_BRANDING_DIR) endif MOZ_SEARCHPLUGIN_DIR = $(MOZ_LIBDIR)/distribution/searchplugins ifneq (,$(filter precise, $(DISTRIB_CODENAME))) UNITY_DEPENDS := libindicate5, libdbusmenu-gtk4, libunity9, libebook-1.2-12, libedataserver-1.2-15 else ifneq (,$(filter quantal raring saucy, $(DISTRIB_CODENAME))) UNITY_DEPENDS := libmessaging-menu0, libunity9, libebook-1.2-14, libedataserver-1.2-17 else UNITY_DEPENDS := libmessaging-menu0, libunity9 endif endif # Make sure thunderbird-couchdb is removed on upgrade to 12.10 # See https://launchpad.net/bugs/1040839 ifeq (,$(filter precise, $(DISTRIB_CODENAME))) TB_COUCHDB_BREAKS := thunderbird-couchdb endif MOZ_LIGHTNING_PKG_NAME = $(addsuffix $(subst $(MOZ_PKG_BASENAME),,$(MOZ_PKG_NAME)),xul-ext-lightning) MOZ_CALENDAR_TZ_PKG_NAME = $(addsuffix $(subst $(MOZ_PKG_BASENAME),,$(MOZ_PKG_NAME)),xul-ext-calendar-timezones) MOZ_GDATA_PROVIDER_PKG_NAME = $(addsuffix $(subst $(MOZ_PKG_BASENAME),,$(MOZ_PKG_NAME)),xul-ext-gdata-provider) MOZ_PKG_SUPPORT_SUGGESTS = $(MOZ_PKG_NAME)-gnome-support MOZ_PKGNAME_SUBST_FILES = \ debian/$(MOZ_PKG_BASENAME).sh \ debian/apport/blacklist \ debian/apport/native-origins \ debian/apport/source_$(MOZ_PKG_NAME).py \ $(NULL) include $(CURDIR)/debian/build/rules.mk MOZ_EXECUTABLES_$(MOZ_PKG_NAME)-testsuite = $(MOZ_LIBDIR)/testing/run_xpcshell_tests \ $(MOZ_LIBDIR)/xpcshell \ $(MOZ_LIBDIR)/testing/filter_results \ $(NULL) debian/control:: sed -i -e 's/@MOZ_LIGHTNING_PKG_NAME@/$(MOZ_LIGHTNING_PKG_NAME)/g' \ -e 's/@MOZ_CALENDAR_TZ_PKG_NAME@/$(MOZ_CALENDAR_TZ_PKG_NAME)/g' \ -e 's/@MOZ_GDATA_PROVIDER_PKG_NAME@/$(MOZ_GDATA_PROVIDER_PKG_NAME)/g' debian/control make-messagingmenu-addon: debian/messagingmenu/messagingmenu@mozilla.com.xpi debian/messagingmenu/messagingmenu@mozilla.com.xpi: cd $(CURDIR)/debian/messagingmenu; \ sh build.sh || exit 1; \ mv $(CURDIR)/debian/messagingmenu/messagingmenu.xpi $(CURDIR)/debian/messagingmenu/messagingmenu@mozilla.com.xpi make-eds-addon: debian/eds/edsintegration@mozilla.com.xpi debian/eds/edsintegration@mozilla.com.xpi: cd $(CURDIR)/debian/eds; \ LIBXUL_SDK=$(CURDIR)/$(MOZ_DISTDIR) sh build.sh || exit 1; \ mv $(CURDIR)/debian/eds/edsintegration.xpi $(CURDIR)/debian/eds/edsintegration@mozilla.com.xpi common-build-arch:: make-messagingmenu-addon make-eds-addon WRITE_SUBSTVARS = $(shell echo "$(2)=$(3)" | sed 's/[ \t\n]\+/ /g' >> debian/$(1).substvars) ifeq (thunderbird,$(MOZ_PKG_NAME)) TB_REPLACES = mozilla-thunderbird, thunderbird-gnome-support (<= 3.0.4+nobinonly-0ubuntu3) TB_CONFLICTS = mozilla-thunderbird TB_BREAKS = thunderbird-gnome-support (<= 3.0.4+nobinonly-0ubuntu3), $(TB_COUCHDB_BREAKS) install/thunderbird:: $(call WRITE_SUBSTVARS,thunderbird,transitional:Replaces,$(TB_REPLACES)) $(call WRITE_SUBSTVARS,thunderbird,transitional:Conflicts,$(TB_CONFLICTS)) $(call WRITE_SUBSTVARS,thunderbird,transitional:Breaks,$(TB_BREAKS)) else TB_BREAKS = $(TB_COUCHDB_BREAKS) install/$(MOZ_PKG_NAME):: $(call WRITE_SUBSTVARS,$(MOZ_PKG_NAME),transitional:Breaks,$(TB_BREAKS)) endif ifneq (,$(UNITY_DEPENDS)) install/$(MOZ_PKG_NAME)-gnome-support:: $(call WRITE_SUBSTVARS,$(MOZ_PKG_NAME)-gnome-support,unity:Depends,$(UNITY_DEPENDS)) endif binary-install/$(MOZ_PKG_NAME):: convert -resize 32x32 debian/$(MOZ_PKG_NAME)/usr/share/pixmaps/$(MOZ_APP_NAME).png debian/$(MOZ_PKG_NAME)/usr/share/pixmaps/$(MOZ_APP_NAME).xpm echo "/usr/share/applications/$(MOZ_APP_NAME).desktop" > debian/$(MOZ_PKG_NAME)/usr/share/indicators/messages/applications/$(MOZ_APP_NAME) binary-install/$(MOZ_LIGHTNING_PKG_NAME):: dh_installdirs -p$(MOZ_LIGHTNING_PKG_NAME) $(MOZ_ADDONDIR)/extensions/{e2fda1a4-762b-4020-b5ad-a41df1933103} unzip -o -d debian/$(MOZ_LIGHTNING_PKG_NAME)/$(MOZ_ADDONDIR)/extensions/{e2fda1a4-762b-4020-b5ad-a41df1933103}/ $(MOZ_DISTDIR)/xpi-stage/lightning-*.xpi binary-install/$(MOZ_GDATA_PROVIDER_PKG_NAME):: dh_installdirs -p$(MOZ_GDATA_PROVIDER_PKG_NAME) $(MOZ_ADDONDIR)/extensions/{a62ef8ec-5fdc-40c2-873c-223b8a6925cc} unzip -o -d debian/$(MOZ_GDATA_PROVIDER_PKG_NAME)/$(MOZ_ADDONDIR)/extensions/{a62ef8ec-5fdc-40c2-873c-223b8a6925cc}/ $(MOZ_DISTDIR)/xpi-stage/gdata-provider-*.xpi pre-build:: $(shell /usr/bin/docbook-to-man $(CURDIR)/debian/thunderbird.sgml > $(CURDIR)/debian/$(MOZ_APP_NAME).1) monkey-patch-upstream-files:: ifneq ($(MOZ_APP_BASENAME),$(MOZ_DEFAULT_APP_BASENAME)) @echo "Changing Name in application.ini to $(MOZ_APP_BASENAME)" $(call moz_monkey_patch_file,$(DEB_SRCDIR)/mail/app/application.ini, \ sed -i 's/^\(Name\=\).*/\1$(MOZ_APP_BASENAME)/') @echo "Setting MOZ_APP_UA_NAME to $(MOZ_DEFAULT_APP_BASENAME)" $(call moz_monkey_patch_file,$(DEB_SRCDIR)/mail/confvars.sh, \ echo "MOZ_APP_UA_NAME=$(MOZ_DEFAULT_APP_BASENAME)" >>) @echo "Setting MOZ_APP_BASENAME to $(MOZ_APP_BASENAME)" $(call moz_monkey_patch_file,$(DEB_SRCDIR)/mail/confvars.sh, \ sed -i 's/^\(MOZ_APP_BASENAME\=\).*/\1$(MOZ_APP_BASENAME)/') endif ifneq ($(MOZ_APP_NAME),$(MOZ_DEFAULT_APP_NAME)) @echo "Setting MOZ_APP_NAME to $(MOZ_APP_NAME)" $(call moz_monkey_patch_file,$(DEB_SRCDIR)/mail/confvars.sh, \ sed -i 's/^\(MOZ_APP_NAME\=\).*/\1$(MOZ_APP_NAME)/') @echo "Replacing instances of \"%APP%\" with \"thunderbird\" in mail/app/profile/all-thunderbird.js" $(call moz_monkey_patch_file,mail/app/profile/all-thunderbird.js, \ sed -ri 's/%APP%/thunderbird/g') @echo "Replacing instances of \"%APP%\" with \"thunderbird\" in $(MOZ_BRANDING_DIR)/thunderbird-branding.js" $(call moz_monkey_patch_file,$(MOZ_BRANDING_DIR)/thunderbird-branding.js, \ sed -ri 's/%APP%/thunderbird/g') endif clean:: rm -f debian/messagingmenu/*.xpi rm -f debian/eds/*.xpi rm -f debian/eds/components/*.xpt rm -f debian/$(MOZ_APP_NAME).1 .PHONY: make-eds-addon make-messagingmenu-addon debian/thunderbird-dev.install.in0000664000000000000000000000013212311704701014243 0ustar @MOZ_INCDIR@ @MOZ_IDLDIR@ @MOZ_SDKDIR@/sdk/lib/*.a @MOZ_SDKDIR@/*.h @MOZ_SDKDIR@/sdk/bin debian/thunderbird.postrm.in0000664000000000000000000000061612311704701013354 0ustar #!/bin/sh set -e MOZ_PKG_NAME=@MOZ_PKG_NAME@ abort_mv_conffile() { local CONFFILE="$1" if [ -e "$CONFFILE.dpkg-remove" ]; then echo "Reinstalling $CONFFILE that was moved away" mv "$CONFFILE.dpkg-remove" "$CONFFILE" fi } if [ "$1" = "abort-install" -o "$1" = "abort-upgrade" ] ; then abort_mv_conffile "/etc/${MOZ_PKG_NAME}/pref/thunderbird.js" fi #DEBHELPER# debian/thunderbird-testsuite.lintian-overrides.in0000664000000000000000000000012412311704701017507 0ustar @MOZ_PKG_NAME@-testsuite binary: symlink-is-self-recursive @MOZ_LIBDIR@/testing/bin debian/copyright0000664000000000000000000007054412311704701011131 0ustar This package was debianized by Fabien Tassin on 2008-02-25 The package was downloaded from http://ftp.mozilla.org/pub/mozilla.org/thunderbird/ EDIT: downloaded from CVS (until 3.0 is released) using the following commands: debian/rules get-orig-source (you need to install mozilla-devscripts >= 0.05) The source tarball is an aggregate of several different works in which the main one, developed by the Mozilla Project, is thunderbird. The contents of the debian directory are licensed like the bulk of thunderbird, tri-licensed under the GPL/LGPL/MPL. Other works include gzip, bzip2, sqlite, libjpeg, libpng, cairo, and others. Please see the sections following the copyright statements for thunderbird for these. If this file is incomplete, which I'm pretty sure is the case, because it's difficult not to forget anything in this huge amount of data, please file a bug or contact me. === thunderbird Overall, the thunderbird project is licensed under the terms of the Mozilla Public License version 1.1 or, at your option, under the terms of the GNU General Public License version 2 or subsequent, or the terms of the GNU Lesser General Public License version 2.1 or subsequent. On Debian systems, the complete text of the GNU General Public License can be found in the file `/usr/share/common-licenses/GPL-2' ; the complete text of the GNU Lesser General Public License can be found in the file `/usr/share/common-licenses/LGPL-2.1'. The complete text of the Mozilla Public License can be found in the MPL file in the same directory as this file. Some of the files are also licensed (single, dual or tri) under the terms of the Netscape Public License (NPL) which can be found as amendments to the MPL at the end of the file. Please refer to http://www.mozilla.org/MPL/relicensing-faq.html While the Mozilla Project is undergoing a global relicensing so that the aim is to have a full tree with these tri-license terms, there are at the moment quite some exceptions as following: (Note that I sometimes assumed files not explicitely licensed to be licensed under the same terms as the files around them and that a lot of the information has been extracted automatically, thus maybe leaving some doubt. Again, if you find "bugs", please contact me by email or via the BTS.) The following files are not tri-licenced MPL/GPL/LGPL: - GPL only: dom/tests/mochitest/ajax/jquery/dist/jquery.js dom/tests/mochitest/ajax/jquery/src/jquery/jquery.js - MPL and GPL: embedding/browser/activex/src/install/README.txt - MPL and LGPL: gfx/cairo/cairo/* - GPL and LGP: extensions/pref/system-pref/src/gconf/nsSystemPrefService.cpp extensions/pref/system-pref/src/gconf/nsSystemPrefService.h extensions/pref/system-pref/src/nsSystemPref.cpp extensions/pref/system-pref/src/nsSystemPref.h extensions/pref/system-pref/src/nsSystemPrefFactory.cpp extensions/pref/system-pref/src/nsSystemPrefLog.h netwerk/base/public/nsIIOService2.idl netwerk/base/public/nsINetworkLinkService.idl netwerk/streamconv/converters/mozTXTToHTMLConv.cpp netwerk/streamconv/converters/mozTXTToHTMLConv.h netwerk/streamconv/public/mozITXTToHTMLConv.idl toolkit/system/dbus/nsDBusModule.cpp toolkit/system/dbus/nsDBusService.cpp toolkit/system/dbus/nsDBusService.h toolkit/system/dbus/nsNetworkManagerListener.cpp toolkit/system/dbus/nsNetworkManagerListener.h - NPL only (thus MPL): (EDIT: to investigate) intl/uconv/ucvibm/864i.uf intl/uconv/ucvibm/cp850.uf intl/uconv/ucvibm/cp852.uf intl/uconv/ucvibm/cp855.uf intl/uconv/ucvibm/cp857.uf intl/uconv/ucvibm/cp862.uf intl/uconv/ucvibm/cp864.uf intl/uconv/ucvmath/mathematica1.uf intl/uconv/ucvmath/mathematica2.uf intl/uconv/ucvmath/mathematica3.uf intl/uconv/ucvmath/mathematica4.uf intl/uconv/ucvmath/mathematica5.uf intl/uconv/ucvmath/mtextra.uf intl/uconv/ucvmath/texcmex-t1.uf intl/uconv/ucvmath/texcmex-ttf.uf intl/uconv/ucvmath/texcmmi-t1.uf intl/uconv/ucvmath/texcmmi-ttf.uf intl/uconv/ucvmath/texcmr-ttf.uf intl/uconv/ucvmath/texcmsy-t1.uf - Public Domain: aclocal.m4 build/autoconf/freetype2.m4 build/autoconf/gtk.m4 build/autoconf/nspr.m4 db/mork/src/morkDeque.cpp db/mork/src/morkDeque.h The file db/mork/src/morkQuickSort.cpp is licensed under the following terms: Copyright (c) 1992, 1993 The Regents of the University of California. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The following files come from X11R5, without any clear licensing terms: build/autoconf/install-sh directory/c-sdk/config/autoconf/install-sh nsprpub/build/autoconf/install-sh The embedding/qa/mozembed/public/nsIQABrowserUIGlue.idl file and the files under the embedding/tests directory are licensed under the following terms: Copyright (c) 2002 Netscape Communications Corporation and other contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this Mozilla sample software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. === sqlite The db/sqlite3 directory contains a copy of sqlite with the following licensing terms: The author disclaims copyright to this source code. In place of a legal notice, here is a blessing: May you do good and not evil. May you find forgiveness for yourself and forgive others. May you share freely, never taking more than you give. === dbm The dbm directory, except the files listed under the thunderbird section, is licensed under the following terms: Copyright (c) 1991, 1993, 1994 The Regents of the University of California. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed by the University of California, Berkeley and its contributors. 4. Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. === myspell The extensions/spellcheck/myspell directory is licensed under the following terms: Copyright 2002 Kevin B. Hendricks, Stratford, Ontario, Canada And Contributors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. All modifications to the source code must be clearly marked as such. Binary redistributions based on modified source code must be clearly marked as modified versions in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY KEVIN B. HENDRICKS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEVIN B. HENDRICKS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. === boehm The gc/boehm directory, except the files listed under the thunderbird section, is licensed under the following terms: Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers Copyright (c) 1991-1996 by Xerox Corporation. All rights reserved. Copyright (c) 1996-1998 by Silicon Graphics. All rights reserved. THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED OR IMPLIED. ANY USE IS AT YOUR OWN RISK. Permission is hereby granted to use or copy this program for any purpose, provided the above notices are retained on all copies. Permission to modify the code and to distribute modified code is granted, provided the above notices are retained, and a notice that the code was modified is included with the above copyright notice. === cairo The gfx/cairo/cairo directory is licensed under the following terms: Copyright (c) 2003 University of Southern California Copyright (c) 1999 Tom Tromey Copyright (c) 2002, 2003 University of Southern California Copyright (c) 2004 Calum Robinson Copyright (c) 2004 David Reveman Copyright (c) 2000, 2002, 2004 Keith Packard Copyright (c) 2004, 2005 Red Hat, Inc Cairo is free software and is available to be redistributed and/or modified under the terms of either the GNU Lesser General Public License (LGPL) version 2.1 or the Mozilla Public License (MPL) version 1.1. === libpixman The gfx/cairo/libpixman directory is licensed under the following terms: (from gfx/cairo/libpixman/COPYING) libpixregion Copyright 1987, 1998 The Open Group Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of The Open Group shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from The Open Group. Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. All Rights Reserved Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Digital not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ---------------------------------------------------------------------- libic Copyright © 2001 Keith Packard Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Keith Packard not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. Keith Packard makes no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ---------------------------------------------------------------------- slim slim is Copyright © 2003 Richard Henderson Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Richard Henderson not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. Richard Henderson makes no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. RICHARD HENDERSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL RICHARD HENDERSON BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. === libjpeg The jpeg directory, except the files under the thunderbird section, are licensed under the following terms: The authors make NO WARRANTY or representation, either express or implied, with respect to this software, its quality, accuracy, merchantability, or fitness for a particular purpose. This software is provided "AS IS", and you, its user, assume the entire risk as to its quality and accuracy. This software is copyright (C) 1991-1998, Thomas G. Lane. All Rights Reserved except as specified below. Permission is hereby granted to use, copy, modify, and distribute this software (or portions thereof) for any purpose, without fee, subject to these conditions: (1) If any part of the source code for this software is distributed, then this README file must be included, with this copyright and no-warranty notice unaltered; and any additions, deletions, or changes to the original files must be clearly indicated in accompanying documentation. (2) If only executable code is distributed, then the accompanying documentation must state that "this software is based in part on the work of the Independent JPEG Group". (3) Permission for use of this software is granted only if the user accepts full responsibility for any undesirable consequences; the authors accept NO LIABILITY for damages of any kind. These conditions apply to any software derived from or based on the IJG code, not just to the unmodified library. If you use our work, you ought to acknowledge us. Permission is NOT granted for the use of any IJG author's name or company name in advertising or publicity relating to this software or products derived from it. This software may be referred to only as "the Independent JPEG Group's software". We specifically permit and encourage the use of this software as the basis of commercial products, provided that all warranty or liability claims are assumed by the product vendor. === bzip2 The modules/libbz2/src directory is licensed under the following terms: (from modules/libbz2/src/LICENSE) This program, "bzip2", the associated library "libbzip2", and all documentation, are copyright (C) 1996-2005 Julian R Seward. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 3. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 4. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Julian Seward, Cambridge, UK. jseward@acm.org bzip2/libbzip2 version 1.0.3 of 15 February 2005 === libpng The directory modules/libimg/png is licensed under the following terms: (from modules/libimg/png/LICENSE) This copy of the libpng notices is provided for your convenience. In case of any discrepancy between this copy and the notices in the file png.h that is included in the libpng distribution, the latter shall prevail. COPYRIGHT NOTICE, DISCLAIMER, and LICENSE: If you modify libpng you may insert additional notices immediately following this sentence. libpng version 1.2.6, September 12, 2004, is Copyright (c) 2004 Glenn Randers-Pehrson, and is distributed according to the same disclaimer and license as libpng-1.2.5 with the following individual added to the list of Contributing Authors Cosmin Truta libpng versions 1.0.7, July 1, 2000, through 1.2.5 - October 3, 2002, are Copyright (c) 2000-2002 Glenn Randers-Pehrson, and are distributed according to the same disclaimer and license as libpng-1.0.6 with the following individuals added to the list of Contributing Authors Simon-Pierre Cadieux Eric S. Raymond Gilles Vollant and with the following additions to the disclaimer: There is no warranty against interference with your enjoyment of the library or against infringement. There is no warranty that our efforts or the library will fulfill any of your particular purposes or needs. This library is provided with all faults, and the entire risk of satisfactory quality, performance, accuracy, and effort is with the user. libpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, are Copyright (c) 1998, 1999 Glenn Randers-Pehrson, and are distributed according to the same disclaimer and license as libpng-0.96, with the following individuals added to the list of Contributing Authors: Tom Lane Glenn Randers-Pehrson Willem van Schaik libpng versions 0.89, June 1996, through 0.96, May 1997, are Copyright (c) 1996, 1997 Andreas Dilger Distributed according to the same disclaimer and license as libpng-0.88, with the following individuals added to the list of Contributing Authors: John Bowler Kevin Bracey Sam Bushell Magnus Holmgren Greg Roelofs Tom Tanner libpng versions 0.5, May 1995, through 0.88, January 1996, are Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc. For the purposes of this copyright and license, "Contributing Authors" is defined as the following set of individuals: Andreas Dilger Dave Martindale Guy Eric Schalnat Paul Schmidt Tim Wegner The PNG Reference Library is supplied "AS IS". The Contributing Authors and Group 42, Inc. disclaim all warranties, expressed or implied, including, without limitation, the warranties of merchantability and of fitness for any purpose. The Contributing Authors and Group 42, Inc. assume no liability for direct, indirect, incidental, special, exemplary, or consequential damages, which may result from the use of the PNG Reference Library, even if advised of the possibility of such damage. Permission is hereby granted to use, copy, modify, and distribute this source code, or portions hereof, for any purpose, without fee, subject to the following restrictions: 1. The origin of this source code must not be misrepresented. 2. Altered versions must be plainly marked as such and must not be misrepresented as being the original source. 3. This Copyright notice may not be removed or altered from any source or altered source distribution. The Contributing Authors and Group 42, Inc. specifically permit, without fee, and encourage the use of this source code as a component to supporting the PNG file format in commercial products. If you use this source code in a product, acknowledgment is not required but would be appreciated. A "png_get_copyright" function is available, for convenient use in "about" boxes and the like: printf("%s",png_get_copyright(NULL)); Also, the PNG logo (in PNG format, of course) is supplied in the files "pngbar.png" and "pngbar.jpg (88x31) and "pngnow.png" (98x31). Libpng is OSI Certified Open Source Software. OSI Certified Open Source is a certification mark of the Open Source Initiative. Glenn Randers-Pehrson glennrp@users.sourceforge.net September 12, 2004 === zlib The directories modules/zlib/src and security/nss/cmd/zlib are licensed under the following terms: (C) 1995-2004 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Jean-loup Gailly Mark Adler jloup@gzip.org madler@alumni.caltech.edu If you use the zlib library in a product, we would appreciate *not* receiving lengthy legal documents to sign. The sources are provided for free but without warranty of any kind. The library has been entirely written by Jean-loup Gailly and Mark Adler; it does not include third-party code. If you redistribute modified sources, we would appreciate that you include in the file ChangeLog history information documenting your changes. Please read the FAQ for more information on the distribution of modified source versions. === expat The directory parser/expat is licensed under the following terms: (from parser/expat/COPYING) Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd and Clark Cooper Copyright (c) 2001, 2002 Expat maintainers. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. debian/thunderbird-testsuite.links.in0000664000000000000000000000004612311704701015174 0ustar @MOZ_LIBDIR@ @MOZ_LIBDIR@/testing/bin debian/control.langpacks.unavail0000664000000000000000000000062112311704701014166 0ustar Package: @MOZ_PKG_NAME@-locale-@LANGCODE@ Architecture: any Depends: ${misc:Depends} Conflicts: ${app:Conflicts} Provides: ${app:Provides} Description: Transitional package for unavailable language This language is unavailable for the current version of Thunderbird . This is an empty transitional package to ensure a clean upgrade process. You can safely remove this package after installation. debian/thunderbird-mozsymbols.install.in0000664000000000000000000000011412311704701015703 0ustar %%ifdef MOZ_ENABLE_BREAKPAD @MOZ_DISTDIR@/*symbols.zip @MOZ_LIBDIR@ %%endif debian/apport/0000775000000000000000000000000012311704701010471 5ustar debian/apport/blacklist.in0000664000000000000000000000020312311704701012764 0ustar %%ifdef MOZ_BUILD_OFFICIAL %%ifdef MOZ_ENABLE_BREAKPAD /@MOZ_LIBDIR@/@MOZ_APP_NAME@ /@MOZ_LIBDIR@/plugin-container %%endif %%endif debian/apport/source_thunderbird.py.in0000664000000000000000000015155712311704701015360 0ustar '''thunderbird apport hook /usr/share/apport/package-hooks/thunderbird.py Copyright (c) 2007: Hilario J. Montoliu (c) 2011: Chris Coulson This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. See http://www.gnu.org/copyleft/gpl.html for the full text of the license. ''' import os import os.path import sys import fcntl import subprocess import struct from subprocess import Popen try: from configparser import ConfigParser except ImportError: from ConfigParser import ConfigParser import sqlite3 import tempfile import re import apport.packaging from apport.hookutils import * from glob import glob import zipfile import stat import functools if sys.version_info[0] < 3: import codecs DISTRO_ADDONS = [ '@MOZ_PKG_NAME@' ] class PrefParseError(Exception): def __init__(self, msg, filename, linenum): super(PrefParseError, self).__init__(msg) self.msg = msg self.filename = filename self.linenum = linenum def __str__(self): return self.msg + ' @ ' + self.filename + ':' + str(self.linenum) class PluginRegParseError(Exception): def __init__(self, msg, linenum): super(PluginRegParseError, self).__init__(msg) self.msg = msg self.linenum = linenum def __str__(self): return self.msg + ' @ line ' + str(self.linenum) class ExtensionTypeNotRecognised(Exception): def __init__(self, ext_type, ext_id): super(ExtensionTypeNotRecognised, self).__init__(ext_type, ext_id) self.ext_type = ext_type self.ext_id = ext_id def __str__(self): return "Extension type not recognised: %s (ID: %s)" % (self.ext_type, self.ext_id) class VersionCompareFailed(Exception): def __init__(self, a, b, e): if a == None: a = '' if b == None: b = '' super(VersionCompareFailed, self).__init__(a, b, e) self.a = a self.b = b self.e = e def __str__(self): return "Failed to compare versions A = %s, B = %s (%s)" % (self.a, self.b, str(self.e)) def _open(filename, mode): if sys.version_info[0] < 3: return codecs.open(filename, mode, 'utf-8') return open(filename, mode) def mkstemp_copy(path): '''Make a copy of a file to a temporary file, and return the path''' (outfd, outpath) = tempfile.mkstemp() outfile = os.fdopen(outfd, 'wb') infile = open(path, 'rb') total = 0 while True: data = infile.read(4096) total += len(data) outfile.write(data) infile.seek(total) outfile.seek(total) if len(data) < 4096: break return outpath def anonymize_path(path, profiledir = None): if profiledir != None and path == os.path.join(profiledir, 'prefs.js'): return 'prefs.js' elif profiledir != None and path == os.path.join(profiledir, 'user.js'): return 'user.js' elif profiledir != None and path.startswith(profiledir): return os.path.join('[Profile]', os.path.relpath(path, profiledir)) elif path.startswith(os.environ['HOME']): return os.path.join('[HomeDir]', os.path.relpath(path, os.environ['HOME'])) else: return path class CompatINIParser(ConfigParser): def __init__(self, path): ConfigParser.__init__(self) self.read(os.path.join(path, "compatibility.ini")) @property def last_version(self): if not self.has_section("Compatibility") or not self.has_option("Compatibility", "LastVersion"): return None return re.sub(r'([^_]*)(.*)', r'\1', self.get("Compatibility", "LastVersion")) @property def last_buildid(self): if not self.has_section("Compatibility") or not self.has_option("Compatibility", "LastVersion"): return None return re.sub(r'([^_]*)_([^/]*)/(.*)', r'\2', self.get("Compatibility", "LastVersion")) class AppINIParser(ConfigParser): def __init__(self, path): ConfigParser.__init__(self) self.read(os.path.join(path, "application.ini")) @property def buildid(self): if not self.has_section('App') or not self.has_option('App', 'BuildID'): return None return self.get('App', 'BuildID') @property def appid(self): if not self.has_section('App') or not self.has_option('App', 'ID'): return None return self.get('App', 'ID') class ExtensionINIParser(ConfigParser): def __init__(self, path): ConfigParser.__init__(self) self.read(os.path.join(path, "extensions.ini")) self._extensions = [] if self.has_section('ExtensionDirs'): items = self.items('ExtensionDirs') for item in items: self._extensions.append(item[1]) def __getitem__(self, key): if key > len(self) - 1: raise IndexError return self._extensions[key] def __iter__(self): class ExtensionINIParserIter: def __init__(self, parser): self.parser = parser self.index = 0 def __next__(self): if self.index == len(self.parser): raise StopIteration res = self.parser[self.index] self.index += 1 return res def next(self): return self.__next__() return ExtensionINIParserIter(self) def __len__(self): return len(self._extensions) def compare_versions(a, b): '''Compare 2 version numbers, returns -1 for ab This is basically just a python reimplementation of nsVersionComparator''' class VersionPart: def __init__(self): self.numA = 0 self.strB = None self.numC = 0 self.extraD = None def parse_version(part): res = VersionPart() if part == None or part == '': return (part, res) spl = part.split('.') if part == '*' and len(part) == 1: try: res.numA = sys.maxint except: res.numA = sys.maxsize # python3 res.strB = "" else: res.numA = int(re.sub(r'([0-9]*)(.*)', r'\1', spl[0])) res.strB = re.sub(r'([0-9]*)(.*)', r'\2', spl[0]) if res.strB == '': res.strB = None if res.strB != None: if res.strB[0] == '+': res.numA += 1 res.strB = "pre" else: tmp = res.strB res.strB = re.sub(r'([^0-9+-]*)([0-9]*)(.*)', r'\1', tmp) strC = re.sub(r'([^0-9+-]*)([0-9]*)(.*)', r'\2', tmp) if strC != '': res.numC = int(strC) res.extraD = re.sub(r'([^0-9+-]*)([0-9]*)(.*)', r'\3', tmp) if res.extraD == '': res.extraD = None return (re.sub(r'([^\.]*)\.*(.*)', r'\2', part), res) def strcmp(stra, strb): if stra == None and strb == None: return 0 elif stra == None and strb != None: return 1 elif stra != None and strb == None: return -1 if stra < strb: return -1 elif stra > strb: return 1 else: return 0 def do_compare(apart, bpart): if apart.numA < bpart.numA: return -1 elif apart.numA > bpart.numA: return 1 res = strcmp(apart.strB, bpart.strB) if res != 0: return res if apart.numC < bpart.numC: return -1 elif apart.numC > bpart.numC: return 1 return strcmp(apart.extraD, bpart.extraD) try: saved_a = a saved_b = b while a or b: (a, va) = parse_version(a) (b, vb) = parse_version(b) res = do_compare(va, vb) if res != 0: break except Exception as e: raise VersionCompareFailed(saved_a, saved_b, e) return res class Plugin(object): def __init__(self): self.lib = None self.path = None self.desc = None self._package = None self._checked_package = False def dump(self): if self.path.startswith(os.path.join(os.environ['HOME'], '.@MOZ_APP_NAME@')): location = "[Profile]" else: location = os.path.dirname(self.path) pkgname = ' (%s)' % self.package if self.package != None else '' return ("%s - %s%s" % (self.desc, os.path.join(location, self.lib), pkgname)) @property def package(self): if self._checked_package == False: self._package = apport.packaging.get_file_package(self.path) self._checked_package = True return self._package class PluginRegistry: STATE_PENDING = 0 STATE_START = 1 STATE_PROCESSING_1 = 2 STATE_PROCESSING_2 = 3 STATE_PROCESSING_3 = 4 STATE_FINISHED = 5 def __init__(self, path): self.plugins = [] self._state = PluginRegistry.STATE_PENDING self._current_plugin = None self._profile_path = path self.error = None fd = None try: fd = _open(os.path.join(path, 'pluginreg.dat'), 'r') try: skip = 0 linenum = 1 for line in fd.readlines(): if skip == 0: skip = self._parseline(line, linenum) if skip == -1: break else: skip -= 1 linenum += 1 if skip > 0: raise PluginRegParseError("Unexpected EOF", linenum) except Exception as e: self.error = str(e) except: pass finally: if fd != None: fd.close() def _parseline(self, line, linenum): line = line.strip() if line != '' and line[0] == '[' and self._state != PluginRegistry.STATE_START and self._state != PluginRegistry.STATE_PENDING: raise PluginRegParseError('Unexpected section header', linenum) if self._state == PluginRegistry.STATE_PENDING: if line == '[PLUGINS]': self._state += 1 return 0 elif self._state == PluginRegistry.STATE_START: if line == '': return 0 if line[0] == '[': self._state = PluginRegistry.STATE_FINISHED return -1 self._current_plugin = Plugin() self._current_plugin.lib = line.split(':')[0] self._state += 1 return 0 elif self._state == PluginRegistry.STATE_PROCESSING_1: path = line.split(':')[0] if path[0] != '/': raise PluginRegParseError("Invalid path", linenum) self._current_plugin.path = anonymize_path(path, self._profile_path) self._state += 1 return 3 elif self._state == PluginRegistry.STATE_PROCESSING_2: self._current_plugin.desc = line.split(':')[0] self._state += 1 return 0 elif self._state == PluginRegistry.STATE_PROCESSING_3: self.plugins.append(self._current_plugin) self._state = PluginRegistry.STATE_START return int(line.strip()) else: return -1 def __getitem__(self, key): if key > len(self) - 1: raise IndexError return self.plugins[key] def __iter__(self): class PluginRegistryIter: def __init__(self, registry): self.registry = registry self.index = 0 def __next__(self): if self.index == len(self.registry): raise StopIteration ret = self.registry[self.index] self.index += 1 return ret def next(self): return self.__next__() return PluginRegistryIter(self) def __len__(self): return len(self.plugins) class Prefs: '''Class which represents a pref file. Handles all of the parsing, and can be accessed like a normal python dictionary''' PREF_WHITELIST = [ r'accessibility\.*', r'browser\.fixup\.*', r'browser\.history_expire_*', r'browser\.link\.open_newwindow', r'browser\.mousewheel\.*', r'browser\.places\.*', r'browser\.startup\.homepage', r'browser\.tabs\.*', r'browser\.zoom\.*', r'dom\.*', r'extensions\.autoDisableScopes', r'extensions\.checkCompatibility\.*', r'extensions\.enabledScopes', r'extensions\.lastAppVersion', r'extensions\.minCompatibleAppVersion', r'extensions\.minCompatiblePlatformVersion', r'extensions\.strictCompatibility', r'font\.*', r'general\.skins\.*', r'general\.useragent\.*', r'gfx\.*', r'html5\.*', r'mozilla\.widget\.render\-mode', r'layers\.*', r'javascript\.*', r'keyword\.*', r'layout\.css\.dpi', r'network\.*', r'places\.*', r'plugin\.*', r'plugins\.*', r'print\.*', r'privacy\.*', r'security\.*', r'webgl\.*' ] PREF_BLACKLIST = [ r'^network.*proxy\.*', r'.*print_to_filename$', r'print\.tmp\.', r'print\.printer_*', r'printer_*' ] STATE_READY = 0 STATE_COMMENT_MAYBE_START = 1 STATE_COMMENT_BLOCK = 2 STATE_COMMENT_BLOCK_MAYBE_END = 3 STATE_PARSE_UNTIL_OPEN_PAREN = 4 STATE_PARSE_UNTIL_NAME = 5 STATE_PARSE_UNTIL_COMMA = 6 STATE_PARSE_UNTIL_VALUE = 7 STATE_PARSE_UNTIL_CLOSE_PAREN = 8 STATE_PARSE_UNTIL_SEMICOLON = 9 STATE_PARSE_STRING = 10 STATE_PARSE_ESC_SEQ = 11 STATE_PARSE_INT = 12 STATE_SKIP = 13 STATE_PARSE_UNTIL_EOL = 14 def __init__(self, profile_path, extra_paths=None, whitelist=None, blacklist=None): self.whitelist = whitelist if whitelist != None else Prefs.PREF_WHITELIST self.blacklist = blacklist if blacklist != None else Prefs.PREF_BLACKLIST self.prefs = {} self.pref_sources = [] self.errors = {} self._profile_path = profile_path # Read all preferences. Note that we hide preferences that are considered # default (ie, all of those set by the Firefox package or bundled addons, # unless any of the pref files have been modified by the user). # The load order is *very important* if profile_path != None: locations = [ "/@MOZ_LIBDIR@/omni.ja:greprefs.js", "/@MOZ_LIBDIR@/omni.ja:defaults/pref/*.js", "/@MOZ_LIBDIR@/defaults/pref/*.js", "/@MOZ_LIBDIR@/defaults/pref/unix.js", "/@MOZ_LIBDIR@/omni.ja:defaults/preferences/*.js" "/@MOZ_LIBDIR@/defaults/preferences/*.js" ] append_dirs = [ 'defaults/preferences/*.js' ] if os.path.isdir('/@MOZ_LIBDIR@/distribution/bundles'): bundles = os.listdir('/@MOZ_LIBDIR@/distribution/bundles') bundles.sort(reverse=True) for d in append_dirs: for bundle in bundles: path = os.path.join('/@MOZ_LIBDIR@/distribution/bundles', bundle) if path.endswith('.xpi'): locations.append(path + ':' + d) elif os.path.isdir(path): locations.append(os.path.join(path, d)) locations.append(os.path.join(profile_path, "prefs.js")) locations.append(os.path.join(profile_path, "user.js")) extensions = ExtensionINIParser(profile_path) for extension in extensions: if extension.endswith('.xpi'): locations.append(extension + ':defaults/preferences/*.(J|j)(S|s)') elif os.path.isdir(extension): locations.append(os.path.join(extension, 'defaults/preferences/*.js')) locations.append(os.path.join(profile_path, 'preferences/*.js')) else: locations = [] if extra_paths != None: for extra in extra_paths: locations.append(extra) for location in locations: m = re.match(r'^([^:]*):?(.*)', location) if m.group(2) == '': files = glob(location) files.sort(reverse=True) for f in files: self._parse_file(f) else: self._parse_jar(m.group(1), m.group(2)) def _should_ignore_file(self, filename): realpath = os.path.realpath(filename) package = apport.packaging.get_file_package(realpath) if package and apport.packaging.is_distro_package(package) and \ package in DISTRO_ADDONS and \ realpath[1:] not in apport.packaging.get_modified_files(package): return True return False def _parse_file(self, filename): f = None self._state = Prefs.STATE_READY try: f = _open(filename, 'r') try: linenum = 1 state = None for line in f.readlines(): state = self._parseline(line, filename, linenum, state) linenum += 1 except Exception as e: self.errors[filename] = str(e) except: pass finally: if f != None: f.close() if filename not in self.errors \ and not self._should_ignore_file(filename): self.pref_sources.append(filename) def _parse_jar(self, jar, match): jarfile = None try: jarfile = zipfile.ZipFile(jar) entries = jarfile.namelist() entries.sort(reverse=True) for entry in entries: if re.match(r'^' + match + '$', entry): source = '%s:%s' % (jar, entry) try: f = jarfile.open(entry, 'r') linenum = 1 state = None for line in f.readlines(): state = self._parseline(line.decode('utf-8'), source, linenum, state) linenum += 1 except Exception as e: self.errors[source] = str(e) finally: if source not in self.errors \ and not self._should_ignore_file(jar): self.pref_sources.append(source) except: pass finally: if jarfile != None: jarfile.close() def _maybe_add_pref(self, key, value, source, default, locked): class Pref(object): def __init__(self, profile_path): self._default = None self._value = None self._default_source = None self._value_source = None self.locked = False self._profile_path = profile_path @property def value(self): if self._value != None: return self._value return self._default @property def source(self): if self._value != None: return self._value_source return self._default_source @property def anon_source(self): if self._value != None: return anonymize_path(self._value_source, self._profile_path) return anonymize_path(self._default_source, self._profile_path) def set_value(self, value, source, default, locked): if self.locked == True: return if default == True: self._default = value self._default_source = source else: self._value = value self._value_source = source self.locked = locked for match in self.blacklist: if re.match(match, key): return for match in self.whitelist: if re.match(match, key): if key not in self.prefs: self.prefs[key] = Pref(self._profile_path) self.prefs[key].set_value(value, source, default, locked) def _parseline(self, line, source, linenum, old_state): # XXX: I pity the poor soul who ever needs to change anything inside this function class PrefParseState(object): def __init__(self): self.state = Prefs.STATE_READY def _reset(self): self.next_state = Prefs.STATE_READY self.default = False self.locked = False self.name = None self.value = None self.tmp = None self.skip = None self.quote = None def _get_state(self): return self._state def _set_state(self, state): self._state = state if state == Prefs.STATE_READY: self._reset() state = property(_get_state, _set_state) state = old_state if state == None: state = PrefParseState() index = 0 for c in line: if state.state == Prefs.STATE_READY: if c == '/': state.state = Prefs.STATE_COMMENT_MAYBE_START elif c == '#': state.state = Prefs.STATE_PARSE_UNTIL_EOL elif line.startswith('pref', index): state.default == True state.next_state = Prefs.STATE_PARSE_UNTIL_OPEN_PAREN state.state = Prefs.STATE_SKIP state.skip = 3 elif line.startswith('user_pref', index): state.next_state = Prefs.STATE_PARSE_UNTIL_OPEN_PAREN state.state = Prefs.STATE_SKIP state.skip = 8 elif line.startswith('lockPref', index): state.default = True state.locked = True state.next_state = Prefs.STATE_PARSE_UNTIL_OPEN_PAREN state.state = Prefs.STATE_SKIP state.skip = 7 elif not c.isspace(): raise PrefParseError("Unexpected character '%s' before pref" % c, anonymize_path(source, self._profile_path), linenum) elif state.state == Prefs.STATE_SKIP: state.skip -= 1 if state.skip == 0: state.state = state.next_state state.next_state = Prefs.STATE_READY elif state.state == Prefs.STATE_COMMENT_MAYBE_START: if c == '*': state.state = Prefs.STATE_COMMENT_BLOCK elif c == '/': state.state = Prefs.STATE_PARSE_UNTIL_EOL else: raise PrefParseError("Unexpected '/'", anonymize_path(source, self._profile_path), linenum) elif state.state == Prefs.STATE_PARSE_UNTIL_EOL: pass elif state.state == Prefs.STATE_COMMENT_BLOCK: if c == '*': state.state = Prefs.STATE_COMMENT_BLOCK_MAYBE_END elif state.state == Prefs.STATE_COMMENT_BLOCK_MAYBE_END: if c == '/': state.state = state.next_state state.next_state = Prefs.STATE_READY else: state.state = Prefs.STATE_COMMENT_BLOCK elif state.state == Prefs.STATE_PARSE_UNTIL_OPEN_PAREN: if c == '(': state.state = Prefs.STATE_PARSE_UNTIL_NAME elif c == '/': state.next_state = state.state state.state = Prefs.STATE_COMMENT_MAYBE_START elif not c.isspace(): raise PrefParseError("Unexpected character '%s' before open parenthesis" % c, anonymize_path(source, self._profile_path), linenum) elif state.state == Prefs.STATE_PARSE_UNTIL_NAME: if c == '"' or c == '\'': state.tmp = '' state.quote = c state.state = Prefs.STATE_PARSE_STRING state.next_state = Prefs.STATE_PARSE_UNTIL_COMMA elif c == '/': state.next_state = state.state state.state = Prefs.STATE_COMMENT_MAYBE_START elif not c.isspace(): raise PrefParseError("Unexpected character '%s' before pref name" % c, anonymize_path(source, self._profile_path), linenum) elif state.state == Prefs.STATE_PARSE_STRING: if c == '\\': state.state = Prefs.STATE_PARSE_ESC_SEQ elif c == state.quote: state.state = state.next_state state.next_state = Prefs.STATE_READY else: state.tmp += c elif state.state == Prefs.STATE_PARSE_ESC_SEQ: # XXX: We don't handle UTF16 / hex here if c == 'n': c = '\n' elif c == 'r': c = '\r' state.tmp += c state.state = Prefs.STATE_PARSE_STRING elif state.state == Prefs.STATE_PARSE_UNTIL_COMMA: if state.tmp != None: state.name = state.tmp state.tmp = None if c == ',': state.state = Prefs.STATE_PARSE_UNTIL_VALUE elif c == '/': state.next_state = state.state state.state = Prefs.STATE_COMMENT_MAYBE_START elif not c.isspace(): raise PrefParseError("Unexpected character '%s' before comma" % c, anonymize_path(source, self._profile_path), linenum) elif state.state == Prefs.STATE_PARSE_UNTIL_VALUE: if c == '"' or c == '\'': state.tmp = '' state.quote = c state.state = Prefs.STATE_PARSE_STRING state.next_state = Prefs.STATE_PARSE_UNTIL_CLOSE_PAREN elif line.startswith('true', index): state.tmp = True state.next_state = Prefs.STATE_PARSE_UNTIL_CLOSE_PAREN state.state = Prefs.STATE_SKIP state.skip = 3 elif line.startswith('false', index): state.tmp = False state.next_state = Prefs.STATE_PARSE_UNTIL_CLOSE_PAREN state.state = Prefs.STATE_SKIP state.skip = 4 elif (c >= '0' and c <= '9') or c == '+' or c == '-': state.tmp = c state.state = Prefs.STATE_PARSE_INT elif c == '/': state.next_state = state state.state = Prefs.STATE_COMMENT_MAYBE_START elif not c.isspace(): raise PrefParseError("Unexpected character '%s' before value" % c, anonymize_path(source, self._profile_path), linenum) elif state.state == Prefs.STATE_PARSE_INT: if c >= '0' and c <= '9': state.tmp += c elif c == ')': state.value = int(state.tmp) state.tmp = None state.state = Prefs.STATE_PARSE_UNTIL_SEMICOLON elif c.isspace(): state.tmp = int(state.tmp) state.state = Prefs.STATE_PARSE_UNTIL_CLOSE_PAREN elif c == '/': state.tmp = int(state.tmp) state.next_state = Prefs.STATE_PARSE_UNTIL_CLOSE_PAREN state.state = Prefs.STATE_COMMENT_MAYBE_START else: raise PrefParseError("Unexpected character '%s' whilst parsing int" % c, anonymize_path(source, self._profile_path), linenum) elif state.state == Prefs.STATE_PARSE_UNTIL_CLOSE_PAREN: if state.tmp != None: state.value = state.tmp state.tmp = None if c == ')': state.state = Prefs.STATE_PARSE_UNTIL_SEMICOLON elif c == '/': state.next_state = state.state state.state = Prefs.STATE_COMMENT_MAYBE_START elif not c.isspace(): raise PrefParseError("Unexpected character '%s' before close parenthesis" % c, anonymize_path(source, self._profile_path), linenum) elif state.state == Prefs.STATE_PARSE_UNTIL_SEMICOLON: if c == ';': self._maybe_add_pref(state.name, state.value, source, state.default, state.locked) state.state = Prefs.STATE_READY elif c == '/': state.next_state = state.state state.state = Prefs.STATE_COMMENT_MAYBE_START elif not c.isspace(): raise PrefParseError("Unexpected character '%s' before semicolon" % c, anonymize_path(source, self._profile_path), linenum) index += 1 if state.state == Prefs.STATE_PARSE_UNTIL_EOL: state.state = Prefs.STATE_READY return state def __getitem__(self, key): res = self.prefs[key] if res.source in self.pref_sources: return res raise KeyError def __iter__(self): class PrefsIter: def __init__(self, prefs): self.index = 0 self.keys = [] for k in prefs.prefs.keys(): try: test = prefs[k] self.keys.append(k) except: pass self.keys.sort() def __next__(self): if self.index == len(self.keys): raise StopIteration res = self.keys[self.index] self.index += 1 return res def next(self): return self.__next__() return PrefsIter(self) def __len__(self): i = 0 for k in self: i += 1 return i class Extension: '''Small class representing an extension''' def __init__(self, ext_id, location, ver, ext_type, active, desc, min_appver, max_appver, cur_appver, visible, userDisabled, appDisabled, softDisabled, foreign, hasBinary, strictCompat, appStrictCompat): self.ext_id = ext_id; self.location = location self.ver = ver self.ext_type = ext_type self.active = active self.desc = desc self.min_appver = min_appver self.max_appver = max_appver self.cur_appver = cur_appver self.visible = visible self.userDisabled = userDisabled self.appDisabled = appDisabled self.softDisabled = softDisabled self.foreign = foreign self.hasBinary = hasBinary self.strictCompat = strictCompat self.appStrictCompat = appStrictCompat def dump(self): active = "Yes" if self.active == True else "No" foreign = "Yes" if self.foreign == True else "No" visible = "Yes" if self.visible == True else "No" hasBinary = "Yes" if self.hasBinary == True else "No" strictCompat = "Yes" if self.strictCompat == True else "No" if self.active == True: disabled_reason = "" elif self.softDisabled == True: disabled_reason = "(Soft-blocked)" elif self.appDisabled == True: disabled_reason = "(Application disabled)" elif self.userDisabled == True: disabled_reason = "(User disabled)" else: disabled_reason = "(Reason unknown)" return ("%s - ID=%s, Version=%s, minVersion=%s, maxVersion=%s, Location=%s, " + "Type=%s, Foreign=%s, Visible=%s, BinaryComponents=%s, StrictCompat=%s, " + "Active=%s %s") % \ (self.desc, self.ext_id, self.ver, self.min_appver, self.max_appver, self.location, self.ext_type, foreign, visible, hasBinary, strictCompat, active, disabled_reason) @property def active_but_incompatible(self): # FIXME: Assumes that extensions.strictCompatibility is always false return self.active and (self.cur_appver != None and \ (compare_versions(self.cur_appver, self.min_appver) == -1 or \ compare_versions(self.cur_appver, self.max_appver) == 1) and \ (self.hasBinary or self.strictCompat or self.appStrictCompat)) class Profile: '''Container to represent a profile''' def __init__(self, id, name, path, is_default, appini): self.extensions = {} self.locales = {} self.themes = {} self.id = id self.name = name self.path = path self.default = is_default self.appini = appini self.prefs = Prefs(path) self.plugins = PluginRegistry(path) try: self._populate_extensions() except: self.extensions = None def _populate_extensions(self): # We copy the db as it's locked whilst Thunderbird is open. This is still racy # though, as it could be modified during the copy, leaving us with a corrupt # DB. Can we detect this somehow? tmp_db = mkstemp_copy(os.path.join(self.path, "extensions.sqlite")) conn = sqlite3.connect(tmp_db) def get_extension_from_row(row): moz_id = row[0] ext_id = row[1] location = row[2] ext_ver = row[3] ext_type = row[4] visible = True if row[6] == 1 else False active = True if row[7] == 1 else False userDisabled = True if row[8] == 1 else False appDisabled = True if row[9] == 1 else False softDisabled = True if row[10] == 1 else False foreign = True if row[11] == 1 else False hasBinary = True if row[12] == 1 else False strictCompat = True if row[13] == 1 else False cur = conn.cursor() cur.execute("select name from locale where id=:id", { "id": row[5] }) desc = cur.fetchone()[0] cur = conn.cursor() cur.execute("select minVersion, maxVersion from targetApplication where addon_internal_id=:id and (id=:appid or id=:tkid)", \ { "id": row[0], "appid": self.appini.appid, "tkid": "toolkit@mozilla.org" }) (min_ver, max_ver) = cur.fetchone() appStrictCompat = 'extensions.strictCompatibility' in self.prefs and \ self.prefs['extensions.strictCompatibility'].value == 'true' return Extension(ext_id, location, ext_ver, ext_type, active, desc, min_ver, max_ver, self.last_version, visible, userDisabled, appDisabled, softDisabled, foreign, hasBinary, strictCompat, appStrictCompat) cur = conn.cursor() cur.execute("select internal_id, id, location, version, type, defaultLocale, " + \ "visible, active, userDisabled, appDisabled, softDisabled, " + \ "isForeignInstall, hasBinaryComponents, strictCompatibility from addon") rows = cur.fetchall() for row in rows: extension = get_extension_from_row(row) if extension.ext_type == "extension": storage_array = self.extensions elif extension.ext_type == "locale": storage_array = self.locales elif extension.ext_type == "theme": storage_array = self.themes else: raise ExtensionTypeNotRecognised(extension.type, extension.ext_id) if not extension.location in storage_array: storage_array[extension.location] = [] storage_array[extension.location].append(extension) os.remove(tmp_db) def _do_dump(self, storage_array): if self.extensions == None: return "extensions.sqlite corrupt or missing" ret = "" for location in storage_array: ret += "Location: " + location + "\n\n" for extension in storage_array[location]: prefix = " (Inactive) " if not extension.active else "" ret += '\t%s%s\n' % (prefix, extension.dump()) ret += "\n\n\n" return ret @property def running(self): if not hasattr(self, '_running'): # We detect if this profile is running or not by trying to lock the lockfile # If we can't lock it, then Thunderbird is running fd = os.open(os.path.join(self.path, ".parentlock"), os.O_WRONLY|os.O_CREAT|os.O_TRUNC, 0o666) lock = struct.pack("hhqqi", 1, 0, 0, 0, 0) try: fcntl.fcntl(fd, fcntl.F_SETLK, lock) self._running = False # If we acquired the lock, ensure that we unlock again immediately lock = struct.pack("hhqqi", 2, 0, 0, 0, 0) fcntl.fcntl(fd, fcntl.F_SETLK, lock) except: self._running = True return self._running def dump_extensions(self): return self._do_dump(self.extensions) def dump_locales(self): return self._do_dump(self.locales) def dump_themes(self): return self._do_dump(self.themes) def dump_prefs(self): ret = '' for pref in self.prefs: if type(self.prefs[pref].value) == int: value = str(self.prefs[pref].value) elif type(self.prefs[pref].value) == bool: value = 'true' if self.prefs[pref].value == True else 'false' else: value = "\"%s\"" % self.prefs[pref].value ret += pref + ': ' + value + ' (' + self.prefs[pref].anon_source + ')\n' return ret def dump_pref_sources(self): ret = '' for source in self.prefs.pref_sources: ret += anonymize_path(source, self.path) + '\n' return ret def dump_pref_errors(self): ret = '' for source in self.prefs.errors: ret += self.prefs.errors[source] + '\n' return ret def dump_plugins(self): if self.plugins.error != None: return "pluginreg.dat exists but isn't parseable. %s" % self.plugins.error ret = '' for plugin in self.plugins: ret += plugin.dump() + '\n' return ret def get_plugin_packages(self, pkglist): if self.plugins.error != None: return None for plugin in self.plugins: if plugin.package != None and plugin.package not in pkglist: pkglist.append(plugin.package) @property def current(self): return True if self.appini.buildid == self.last_buildid or self.appini.buildid == None else False @property def has_active_but_incompatible_extensions(self): if self.last_version == None or self.extensions == None: return False for storage_array in self.extensions, self.locales, self.themes: for location in storage_array: for extension in storage_array[location]: if extension.active_but_incompatible: return True return False def dump_active_but_incompatible_extensions(self): if self.last_version == None or self.extensions == None: return "Unavailable (corrupt or non-existant compatibility.ini or extensions.sqlite)" res = '' for storage_array in self.extensions, self.locales, self.themes: for location in storage_array: for extension in storage_array[location]: if extension.active_but_incompatible: res += extension.desc + " - " + extension.ext_id + "\n" return res def dump_files_with_broken_permissions(self): broken = [] blacklist = [ r'^lock$' ] for dirpath, dirnames, filenames in os.walk(self.path): def check_path(path): relpath = os.path.relpath(path, self.path) for i in blacklist: if re.match(i, relpath): return flags = os.R_OK | os.W_OK if os.path.isdir(path): flags |= os.X_OK if not os.access(path, flags): broken.append(relpath) check_path(dirpath) for name in filenames: check_path(os.path.join(dirpath, name)) uid = os.getuid() broken.sort() broken_txt = '' for file in broken: fstat = os.stat(os.path.join(self.path, file)) summary = "%#o" % (fstat.st_mode & (stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO)) if fstat.st_uid != uid: summary += ', wrong owner' broken_txt += file + ' (' + summary + ')\n' return broken_txt @property def has_forced_layers_acceleration(self): if "layers.acceleration.force-enabled" in self.prefs and self.prefs["layers.acceleration.force-enabled"].value == "true": return True return False @property def compatini(self): if not hasattr(self, '_compatini'): self._compatini = CompatINIParser(self.path) return self._compatini @property def last_version(self): return self.compatini.last_version @property def last_buildid(self): return self.compatini.last_buildid @property def addon_compat_check_disabled(self): is_nightly = re.sub(r'^[^\.]+\.[0-9]+([a-z0-9]*).*', r'\1', self.last_version) == 'a1' if is_nightly == True: pref = "extensions.checkCompatibility.nightly" else: pref = "extensions.checkCompatibility.%s" % re.sub(r'(^[^\.]+\.[0-9]+[a-z]*).*', r'\1', self.last_version) return pref in self.prefs and self.prefs[pref].value == 'false' class Profiles: '''Small class to build an array of profiles from a profile.ini. Can be accessed like a normal array''' def __init__(self, ini_file, appini): self.profiles = [] parser = ConfigParser() parser.read(ini_file) profile_folder = os.path.dirname(ini_file) for section in parser.sections(): if section == "General": continue if not parser.has_option(section, "Path"): continue path = parser.get(section, "Path") name = parser.get(section, "Name") is_default = True if parser.has_option(section, "Default") and parser.getint(section, "Default") == 1 else False self.profiles.append(Profile(section, name, os.path.join(profile_folder, path), is_default, appini)) # No "Default" entry when there is one profile if len(self) == 1: self[0].default = True def __getitem__(self, key): if key > len(self) - 1: raise IndexError return self.profiles[key] def __iter__(self): class ProfilesIter: def __init__(self, profiles): self.profiles = profiles self.index = 0 def __next__(self): if self.index == len(self.profiles): raise StopIteration res = self.profiles[self.index] self.index += 1 return res def next(self): return self.__next__() return ProfilesIter(self) def __len__(self): return len(self.profiles) def dump_profile_summaries(self): res = '' for profile in self: running = " (In use)" if profile.running == True else "" default = " (Default)" if profile.default else "" outdated = " (Out of date)" if not profile.current else "" res += "%s%s - LastVersion=%s/%s%s%s\n" % (profile.id, default, profile.last_version, profile.last_buildid, running, outdated) return res def recent_kernlog(pattern): '''Extract recent messages from kern.log or message which match a regex. pattern should be a "re" object. ''' lines = '' if os.path.exists('/var/log/kern.log'): file = '/var/log/kern.log' elif os.path.exists('/var/log/messages'): file = '/var/log/messages' else: return lines for line in open(file): if pattern.search(line): lines += line return lines def recent_auditlog(pattern): '''Extract recent messages from kern.log or message which match a regex. pattern should be a "re" object. ''' lines = '' if os.path.exists('/var/log/audit/audit.log'): file = '/var/log/audit/audit.log' else: return lines for line in open(file): if pattern.search(line): lines += line return lines def add_info(report, ui): '''Entry point for apport''' def populate_item(key, data): if data != None and data.strip() != '': report[key] = data def append_tag(tag): tags = report.get('Tags', '') if tags: tags += ' ' report['Tags'] = tags + tag ddproc = Popen(['dpkg-divert', '--truename', '/usr/bin/@MOZ_APP_NAME@'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True) truename = ddproc.communicate() if ddproc.returncode == 0 and truename[0].strip() != '/usr/bin/@MOZ_APP_NAME@': ddproc = Popen(['dpkg-divert', '--listpackage', '/usr/bin/@MOZ_APP_NAME@'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True) diverter = ddproc.communicate() report['UnreportableReason'] = "/usr/bin/@MOZ_APP_NAME@ has been diverted by a third party package (%s)" % diverter[0].strip() return conf_dir = os.path.join(os.environ["HOME"], ".@MOZ_APP_NAME@") appini = AppINIParser('/@MOZ_LIBDIR@') populate_item("BuildID", appini.buildid) profiles = Profiles(os.path.join(conf_dir, "profiles.ini"), appini) populate_item("Profiles", profiles.dump_profile_summaries()) if len(profiles) == 0: report["NoProfiles"] = 'True' for profile in profiles: if profile.running and not profile.current: report["UnreportableReason"] = "Thunderbird has been upgraded since you started it. Please restart all instances of Thunderbird and try again" return seen_default = False running_incompatible_addons = False forced_layers_accel = False addon_compat_check_disabled = False for profile in profiles: if profile.default and not seen_default and len(profiles) > 1: prefix = 'DefaultProfile' seen_default = True elif len(profiles) > 1: prefix = profile.id else: prefix = '' populate_item(prefix + "Extensions", profile.dump_extensions()) populate_item(prefix + "Locales", profile.dump_locales()) populate_item(prefix + "Themes", profile.dump_themes()) populate_item(prefix + "Plugins", profile.dump_plugins()) populate_item(prefix + "IncompatibleExtensions", profile.dump_active_but_incompatible_extensions()) populate_item(prefix + "Prefs", profile.dump_prefs()) populate_item(prefix + "PrefSources", profile.dump_pref_sources()) populate_item(prefix + "PrefErrors", profile.dump_pref_errors()) populate_item(prefix + "BrokenPermissions", profile.dump_files_with_broken_permissions()) if (profile.current or profile.default) and profile.has_active_but_incompatible_extensions: running_incompatible_addons = True if (profile.current or profile.default) and profile.has_forced_layers_acceleration: forced_layers_accel = True if (profile.current or profile.default) and profile.addon_compat_check_disabled: addon_compat_check_disabled = True crash_reports = [] report_to_mtime = {} most_recent_report = None most_recent_mtime = 0 for crash in glob(os.path.join(conf_dir, 'Crash Reports', 'submitted', '*.txt')): id = re.sub(r'\.txt$', '', os.path.basename(crash)) report_to_mtime[id] = os.stat(crash).st_mtime crash_reports.append(id) if most_recent_report == None or report_to_mtime[id] > most_recent_mtime: most_recent_report = id most_recent_mtime = report_to_mtime[id] def crashes_sort(a, b): if report_to_mtime[b] > report_to_mtime[a]: return 1 elif report_to_mtime[b] < report_to_mtime[a]: return -1 else: return 0 # Put the most recent first crash_reports.sort(key=functools.cmp_to_key(crashes_sort)) crash_reports_str = '' i = 0 for crash in crash_reports: crash_reports_str += crash + '\n' i += 1 if i == 15: break populate_item('SubmittedCrashIDs', crash_reports_str) populate_item('MostRecentCrashID', most_recent_report) plugin_packages = [] for profile in profiles: profile.get_plugin_packages(plugin_packages) if len(plugin_packages) > 0: attach_related_packages(report, plugin_packages) report["RunningIncompatibleAddons"] = 'True' if running_incompatible_addons == True else 'False' report["ForcedLayersAccel"] = 'True' if forced_layers_accel == True else 'False' report["AddonCompatCheckDisabled"] = 'True' if addon_compat_check_disabled == True else 'False' if '@MOZ_APP_NAME@' == 'thunderbird-trunk': report["Channel"] = 'nightly' append_tag('nightly-channel') if report["SourcePackage"] == 'thunderbird-trunk': report["SourcePackage"] = 'thunderbird' else: channelpref = Prefs(None, ['/@MOZ_LIBDIR@/defaults/pref/channel-prefs.js'], whitelist = [ r'app\.update\.channel' ]) if "app.update.channel" in channelpref: report["Channel"] = channelpref["app.update.channel"].value append_tag(channelpref["app.update.channel"].value + '-channel') else: report["Channel"] = 'Unavailable' if os.path.exists('/sys/bus/pci'): report['Lspci'] = command_output(['lspci','-vvnn']) attach_alsa(report) attach_network(report) attach_wifi(report) # XXX: Apparmor stuff disabled for Thunderbird # Get apparmor stuff if the profile isn't disabled. copied from # source_apparmor.py until apport runs hooks via attach_related_packages #apparmor_disable_dir = "/etc/apparmor.d/disable" #add_apparmor = True #if os.path.isdir(apparmor_disable_dir): # for f in os.listdir(apparmor_disable_dir): # if f.startswith("usr.bin.@MOZ_PKG_NAME@"): # add_apparmor = False # break #if add_apparmor: # attach_related_packages(report, ['apparmor', 'libapparmor1', # 'libapparmor-perl', 'apparmor-utils', 'auditd', 'libaudit0']) # attach_file(report, '/proc/version_signature', 'ProcVersionSignature') # attach_file(report, '/proc/cmdline', 'ProcCmdline') # sec_re = re.compile('audit\(|apparmor|selinux|security', re.IGNORECASE) # report['KernLog'] = recent_kernlog(sec_re) # if os.path.exists("/var/log/audit"): # # this needs to be run as root # report['AuditLog'] = recent_auditlog(sec_re) if __name__ == "__main__": import apport from apport import packaging D = {} D['Package'] = '@MOZ_PKG_NAME@' D['SourcePackage'] = '@MOZ_PKG_NAME@' add_info(D, None) for KEY in D.keys(): print('''-------------------%s: ------------------\n%s''' % (KEY, D[KEY])) debian/apport/native-origins.in0000664000000000000000000000015712311704701013762 0ustar LP-PPA-ubuntu-mozilla-daily LP-PPA-ubuntu-mozilla-daily-thunderbird-aurora LP-PPA-mozillateam-thunderbird-next debian/messagingmenu/0000775000000000000000000000000012311704701012026 5ustar debian/messagingmenu/content/0000775000000000000000000000000012311704701013500 5ustar debian/messagingmenu/content/thunderbirdOverlay.xul0000664000000000000000000000400012311704701020100 0ustar