2010-05-24 13:38:06 +00:00
# -*- Encoding: utf-8 -*-
2007-02-04 17:35:40 +00:00
###
# Copyright (c) 2006-2007 Dennis Kaarsemaker
2010-05-24 13:38:06 +00:00
# Copyright (c) 2008-2010 Terence Simpson
2017-05-28 00:56:04 +00:00
# Copyright (c) 2017- Krytarik Raido
2007-02-04 17:35:40 +00:00
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of version 2 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.
#
###
2010-05-24 13:38:06 +00:00
2009-01-19 22:26:32 +00:00
import warnings
2017-05-28 00:56:04 +00:00
warnings . filterwarnings ( " ignore " , " apt API not stable yet " , FutureWarning )
2018-03-09 18:56:04 +00:00
import subprocess , os , apt , re
2017-05-28 00:56:04 +00:00
import supybot . utils as utils
from email . parser import FeedParser
2007-02-04 17:35:40 +00:00
def component ( arg ) :
2018-03-09 18:56:04 +00:00
if ' / ' in arg :
return arg [ : arg . find ( ' / ' ) ]
2007-02-04 17:35:40 +00:00
return ' main '
2012-04-10 23:48:30 +00:00
def description ( pkg ) :
2017-05-28 00:56:04 +00:00
if ' Description-en ' in pkg :
2012-04-10 23:48:30 +00:00
return pkg [ ' Description-en ' ] . split ( ' \n ' ) [ 0 ]
2017-05-28 00:56:04 +00:00
elif ' Description ' in pkg :
2012-04-10 23:48:30 +00:00
return pkg [ ' Description ' ] . split ( ' \n ' ) [ 0 ]
2018-03-09 18:56:04 +00:00
return " Description not available "
2012-04-10 23:48:30 +00:00
2018-03-09 18:56:04 +00:00
def apt_cache ( aptdir , distro , cmd , pkg ) :
2017-05-27 02:45:04 +00:00
return subprocess . check_output ( [ ' apt-cache ' ,
2018-03-09 18:56:04 +00:00
' -oAPT::Architecture=amd64 ' ,
' -oAPT::Architectures::=i386 ' ,
' -oAPT::Architectures::=amd64 ' ,
2017-05-27 02:45:04 +00:00
' -oDir::State::Lists= %s / %s ' % ( aptdir , distro ) ,
' -oDir::State::Status= %s / %s .status ' % ( aptdir , distro ) ,
' -oDir::Etc::SourceList= %s / %s .list ' % ( aptdir , distro ) ,
' -oDir::Etc::SourceParts= " " ' ,
2018-03-09 18:56:04 +00:00
' -oDir::Cache= %s /cache ' % aptdir ] +
cmd + [ pkg . lower ( ) ] ) . decode ( ' utf8 ' )
2017-05-27 02:45:04 +00:00
def apt_file ( aptdir , distro , pkg ) :
return subprocess . check_output ( [ ' apt-file ' ,
' -s ' , ' %s / %s .list ' % ( aptdir , distro ) ,
' -c ' , ' %s /apt-file/ %s ' % ( aptdir , distro ) ,
2018-03-09 18:56:04 +00:00
' -l ' , ' -i ' , ' -a ' , ' amd64 ' ,
2017-05-27 02:45:04 +00:00
' search ' , pkg ] ) . decode ( ' utf8 ' )
2007-02-04 17:35:40 +00:00
class Apt :
def __init__ ( self , plugin ) :
self . aptdir = plugin . registryValue ( ' aptdir ' )
self . distros = [ ]
2008-05-07 02:04:23 +00:00
self . plugin = plugin
2008-10-22 17:31:40 +00:00
self . log = plugin . log
2009-10-12 18:26:35 +00:00
os . environ [ " LANG " ] = " C "
2007-02-04 17:35:40 +00:00
if self . aptdir :
2017-05-28 00:56:04 +00:00
self . distros = sorted ( [ x [ : - 5 ] for x in os . listdir ( self . aptdir ) if x . endswith ( ' .list ' ) ] )
2007-02-04 17:35:40 +00:00
2018-03-09 18:56:04 +00:00
def _parse ( self , pkg ) :
parser = FeedParser ( )
parser . feed ( pkg )
return parser . close ( )
def find ( self , pkg , distro , filelookup = True ) :
2007-02-04 17:35:40 +00:00
if distro not in self . distros :
2018-03-09 18:56:04 +00:00
return " %r is not a valid release: %s " % ( distro , " , " . join ( self . distros ) )
if distro . split ( ' - ' ) [ 0 ] in ( ' oldstable ' , ' stable ' , ' unstable ' , ' testing ' , ' experimental ' ) :
pkgTracURL = " https://packages.debian.org "
else :
pkgTracURL = " https://packages.ubuntu.com "
2007-02-04 17:35:40 +00:00
2017-05-27 02:45:04 +00:00
try :
2018-03-09 18:56:04 +00:00
data = apt_cache ( self . aptdir , distro , [ ' search ' , ' -n ' ] , pkg )
2017-05-27 02:45:04 +00:00
except subprocess . CalledProcessError as e :
data = e . output
2007-02-04 17:35:40 +00:00
if not data :
if filelookup :
2017-05-27 02:45:04 +00:00
try :
data = apt_file ( self . aptdir , distro , pkg ) . split ( )
except subprocess . CalledProcessError as e :
data = e . output
2007-02-04 17:35:40 +00:00
if data :
2008-05-05 11:34:57 +00:00
if data [ 0 ] == ' sh: ' : # apt-file isn't installed
2018-03-09 18:56:04 +00:00
self . log . error ( " PackageInfo/packages: apt-file is not installed " )
return " Please use %s / to search for files " % pkgTracURL
2008-05-05 11:34:57 +00:00
if data [ 0 ] == ' E: ' : # No files in the cache dir
2018-03-09 18:56:04 +00:00
self . log . error ( " PackageInfo/packages: Please run the ' update_apt_file ' script " )
return " Cache out of date, please contact the administrator "
2009-01-19 22:26:32 +00:00
if data [ 0 ] == " Use " and data [ 1 ] == " of " :
2018-03-09 18:56:04 +00:00
return " %s /search?searchon=contents&keywords= %s &mode=exactfilename&suite= %s &arch=any " % ( pkgTracURL , utils . web . urlquote ( pkg ) , distro )
2010-09-04 15:19:46 +00:00
if len ( data ) > 10 :
2018-03-09 18:56:04 +00:00
return " File %s found in %s and %d others < %s /search?searchon=contents&keywords= %s &mode=exactfilename&suite= %s &arch=any> " % ( pkg , ' , ' . join ( data [ : 10 ] ) , len ( data ) - 10 , pkgTracURL , utils . web . urlquote ( pkg ) , distro )
2007-02-04 17:35:40 +00:00
return " File %s found in %s " % ( pkg , ' , ' . join ( data ) )
return ' Package/file %s does not exist in %s ' % ( pkg , distro )
return " No packages matching ' %s ' could be found " % pkg
2017-05-27 02:45:04 +00:00
pkgs = [ x . split ( ) [ 0 ] for x in data . split ( ' \n ' ) if x ]
2010-09-04 15:19:46 +00:00
if len ( pkgs ) > 10 :
2018-03-09 18:56:04 +00:00
return " Found: %s and %d others < %s /search?keywords= %s &searchon=names&suite= %s §ion=all> " % ( ' , ' . join ( pkgs [ : 10 ] ) , len ( pkgs ) - 10 , pkgTracURL , utils . web . urlquote ( pkg ) , distro )
2007-02-04 17:35:40 +00:00
else :
return " Found: %s " % ' , ' . join ( pkgs [ : 5 ] )
2018-03-09 18:56:04 +00:00
def raw_info ( self , pkg , distro , archlookup = True ) :
2008-10-22 17:31:40 +00:00
if distro not in self . distros :
2018-03-09 18:56:04 +00:00
return " %r is not a valid release: %s " % ( distro , " , " . join ( self . distros ) )
2007-02-04 17:35:40 +00:00
2017-05-27 02:45:04 +00:00
try :
2018-03-09 18:56:04 +00:00
data = apt_cache ( self . aptdir , distro , [ ' show ' ] , pkg )
2017-05-27 02:45:04 +00:00
except subprocess . CalledProcessError as e :
data = e . output
2010-05-24 13:38:06 +00:00
if not data or ' E: No packages found ' in data :
return ' Package %s does not exist in %s ' % ( pkg , distro )
2018-03-09 18:56:04 +00:00
2014-11-05 11:42:35 +00:00
maxp = { ' Version ' : ' 0~ ' }
2018-03-09 18:56:04 +00:00
packages = list ( map ( self . _parse , [ x for x in data . split ( ' \n \n ' ) if x ] ) )
2010-05-24 13:38:06 +00:00
for p in packages :
2014-11-05 11:42:35 +00:00
if apt . apt_pkg . version_compare ( maxp [ ' Version ' ] , p [ ' Version ' ] ) < = 0 :
2010-05-24 13:38:06 +00:00
maxp = p
2018-03-09 18:56:04 +00:00
if not archlookup :
return maxp
try :
data2 = apt_cache ( self . aptdir , distro , [ ' showsrc ' ] , pkg )
except subprocess . CalledProcessError :
return maxp
2014-11-05 11:42:35 +00:00
maxp2 = { ' Version ' : ' 0~ ' }
2018-03-09 18:56:04 +00:00
packages2 = list ( map ( self . _parse , [ x for x in data2 . split ( ' \n \n ' ) if x ] ) )
2010-05-24 13:38:06 +00:00
for p in packages2 :
2014-11-05 11:42:35 +00:00
if apt . apt_pkg . version_compare ( maxp2 [ ' Version ' ] , p [ ' Version ' ] ) < = 0 :
2010-05-24 13:38:06 +00:00
maxp2 = p
2018-03-09 18:56:04 +00:00
archs = re . match ( r ' .*^ %s \ S+ \ S+ \ S+ arch=(?P<arch> \ S+)$ ' % re . escape ( pkg ) , maxp2 [ ' Package-List ' ] ,
re . I | re . M | re . DOTALL )
if archs :
archs = archs . group ( ' arch ' ) . split ( ' , ' )
if not ( ' any ' in archs or ' all ' in archs ) :
maxp [ ' Architectures ' ] = ' , ' . join ( archs )
2017-05-27 02:45:04 +00:00
return maxp
2018-03-09 18:56:04 +00:00
def info ( self , pkg , distro ) :
maxp = self . raw_info ( pkg , distro )
2017-05-27 02:45:04 +00:00
if isinstance ( maxp , str ) :
return maxp
2010-05-24 13:38:06 +00:00
return ( " %s (source: %s ): %s . In component %s , is %s . Version %s ( %s ), package size %s kB, installed size %s kB %s " %
2018-03-09 18:56:04 +00:00
( maxp [ ' Package ' ] , maxp . get ( ' Source ' , None ) or maxp [ ' Package ' ] , description ( maxp ) , component ( maxp [ ' Section ' ] ) ,
maxp [ ' Priority ' ] , maxp [ ' Version ' ] , distro , int ( maxp [ ' Size ' ] ) / 1024 , maxp [ ' Installed-Size ' ] ,
" . (Only available for %s ) " % maxp [ ' Architectures ' ] if maxp . get ( ' Architectures ' , None ) else " " ) )
2017-05-27 02:45:04 +00:00
2018-03-09 18:56:04 +00:00
def depends ( self , pkg , distro ) :
maxp = self . raw_info ( pkg , distro , archlookup = False )
2017-05-27 02:45:04 +00:00
if isinstance ( maxp , str ) :
return maxp
2018-03-09 18:56:04 +00:00
return ( " %s (version: %s , %s ): Depends on %s %s " %
( maxp [ ' Package ' ] , maxp [ ' Version ' ] , distro , maxp . get ( ' Depends ' , None ) or " nothing " ,
" . Recommends %s " % maxp [ ' Recommends ' ] if maxp . get ( ' Recommends ' , None ) else " " ) )
2009-10-12 18:26:35 +00:00
# Simple test
if __name__ == " __main__ " :
import sys
argv = sys . argv
argc = len ( argv )
if argc == 1 :
2017-05-28 00:56:04 +00:00
print ( " Need at least one arg " )
2009-10-12 18:26:35 +00:00
sys . exit ( 1 )
if argc > 3 :
2017-05-28 00:56:04 +00:00
print ( " Only takes 2 args " )
2009-10-12 18:26:35 +00:00
sys . exit ( 1 )
class FakePlugin :
class FakeLog :
def error ( * args , * * kwargs ) :
pass
def __init__ ( self ) :
self . log = self . FakeLog ( )
def registryValue ( self , * args , * * kwargs ) :
2010-04-13 01:12:54 +00:00
return " /home/bot/aptdir "
2009-10-12 18:26:35 +00:00
try :
2018-03-09 18:56:04 +00:00
( command , lookup ) = argv [ 1 ] . split ( None , 1 )
2009-10-12 18:26:35 +00:00
except :
2018-03-09 18:56:04 +00:00
print ( " Need something to look up " )
2009-10-12 18:26:35 +00:00
sys . exit ( 1 )
2018-03-09 18:56:04 +00:00
dist = " zesty "
2009-10-12 18:26:35 +00:00
if argc == 3 :
2018-03-09 18:56:04 +00:00
dist = argv [ 2 ]
2009-10-12 18:26:35 +00:00
plugin = FakePlugin ( )
aptlookup = Apt ( plugin )
2018-03-09 18:56:04 +00:00
print ( getattr ( aptlookup , command ) ( lookup , dist ) )