JFIF   ( %!1!%)+...383,7(-.+  ++-+++++-++-++--+--+-+-------+-++-+--+---+++--+7+-+"F!1AQaq"2BRb#3Sr$CDsT&!Q1Aa"2Rbq ?򉄘ǷLR HR,nNb .&W)fJbMOYxj-\bT2(4CQ"qiC/ " %0Jl"e2V  0SDd2@TV^{cW&F͉x9#l,.XɳvRZ C8S 6ml!@!E! `FS!M #(d)Q lml1ml Ų&x(ʨ2NFmj@D<dN5UN˄uTB emLAy#` ` ` I!I 6āHBxL & J#7BQ.$hv h q+tC"EJ) 8R e2U2Y@j%6PF^4LnNBp"8)4JI-ֲvK ^؊)hz[T5˗",Rҥf8ڤS4ʘ!`D ` X+ L,(hl)*S##`6[`0*L T H*HA@I&&r1kr*r*)N$#L  1#ZFSl `[( ("((he`4 Ch [="A R / 0I`twCDcWh"i) cLad\BcLKHZ"ZEW$Ƚ@A~i^`S *A&h:+c Y6vϕGClRPs.`H`(@<$qDe pL@DpLX, E2MP A  `II m& AQ "AT rbg# g2!SiLj*3L \ G;TFL`K BMy 2S`YLh1 d >-"ZfD^Q DH" RAbEV#Lfq,(rETp64-IJ!*p4F$q;G8DQ/TKP2$jp3KW]FtLtƉ1ol]VBgػJH6 )h61GJR7Nj.Z4piJRDd]t]0dP]:N.b'⹙SvDSz]L,_#ugT&[~?cS^"{Bh{/=ۑxOk̳O59o dar793`)SeYM@\ "$E(Tm&)N2Ih)F5EDed(FS,Pa @!@#@lea HCD$11jCLJqcod S3yd*,lL+QEfsgW1nw)cT#dS HXkFJB"6(ʝH)H"#EZh:Y`khݳh%Sc<mlAko2]gDqQtro=3OƸU9_-t8UvW3sGəg*#:c)><"wc\ASmT|6Ę>9~#1Ƈ~ڒE1vVi# I MM#u$8W 5ǍfƬΜg*Qpi1ȩFOf۔S,/⎯(Lrմ`(Z LsbA \6 6dm[I=!r:REI.wgzG)ԇSbӑxuׇTyyL^e'x^ty4Z&eB]I|v59Jjhm;Ng񷫳n<ϞҼѝjk;׹DlY^ҍ\+x9V!j([cmS.NO6jxNζrm&oײizT$N>?~ Sl-:iڥk\at#E!CL`.O0a*w/WV7/r)DŽt7'Nĵ#7O1 ]{[/-2bA<$&Gm_4t)_>)mjG;V^'k59o>ɌM,ؾf9z6 4v_3T.5V/RD-5 %T5XTޫ4TaZ`U *ƱUƲ UG"5+sJJ2E9#܎kr2G3Bb,XM6H: ?@p!'\4V02aԙ) hbZ]:` ev3ʘ'}!ohȒ*TJjr[RFyQ*#{h{R]J]Lr-.D-.җfo$D ?X0%~1P.Og{cWϫ22&Ϭ_V.W3nmiOl}+!˫#`kR33aUb0-g:qmsέ+0HO|&nhOn+}n5QF_"gvLm/z'+r'n_oC语i|1}Gi|}_D~9JZ_%DVQp\koۅjAs~/c0ksUJi^W9W5!>?O:q|ˣSIB/&K<(lg(%Wg$|LW7vߤW߇q|jef3D H\S6(eJb*@&sTKTW/*@v:.N- @ITʓ1Zg&-eꓝM r]EMס{q$b]'7Z7N:O~lNlP7iͲk)$O^퉢<YSD*hr'Z#5e6t[Fdh AJǔP9P 1\R).Il+jI*,(ܢ22N*OwKFX gc?\mB7iA+εe8 "ġ/p5pW-$މ-[a 5ViAW/V{/&UsF./՞ҕ*)rZg.^_+gt_z-oAbqQn*WlHyZ*\TaEewlLR3ԹȭN}MM}aih"5ܕRT$:~'TcT|*)xGC>n+r{XU xuF"<~67у'fxlf`r3D*#Z1ђfH`2dIWo/qB| 63xxW6^m%Kvg>\>x>!H5Nr8J/FJ9Wx(Hou" S'kWاC\9ְ#^OaҮ+~gnkuЉ,aWU*1 읍jnb|e= :2.UL`Q}YS&gI.c=a`%j:C%2@^>])25/ܙ<lzwɛ)ݣS4h3=J tyϬ.E7 8ڞGZu\_JHsݢϑ}IZ"ӳ=X<Ɖ2{a:{7L+>V}c)*lo Yv&+|L;>+/Sj26K+澡*;>-s"}M2] Ig5aCL*r"&\} #^R.7_Mgf}.ߌy(}Z\gP&ʠHj%</{.]rߙQ`>;5g;u6dԛ %xb|oՋTJ5Ϥ(]XqP>f{Jk2,8'~ZU6tMQsg XKg^2ϓ3},[wo۴I|ܷ%[Ol\Pkr]Y//cg6U⧻/VПi8ys_n<\~cze!!H~x;QJZKȮ^ȧG|cS~8ji,Fo+,y~?pk)u /in3JmkX(Mj1N 4c Epc>BO *LfQO&` c;LjcYf 1ɻ)CLsY^Y5" lP/wuEln&dav,(;'W9ej ku`-KHI՟%ԁʁ 1\}?OjsF^Xn$Ё.օC>D:?I @aGE.ĩ1 $ et~T`߸Ir'RX.Zwc%~U=r>-UaFbǺ?R=Z?i'[ASS;siJrzy>nxu$[_B\4}:r'ҵj1_v-[;y?ֹ0I16 . M%4^!S&t ! h !zQð.bBT ?@]?CHq(rd!.$>/x+bnʎNN#w)` )*f!-ɂ\(طYLHzc`Uq7BfCcE0ԉ4Fم쏠ce5T r͸GVlФ?ѣ} mhrkly.Ts㷖)Mө S^%'g>wk%bP[}j~ǾV#K -Fgv켼ǨgɼeSz/6{M=BPZFu\Q75n3Iݤ.W9QfF{vJwF't[@iVj4G~KOnH߿_Do=.c.One?E+GfGN⧭H?4;u`ua|V-+j4?48n ɦ=-]puv&Jc}K>b%U x8pz6L8AXFsW]N55ҦbIWZQ7ï Ԗ3cjz匩ӺOTɖƴ%a'MI}cdR$ݚIζ̝ LIu>J3{^෠㜦˯xܿe\b"2y'x{ RDW b+o2KFhR0:U늞En>լRӉt Iڹ\ wշQEv"v;EJ)yl[5:F0=b4,\PqKtv4{bQz:>C7"8W#Zjdd| cjz%K %Z 9dD{=NFʳAƩtI)kS*s$`:A\ʬ*ֹ9{Nl|eJ١rQnM%z_#x_•TO><)kyD %GN<~y>vfǧB)F)c\lې(#\ h`fgfjTBdhhHL2Y0^ Y0^-"D!QaI15 m~ gՒd|;#gMn(P$l H.R2^PU")pN` N8󫅂OJ;^jz\uumJMF|ηq[]$Vrrt:Q^;QPkHՠ{]HwˆMuIr7!r&- j%"9LtUb56+^TWBqdhHAD7 HwKH^F3LIq #hK`]IWKiH?کǴeԥQ>g{^q^>HKoOB||8aݏS}{S_]ϸ/X~ܵw'OSPAf֩ܟ[>7 @[ֵ;G߇QU*Cթ *OKU^zz[fRnpcJX9u<iq8B]u8 ]I,;[G#2W.¸D8rPG Y%PBJ= wo;PJgx6;yB`3zZGPAͫy{5Nb_re*ONHR]Ji)U{Ӓ:qqɏ[mB4࢒I$ 2vpBADY`DIVAn"Bh$&&cMbdB 鮆wHR'E(ѸZA*H~{B M҅n\@N{7ISCp Vd( r+bg|ns:qg:|J|ɪV.UVaAS͓FyRuLѦT騬 `3􏳕{eo/Tz8DkW?,cl~TqLne֠[B*D +t 6˦S;5KjV3e WBrT.XSHm sl5F%NGM`Y )": J!W4]HTrPX2 QYɕ\m2VLd+`,^ѺiPztUGY6+cӧ6] U%u/ˈFOiB*nFF#ұJ Z/c')?Q͟5.8E~G6e<\?}GkhMFUظOqhEA - "`dQ#(4Ԧf VLmc@q5J8K; M^JZnn)9Zm\ qIJqS: i[9~Oaƒ]Z4F&+666( N]쁼LM(oyvUI/Χ[ھ]hTˉG".SeYgu;hRDtڬv=5 ׁqMS\Ȭi5D]1$*0UL1QY`QdLb[+z9";'yi`OT/4{@EZ'Y0>4I*d nM#5hі.vrM[]Ä;]\ʦS,叕DQZq0fӌI͋]TNK"#;?F;aURx_4WDm+F*0XJE@){ 1R-E2(@Qh l D rT.Q;[J;[`30`ɀ 2#=JeSsxRjG=`H rLJ@ Y$JaB2/x( "Id'6O0CI$:Ol+}I>[L|iK+]ZrH*2Aʶ uHRd)OrrbSx=5dmue1neܬ"e>Lw94勲u ҏ_4GuоJw]QtgSk(qW(6h|v= 1=P/\YZ|R>"*5W/ίR'o %R$5= .!VIRMf4*aR5nv% Usj:V Lj]Bn/TZ&.2„ܒBP)aYRʌW!#ErGf';tW$czI*\KI,c7Zc-ўj|p+-ђ{eg 2;R_{VLM]7sؒFmԻy853gҾqJG!E̤ӏqzs༿? U#R)ŧU(,>,&,-^e^۔.b EW^n<)\9.QeJuFiSh2"EL8yeCKQD\5R,D5.P]c1STt*ZFJ.T:N #%]M}khOe(͓iEMsɆ3( YF<"Ly^*[ry6.ɸm k݊iT%nM8 $Q#F# q 1*?% iS^4oܗ wWPS,aNޖxOxڽqp#F6&o,7LJuMΤK(Td{U Ƹf|q5U{3[FLNK6ӵQY5+'>Q3FSk).&:5z yZq/*q$d+Ge+$lO@Nڤy5eBvˌ䖥shS:JksgksF ꧸oi-FYxy9[Vȼĝ'_.[y2U*c?E+:TsWՀgOS> z75>ncߏ-Kz8ԋ,Ϧ70Z9_1h$Xiu10)0$+$! qsE4wRkh2*T.s%DH:`:=k.'WB{ ȮRGҷ7чVg)CHS}1ݍԳۂ<8g_4y*-Ml\]mZT)mJ~|k<6zWjf4'*u%RNRȉZA) .VLtp 4 V&mtJ#l˅;&{]8>TmhoLXOeD^_J>]jsSej﫦iOM SK([!Vc5zn-A@p]Ӄ \3kmK>#-sܧ?NLar@Js?…Xldny]݌E5•9.8hh69#7js׳R,'pqt:kgPhRԄ+ՕG9}="ֲ\kǁm R73pg$t3+o |o\]'ee5ɐ.7ѐ|ZعSF{qkx5-$Q h5*1yM$ 7)hJ2Kg`-hn*>)EYDIkBpȩAzfǪ>7O K#lߤg]:u~huُ۵u}(mjGIj܏6ES~/5CiRy|kVKGBޭ3;w /jꏈUu>iƪi:WRo'yr4C/?c:w!?\'?#Q:>u/?uEeuG*xY2)?־CAr*23_ץ}գk1%(_ _6aԗ _4 $ϗ+ϫɆzǾIgu?Y<#_xS>i\uɇ۽r}[ͫyRoWCC!H,iD։"Cj5 4] cTk2YZRBvRY~FqQt^RO-g"QP]Ih/t:ljs YӹqI] wqXp KV+8j} uu8PGP&zF:;8+ Sx9(. Q}:ƻWr,Ũ*'shfƧ-6__5,DH{* qp묘G MA}QRe{dyMucǨɾ7߈Avϩe͜jmUi p3\5,ާbf:o+7#ܾ~iU#up=}˄k{NV8m!ҌiptޜBvKi}!ש3UK)`igӞVMR'J[ky~g&6vǍ7ķ>uXd(3瓓[]QTTqnͮz1~_͓k俸0~Z1գ =18cL 5^lf^k^<ҲJɬcC-[^;J8j_q=WpeA_6 4.Ntc>Sv2Jf;G8. 5[,;ArSTˬmpmzjGe EoǩOgDWaGhz<|kT\$Q=u/ci˜S mN&Ok~'0,a} s + NC-G'(*>vw~&*wYG Ŷ K-L/$߮l/A/^:Z@X- Q-D2`@M2+w$Q"胊"47&+Dh'9Y* L7VhT+ -?K]Ik \Ϣgy) s v z)Z ˦2&ލ OjmG9@8F_u䊜r>3K%Yg-FFI]e+Kxkzװy"\Q4Ri'0+P=V&Sw3N/U|UEt*uS c M*tsBE 2ʃ@Kir(˫LRr璜Zy@].%NbXvz덟 hӰNMe#|g͒po9^licxB[e' {U? mlt%?霋ǒxZc X]ϗ15SeE{-Ӕi~DƯO|ë5a@G=%<ƧAs*+tzo, IpȔ|:X6J3Z5JXd]2 3%v*GvE@(S&SX7D0^{5t Z{ﮄsh- ]ɑqEV=^Ki9äBtI@&pEg*O<`F-}ǎ51H,<~qibQѓɳx#l$G9td1U+Sq%B[jOq+^ޏ7K >YY  $KK{*˝e"|$g"6v,,9.DaA,qэI~ܨ|kdv; hz2]x5{M5M~yלqTzUl9Mӏ.WVnkun !jzKO!v|& ;gۇ2BrI閵C tqHe[Zkގ=Q;OԶiᵞBcIU eN cOGz S__>.hNgG6).J$_Taѯ5^LqeB]O?A]H;ò{^0ٺuޚxB|:q'xu4"9Ο7k^eZ_fQOmzm̗{c3ٵKO|m*ek(8"yO(ٵ{LJb2Ǩkgg1_/qrDՆ[_l\ I~Bsc/x ),,̿@PFޞ>O)<<=5m=^x6}~6qoYGޣiY{uN+<,CǚwVxe~c!,5R4u/9In=G•^PF6ɼM򿶤$"\|78ؖYU cXFOKc4s-=6O<;.ϴ޶$q>e? qY}StirX?e/&R'ʑ[ѯMi{?8\g^>\!-VZCf.ȾzRWMh_{^H)mz}V%չM.EJUz7z>ZW6\BW~:W3!S_4~m ǚ! ;VeGKFڵ858Buj:ZZ(/H׭eav!$gpLV)țAJO~YBꤞ厅XJdjg{hR9~_f '5U+}W5%ZjzgTtozYD @%JK\qymeЪKIIp"xoz\B1$G)8Ԅ Jeyc".yyVBR-%BEA-k^Luj cYwԄ%X!e-4ZRḡlJvYsB԰˗0?RM\TlaߏVu4BmY!UyYylgd!m2$i=[hN,6)_~7͖CDF2zÕ{?l;Hܲk׋!/XAłrCXEI{]P[e! ?%Ktqܱ5! jַĞ*TvAG)fuxTҖV7~ 4=r! ob%jTwU$Bnqed䤿@0P&V]HJ)^YrޯĿbsY8=1! n}UD*7uƫi~!s[W{V9J;~Ӯ|[3s۷dڔIj?qJ'O,IkE]G(5\ۖ7)-g,ŶǗ=~e>k쐁%(g˦o[fxN_baGBm:܆VGЗ,G_D!/og,ҢVܤ_iS_~@ SkidSec Webshell

SkidSec WebShell

Server Address : 172.31.38.4

Web Server : Apache/2.4.58 (Ubuntu)

Uname : Linux ip-172-31-38-4 6.14.0-1017-aws #17~24.04.1-Ubuntu SMP Wed Nov 5 10:48:17 UTC 2025 x86_64

PHP Version : 7.4.33



Current Path : /lib/update-notifier/



Current File : //lib/update-notifier/package-data-downloader
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""Process new requests to download per-package data"""
# Copyright (C) 2012 Canonical Ltd
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of version 3 of the GNU General Public License as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

import glob
import os
import sys
import subprocess
import traceback
import debian.deb822
import string
import debconf
from datetime import datetime

import apt_pkg

# avoid hanging forever (LP: #1243090)
import socket
socket.setdefaulttimeout(60)


DATADIR = "/usr/share/package-data-downloads/"
STAMPDIR = "/var/lib/update-notifier/package-data-downloads/"
NOTIFIER_SOURCE_FILE = \
    "/usr/share/update-notifier/package-data-downloads-failed"
NOTIFIER_FILE = "/var/lib/update-notifier/user.d/data-downloads-failed"
NOTIFIER_PERMANENT_SOURCE_FILE = NOTIFIER_SOURCE_FILE + '-permanently'
NOTIFIER_PERMANENT_FILE = NOTIFIER_FILE + '-permanently'

failures = []
permanent_failures = []


def create_or_update_stampfile(file):
    """Create or update the indicated stampfile, and remove failure flags"""

    try:
        with open(file, 'w'):
            pass
    # Ignore errors
    except Exception:
        traceback.print_exc(file=sys.stderr)

    os.utime(file, None)

    for ext in ('.failed', '.permanent-failure'):
        if os.path.exists(file + ext):
            os.unlink(file + ext)


def mark_hook_failed(hook_name, permanent=False):
    """Create a stampfile recording that a hook failed

    We create separate stampfiles for failed hooks so we can
    keep track of how long the hook has been failing and if the failure
    should be considered permanent."""

    if permanent:
        filename = hook_name + '.permanent-failure'
    else:
        filename = hook_name + '.failed'

    failure_file = os.path.join(STAMPDIR, filename)
    try:
        with open(failure_file, 'w'):
            pass

    # Ignore errors
    except Exception:
        traceback.print_exc(file=sys.stderr)

    for ext in ('', '.failed', '.permanent-failure'):
        stampfile = hook_name + ext
        if filename != stampfile \
           and os.path.exists(os.path.join(STAMPDIR, stampfile)):
            os.unlink(os.path.join(STAMPDIR, stampfile))


def hook_is_permanently_failed(hook_name):
    """Check if this hook has been marked as permanently failing.

    If so, don't raise any more errors about it."""

    failure_file = os.path.join(STAMPDIR, hook_name + '.permanent-failure')
    return os.path.exists(failure_file)


def hook_aged_out(hook_name):
    """Check if this hook has been failing consistently for >= 3 days"""

    failure_file = os.path.join(STAMPDIR, hook_name + '.failed')
    try:
        hook_date = datetime.fromtimestamp(os.stat(failure_file).st_ctime)
        cur_time = datetime.now()
        d = cur_time - hook_date
        if d.days >= 3:
            return True
    except OSError:
        pass
    except Exception:
        traceback.print_exc(file=sys.stderr)
    return False


def record_failure(hook):
    """Record that the named hook has failed"""
    if hook_aged_out(hook):
        permanent_failures.append(hook)
    else:
        failures.append(hook)


def existing_permanent_failures():
    """Return the list of all previously recorded permanent failures"""

    files = glob.glob(os.path.join(STAMPDIR, "*.permanent-failure"))
    return [os.path.splitext(os.path.basename(path))[0] for path in files]


def trigger_update_notifier(failures, permanent=False):
    """Tell update-notifier that there were failed packages"""

    try:
        if permanent:
            with open(NOTIFIER_PERMANENT_SOURCE_FILE, 'r',
                      encoding='utf-8') as f:
                input = f.read()
            output_file = open(NOTIFIER_PERMANENT_FILE, 'w', encoding='utf-8')
        else:
            with open(NOTIFIER_SOURCE_FILE, 'r', encoding='utf-8') as f:
                input = f.read()
            output_file = open(NOTIFIER_FILE, 'w', encoding='utf-8')
    except Exception:
        # Things failed and we can't even notify about it.  Break the
        # trigger so that there's some error propagation, even if not
        # the most pleasant sort.
        traceback.print_exc(file=sys.stderr)
        sys.exit(1)

    packages = [os.path.basename(failure) for failure in failures]
    output_file.write(
        string.Template(input).substitute(
            {'packages': ", ".join(packages)}))
    output_file.close()


def get_hook_file_names():
    res = []
    for relfile in os.listdir(DATADIR):
        # ignore files ending in .dpkg-*
        if (os.path.splitext(relfile)[1]
                and os.path.splitext(relfile)[1].startswith(".dpkg")):
            continue
        res.append(relfile)
    return res


# we use apt-helper here as this gives us the exact same proxy behavior
# as apt-get itself (environment/apt-config proxy settings/autodiscover)
def download_file(uri, sha256_hashsum):
    """Download a URI and checks the given hashsum using apt-helper

    Returns: path to the downloaded file or None
    """
    download_dir = os.path.join(STAMPDIR, "partial")
    dest_file = os.path.join(download_dir, os.path.basename(uri))
    try:
        with open(dest_file, 'rb') as dest_file_obj:
            # apt_pkg can directly hash from file obj, let's use that instead
            # of hashlib
            real_sha256 = apt_pkg.sha256sum(dest_file_obj)
            if real_sha256 == sha256_hashsum:
                return dest_file
            else:
                os.remove(dest_file)
    except FileNotFoundError:
        pass

    ret = subprocess.call(
        ["/usr/lib/apt/apt-helper",
         "download-file", uri, dest_file, "SHA256:" + sha256_hashsum])
    if ret != 0:
        if os.path.exists(dest_file):
            os.remove(dest_file)
        return None
    return dest_file


def print_maybe(*args, **kwargs):
    """Version of print() that ignores failure"""
    try:
        print(*args, **kwargs)
    except OSError:
        pass


def process_download_requests():
    """Process requests to download package data files

    Iterate over /usr/share/package-data-downloads and download any
    package data specified in the contained file, then hand off to
    the indicated handler for further processing.

    Successful downloads are recorded in
    /var/lib/update-notifier/package-data-downloads to avoid unnecessary
    repeat handling.

    Failed downloads are reported to the user via the
    update-notifier interface."""

    # Iterate through all the available hooks.  If we get a failure
    # processing any of them (download failure, checksum failure, or
    # failure to run the hook script), record it but continue processing
    # the rest of the hooks since some of them may succeed.
    for relfile in get_hook_file_names():

        stampfile = os.path.join(STAMPDIR, relfile)
        file = os.path.join(DATADIR, relfile)
        try:
            if not os.path.exists(NOTIFIER_FILE) and \
                    not os.path.exists(NOTIFIER_PERMANENT_FILE):
                hook_date = os.stat(file).st_mtime
                stamp_date = os.stat(stampfile).st_mtime
                if hook_date < stamp_date:
                    continue
            elif os.path.exists(stampfile):
                continue

        except Exception as e:
            if not isinstance(e, OSError):
                traceback.print_exc(file=sys.stderr)

        hook = debian.deb822.Deb822()
        files = []
        sums = []
        for para in hook.iter_paragraphs(open(file)):
            if 'Script' in para:
                if not files:
                    record_failure(relfile)
                    break
                command = [para['Script']]

                if 'Should-Download' in para:
                    db = debconf.DebconfCommunicator('update-notifier')
                    try:
                        should = db.get(para['Should-Download'])
                        if should == "false":
                            # Do nothing with this file.
                            break
                    except (DebconfError, KeyError):
                        pass
                    finally:
                        db.shutdown()

                print_maybe("%s: processing..." % (relfile))

                # Download each file and verify the sum
                try:
                    downloaded = set()
                    for i in range(len(files)):
                        print_maybe("%s: downloading %s" % (relfile, files[i]))
                        dest_file = download_file(files[i], sums[i])
                        if dest_file:
                            command.append(dest_file)
                            downloaded.add(dest_file)
                        else:
                            record_failure(relfile)
                            break
                    if relfile in failures + permanent_failures:
                        break

                    sys.stdout.flush()
                    result = subprocess.call(command)
                    if result:
                        # There's no sense redownloading if the script fails
                        permanent_failures.append(relfile)
                    else:
                        create_or_update_stampfile(stampfile)
                    # cleanup
                    for f in downloaded:
                        os.remove(f)
                    break
                except Exception:
                    traceback.print_exc(file=sys.stderr)

                record_failure(relfile)
                # The 'script' is always the last stanza
                break

            # Not in a 'script' stanza, so we should have some urls
            try:
                files.append(para['Url'])
                sums.append(para['Sha256'])
            except Exception as e:
                print_maybe("%s: Error processing!" % (relfile))
                if not isinstance(e, KeyError):
                    traceback.print_exc(file=sys.stderr)
                record_failure(relfile)
                break

    previous_failures = existing_permanent_failures()

    # We only report about "permanent" failures when there are new ones,
    # but we want the whole list of permanently-failing hooks so when
    # we clobber the update-notifier file we don't lose information the
    # user may not have seen yet
    if permanent_failures:
        new_failures = False
        for failure in permanent_failures:
            if failure not in previous_failures:
                mark_hook_failed(failure, permanent=True)
                previous_failures.append(failure)
                new_failures = True
        if new_failures:
            trigger_update_notifier(previous_failures, permanent=True)
        # 2016-09-19 14:36 reset the list of permanent_failures as it caused
        # tests not to be idempotent
        permanent_failures.clear()
    if not previous_failures and os.path.exists(NOTIFIER_PERMANENT_FILE):
        os.unlink(NOTIFIER_PERMANENT_FILE)

    # Filter out new failure reports for permanently-failed packages
    our_failures = [x for x in failures if x not in previous_failures]
    # 2016-09-19 14:36 reset the list of permanent_failures as it caused
    # tests not to be idempotent
    failures.clear()

    if our_failures:
        for failure in our_failures:
            mark_hook_failed(failure)
        trigger_update_notifier(our_failures)
    elif os.path.exists(NOTIFIER_FILE):
        os.unlink(NOTIFIER_FILE)


if __name__ == "__main__":
    process_download_requests()