1
2 '''
3 # DESCRIPTION:
4 # Entropy Object Oriented Interface
5
6 Copyright (C) 2007-2009 Fabio Erculiani
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 '''
22
23 import os
24 import shutil
25 from entropy.exceptions import IncorrectParameter, InvalidData
26 from entropy.const import etpConst, etpCache, etpUi, const_setup_perms
27 from entropy.i18n import _
28 from entropy.output import blue, bold, red, darkgreen, darkred
29
31
32 """
33 ~~ GIVES YOU WINGS ~~
34 """
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51 import entropy.tools as entropyTools
53
54
55 from entropy.client.interfaces import Client
56 if not isinstance(EquoInstance, Client):
57 mytxt = _("A valid Client interface instance is needed")
58 raise IncorrectParameter("IncorrectParameter: %s" % (mytxt,))
59
60 self.Entropy = EquoInstance
61 from entropy.cache import EntropyCacher
62 self.__cacher = EntropyCacher()
63 from entropy.core import SystemSettings
64 self.SystemSettings = SystemSettings()
65 self.lastfetch = None
66 self.previous_checksum = "0"
67 self.advisories_changed = None
68 self.adv_metadata = None
69 self.affected_atoms = None
70
71 from xml.dom import minidom
72 self.minidom = minidom
73
74 self.op_mappings = {
75 "le": "<=",
76 "lt": "<",
77 "eq": "=",
78 "gt": ">",
79 "ge": ">=",
80 "rge": ">=",
81 "rle": "<=",
82 "rgt": ">",
83 "rlt": "<"
84 }
85
86 security_url = \
87 self.SystemSettings['repositories']['security_advisories_url']
88 security_file = os.path.basename(security_url)
89 md5_ext = etpConst['packagesmd5fileext']
90
91 self.unpackdir = os.path.join(etpConst['entropyunpackdir'],
92 "security-%s" % (self.entropyTools.get_random_number(),))
93 self.security_url = security_url
94 self.unpacked_package = os.path.join(self.unpackdir, "glsa_package")
95 self.security_url_checksum = security_url + md5_ext
96
97 self.download_package = os.path.join(self.unpackdir, security_file)
98 self.download_package_checksum = self.download_package + md5_ext
99 self.old_download_package_checksum = os.path.join(
100 etpConst['dumpstoragedir'], os.path.basename(security_url)
101 ) + md5_ext
102
103 self.security_package = os.path.join(etpConst['securitydir'],
104 os.path.basename(security_url))
105 self.security_package_checksum = self.security_package + md5_ext
106
107 try:
108
109 if os.path.isfile(etpConst['securitydir']) or \
110 os.path.islink(etpConst['securitydir']):
111 os.remove(etpConst['securitydir'])
112
113 if not os.path.isdir(etpConst['securitydir']):
114 os.makedirs(etpConst['securitydir'], 0775)
115
116 except OSError:
117 pass
118 const_setup_perms(etpConst['securitydir'], etpConst['entropygid'])
119
120 if os.access(self.old_download_package_checksum, os.F_OK | os.R_OK):
121 f_down = open(self.old_download_package_checksum)
122 try:
123 self.previous_checksum = f_down.readline().strip().split()[0]
124 except (IndexError, OSError, IOError,):
125 pass
126 f_down.close()
127
129
130 if os.path.isfile(self.unpackdir) or os.path.islink(self.unpackdir):
131 os.remove(self.unpackdir)
132
133 if os.path.isdir(self.unpackdir):
134 shutil.rmtree(self.unpackdir, True)
135 try:
136 os.rmdir(self.unpackdir)
137 except OSError:
138 pass
139
140 os.makedirs(self.unpackdir, 0775)
141 const_setup_perms(self.unpackdir, etpConst['entropygid'])
142
145
147 return self.__generic_download(self.security_url_checksum,
148 self.download_package_checksum, show_speed = False)
149
151 fetcher = self.Entropy.urlFetcher(url, save_to, resume = False,
152 show_speed = show_speed)
153 fetcher.progress = self.Entropy.progress
154 rc_fetch = fetcher.download()
155 del fetcher
156 if rc_fetch in ("-1", "-2", "-3", "-4"):
157 return False
158
159 self.Entropy.setup_default_file_perms(save_to)
160 return True
161
163
164
165 if not os.path.isfile(self.download_package_checksum) or \
166 not os.access(self.download_package_checksum, os.R_OK):
167 return 1
168
169 f_down = open(self.download_package_checksum)
170 read_err = False
171 try:
172 checksum = f_down.readline().strip().split()[0]
173 except (OSError, IOError, IndexError,):
174 read_err = True
175
176 f_down.close()
177 if read_err:
178 return 2
179
180 self.advisories_changed = True
181 if checksum == self.previous_checksum:
182 self.advisories_changed = False
183
184 md5res = self.entropyTools.compare_md5(self.download_package, checksum)
185 if not md5res:
186 return 3
187 return 0
188
197
204
206 for advfile in os.listdir(self.unpacked_package):
207 from_file = os.path.join(self.unpacked_package, advfile)
208 to_file = os.path.join(etpConst['securitydir'], advfile)
209 shutil.move(from_file, to_file)
210
212 shutil.rmtree(self.unpackdir, True)
213
214 - def clear(self, xcache = False):
218
220
221 if self.adv_metadata != None:
222 return self.adv_metadata
223
224 if self.Entropy.xcache:
225 dir_checksum = self.entropyTools.md5sum_directory(
226 etpConst['securitydir'])
227 c_hash = "%s%s" % (
228 etpCache['advisories'], hash("%s|%s|%s" % (
229 hash(self.SystemSettings['repositories']['branch']),
230 hash(dir_checksum),
231 hash(etpConst['systemroot']),
232 )),
233 )
234 adv_metadata = self.__cacher.pop(c_hash)
235 if adv_metadata != None:
236 self.adv_metadata = adv_metadata.copy()
237 return self.adv_metadata
238
240 if self.Entropy.xcache:
241 dir_checksum = self.entropyTools.md5sum_directory(
242 etpConst['securitydir'])
243 c_hash = "%s%s" % (
244 etpCache['advisories'], hash("%s|%s|%s" % (
245 hash(self.SystemSettings['repositories']['branch']),
246 hash(dir_checksum),
247 hash(etpConst['systemroot']),
248 )),
249 )
250 self.__cacher.push(c_hash, adv_metadata)
251
253 if not self.check_advisories_availability():
254 return []
255 xmls = os.listdir(etpConst['securitydir'])
256 xmls = sorted([x for x in xmls if x.endswith(".xml") and \
257 x.startswith("glsa-")])
258 return xmls
259
314
315
316
318 keys = adv_metadata.keys()
319 for key in keys:
320 valid = True
321 if adv_metadata[key]['affected']:
322 affected = adv_metadata[key]['affected']
323 affected_keys = affected.keys()
324 valid = False
325 skipping_keys = set()
326 for a_key in affected_keys:
327 match = self.Entropy.atom_match(a_key)
328 if match[0] != -1:
329
330 valid = True
331 else:
332 skipping_keys.add(a_key)
333 if not valid:
334 del adv_metadata[key]
335 for a_key in skipping_keys:
336 try:
337 del adv_metadata[key]['affected'][a_key]
338 except KeyError:
339 continue
340 try:
341 if not adv_metadata[key]['affected']:
342 del adv_metadata[key]
343 except KeyError:
344 continue
345
346 return adv_metadata
347
349 if not adv_data:
350 adv_data = self.get_advisories_metadata()
351 if adv_key not in adv_data:
352 return False
353 mydata = adv_data[adv_key].copy()
354 del adv_data
355
356 if not mydata['affected']:
357 return False
358
359 for key in mydata['affected']:
360
361 vul_atoms = mydata['affected'][key][0]['vul_atoms']
362 unaff_atoms = mydata['affected'][key][0]['unaff_atoms']
363 unaffected_atoms = set()
364 if not vul_atoms:
365 return False
366 for atom in unaff_atoms:
367 matches = self.Entropy.clientDbconn.atomMatch(atom,
368 multiMatch = True)
369 for idpackage in matches[0]:
370 unaffected_atoms.add((idpackage, 0))
371
372 for atom in vul_atoms:
373 match = self.Entropy.clientDbconn.atomMatch(atom)
374 if (match[0] != -1) and (match not in unaffected_atoms):
375 if self.affected_atoms == None:
376 self.affected_atoms = set()
377 self.affected_atoms.add(atom)
378 return True
379 return False
380
383
386
387
388
390 adv_data = self.get_advisories_metadata()
391 adv_data_keys = adv_data.keys()
392 valid_keys = set()
393 for adv in adv_data_keys:
394 is_affected = self.is_affected(adv, adv_data)
395 if affected == is_affected:
396 valid_keys.add(adv)
397
398 for key in adv_data_keys:
399 if key not in valid_keys:
400 try:
401 del adv_data[key]
402 except KeyError:
403 pass
404
405 for adv in adv_data:
406 for key in adv_data[adv]['affected'].keys():
407 atoms = adv_data[adv]['affected'][key][0]['vul_atoms']
408 applicable = True
409 for atom in atoms:
410 if atom in self.affected_atoms:
411 applicable = False
412 break
413 if applicable == affected:
414 del adv_data[adv]['affected'][key]
415 return adv_data
416
418 adv_data = self.get_advisories_metadata()
419 adv_data_keys = adv_data.keys()
420 del adv_data
421 self.affected_atoms = set()
422 for key in adv_data_keys:
423 self.is_affected(key)
424 return self.affected_atoms
425
547
549 """
550 creates from the information in the I{versionNode} a
551 version string (format <op><version>).
552
553 @type vnode: xml.dom.Node
554 @param vnode: a <vulnerable> or <unaffected> Node that
555 contains the version information for this atom
556 @rtype: String
557 @return: the version string
558 """
559 return self.op_mappings[vnode.getAttribute("range")] + \
560 vnode.firstChild.data.strip()
561
563 """
564 creates from the given package name and information in the
565 I{versionNode} a (syntactical) valid portage atom.
566
567 @type pkgname: String
568 @param pkgname: the name of the package for this atom
569 @type vnode: xml.dom.Node
570 @param vnode: a <vulnerable> or <unaffected> Node that
571 contains the version information for this atom
572 @rtype: String
573 @return: the portage atom
574 """
575 return str(self.op_mappings[vnode.getAttribute("range")] + pkgname + \
576 "-" + vnode.firstChild.data.strip())
577
579 if not os.path.lexists(etpConst['securitydir']):
580 return False
581 if not os.path.isdir(etpConst['securitydir']):
582 return False
583 else:
584 return True
585 return False
586
588
589 mytxt = "%s: %s" % (
590 bold(_("Security Advisories")),
591 blue(_("testing service connection")),
592 )
593 self.Entropy.updateProgress(
594 mytxt,
595 importance = 2,
596 type = "info",
597 header = red(" @@ "),
598 footer = red(" ...")
599 )
600
601 mytxt = "%s: %s %s" % (
602 bold(_("Security Advisories")),
603 blue(_("getting latest GLSAs")),
604 red("..."),
605 )
606 self.Entropy.updateProgress(
607 mytxt,
608 importance = 2,
609 type = "info",
610 header = red(" @@ ")
611 )
612
613 gave_up = self.Entropy.lock_check(
614 self.Entropy.resources_check_lock)
615 if gave_up:
616 return 7
617
618 locked = self.Entropy.application_lock_check()
619 if locked:
620 self.Entropy.resources_remove_lock()
621 return 4
622
623
624 self.Entropy.resources_create_lock()
625 try:
626 rc_lock = self.run_fetch()
627 except:
628 self.Entropy.resources_remove_lock()
629 raise
630 if rc_lock != 0:
631 return rc_lock
632
633 self.Entropy.resources_remove_lock()
634
635 if self.advisories_changed:
636 advtext = "%s: %s" % (
637 bold(_("Security Advisories")),
638 darkgreen(_("updated successfully")),
639 )
640 else:
641 advtext = "%s: %s" % (
642 bold(_("Security Advisories")),
643 darkgreen(_("already up to date")),
644 )
645
646 if do_cache and self.Entropy.xcache:
647 self.get_advisories_metadata()
648 self.Entropy.updateProgress(
649 advtext,
650 importance = 2,
651 type = "info",
652 header = red(" @@ ")
653 )
654
655 return 0
656
658
659 self.__prepare_unpack()
660
661
662 status = self.__download_glsa_package()
663 self.lastfetch = status
664 if not status:
665 mytxt = "%s: %s." % (
666 bold(_("Security Advisories")),
667 darkred(_("unable to download the package, sorry")),
668 )
669 self.Entropy.updateProgress(
670 mytxt,
671 importance = 2,
672 type = "error",
673 header = red(" ## ")
674 )
675 self.Entropy.resources_remove_lock()
676 return 1
677
678 mytxt = "%s: %s %s" % (
679 bold(_("Security Advisories")),
680 blue(_("Verifying checksum")),
681 red("..."),
682 )
683 self.Entropy.updateProgress(
684 mytxt,
685 importance = 1,
686 type = "info",
687 header = red(" # "),
688 back = True
689 )
690
691
692 status = self.__download_glsa_package_cksum()
693 if not status:
694 mytxt = "%s: %s." % (
695 bold(_("Security Advisories")),
696 darkred(_("cannot download the checksum, sorry")),
697 )
698 self.Entropy.updateProgress(
699 mytxt,
700 importance = 2,
701 type = "error",
702 header = red(" ## ")
703 )
704 self.Entropy.resources_remove_lock()
705 return 2
706
707
708 status = self.__verify_checksum()
709
710 if status == 1:
711 mytxt = "%s: %s." % (
712 bold(_("Security Advisories")),
713 darkred(_("cannot open packages, sorry")),
714 )
715 self.Entropy.updateProgress(
716 mytxt,
717 importance = 2,
718 type = "error",
719 header = red(" ## ")
720 )
721 self.Entropy.resources_remove_lock()
722 return 3
723 elif status == 2:
724 mytxt = "%s: %s." % (
725 bold(_("Security Advisories")),
726 darkred(_("cannot read the checksum, sorry")),
727 )
728 self.Entropy.updateProgress(
729 mytxt,
730 importance = 2,
731 type = "error",
732 header = red(" ## ")
733 )
734 self.Entropy.resources_remove_lock()
735 return 4
736 elif status == 3:
737 mytxt = "%s: %s." % (
738 bold(_("Security Advisories")),
739 darkred(_("digest verification failed, sorry")),
740 )
741 self.Entropy.updateProgress(
742 mytxt,
743 importance = 2,
744 type = "error",
745 header = red(" ## ")
746 )
747 self.Entropy.resources_remove_lock()
748 return 5
749 elif status == 0:
750 mytxt = "%s: %s." % (
751 bold(_("Security Advisories")),
752 darkgreen(_("verification Successful")),
753 )
754 self.Entropy.updateProgress(
755 mytxt,
756 importance = 1,
757 type = "info",
758 header = red(" # ")
759 )
760 else:
761 mytxt = _("Return status not valid")
762 raise InvalidData("InvalidData: %s." % (mytxt,))
763
764
765 if os.path.isfile(self.download_package_checksum) and \
766 os.path.isdir(etpConst['dumpstoragedir']):
767
768 if os.path.isfile(self.old_download_package_checksum):
769 os.remove(self.old_download_package_checksum)
770 shutil.copy2(self.download_package_checksum,
771 self.old_download_package_checksum)
772 self.Entropy.setup_default_file_perms(
773 self.old_download_package_checksum)
774
775
776 status = self.__unpack_advisories()
777 if status != 0:
778 mytxt = "%s: %s." % (
779 bold(_("Security Advisories")),
780 darkred(_("digest verification failed, try again later")),
781 )
782 self.Entropy.updateProgress(
783 mytxt,
784 importance = 2,
785 type = "error",
786 header = red(" ## ")
787 )
788 self.Entropy.resources_remove_lock()
789 return 6
790
791 mytxt = "%s: %s %s" % (
792 bold(_("Security Advisories")),
793 blue(_("installing")),
794 red("..."),
795 )
796 self.Entropy.updateProgress(
797 mytxt,
798 importance = 1,
799 type = "info",
800 header = red(" # ")
801 )
802
803
804 self.__clear_previous_advisories()
805
806 self.__put_advisories_in_place()
807
808 self.__cleanup_garbage()
809 return 0
810