~ubuntu-branches/ubuntu/quantal/gozerbot/quantal

« back to all changes in this revision

Viewing changes to build/scripts-2.5/gozerbot-install

  • Committer: Bazaar Package Importer
  • Author(s): Jeremy Malcolm
  • Date: 2010-09-29 18:20:02 UTC
  • mfrom: (3.1.7 sid)
  • Revision ID: james.westby@ubuntu.com-20100929182002-gi532gnem1vlu6jy
Tags: 0.9.1.3-5
Added python2.5 build dependency. 

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/python2.5
 
2
#
 
3
#
 
4
 
 
5
""" signature of downloaded plugins is verified by signature files """
 
6
 
 
7
__copyright__ = 'this file is in the public domain'
 
8
 
 
9
# wrapper for the GNU Privacy Guard
 
10
# (c) Wijnand 'tehmaze' Modderman - http://tehmaze.com
 
11
# BSD License
 
12
 
 
13
import sys, os
 
14
sys.path.insert(0, os.getcwd())
 
15
 
 
16
from gozerbot.config import config
 
17
from gozerbot.datadir import datadir
 
18
from gozerbot.utils.popen import gozerpopen
 
19
from gozerbot.utils.log import rlog, enable_logging
 
20
enable_logging(1000)
 
21
import os
 
22
import re
 
23
import tempfile
 
24
 
 
25
class PgpNoPubkeyException(Exception):
 
26
    pass
 
27
 
 
28
class PgpNoFingerprintException(Exception):
 
29
    pass
 
30
 
 
31
class NoGPGException(Exception):
 
32
    pass
 
33
 
 
34
class Pgp(object):
 
35
    ''' Wrapper for the GNU Privacy Guard. '''   
 
36
 
 
37
    re_verify = re.compile('^\[GNUPG:\] VALIDSIG ([0-9A-F]+)', re.I | re.M)
 
38
    re_import = re.compile('^\[GNUPG:\] IMPORT_OK \d ([0-9A-F]+)', re.I | re.M)
 
39
    re_pubkey = re.compile('^\[GNUPG:\] NO_PUBKEY ([0-9A-F]+)', re.I | re.M)
 
40
 
 
41
    def __init__(self):
 
42
        self.homedir = os.path.join(datadir, 'pgp')
 
43
        if not os.path.exists(self.homedir):
 
44
            rlog(5, 'pgp', 'creating directory %s' % self.homedir)
 
45
            os.mkdir(self.homedir)
 
46
        if hasattr(config, 'pgpkeyserver'):
 
47
            self.keyserver = config.pgpkeyserver
 
48
        else:
 
49
            self.keyserver = 'pgp.mit.edu'
 
50
        # make sure the pgp dir is safe
 
51
        os.chmod(self.homedir, 0700)
 
52
        rlog(5, 'pgp', 'will be using public key server %s' % self.keyserver)
 
53
 
 
54
    def exists(self, keyid):
 
55
        (data, returncode) = self._gpg('--fingerprint', keyid)
 
56
        return returncode == 0
 
57
 
 
58
    def imports(self, data):
 
59
        tmp = self._tmp(data)
 
60
        (data, returncode) = self._gpg('--import', tmp)
 
61
        os.unlink(tmp)
 
62
        test_import = self.re_import.search(data)
 
63
        if test_import:
 
64
            fingerprint = test_import.group(1)
 
65
            return fingerprint
 
66
        return None
 
67
 
 
68
    def imports_keyserver(self, fingerprint):
 
69
        (data, returncode) = self._gpg('--keyserver ', self.keyserver, \
 
70
'--recv-keys', fingerprint)
 
71
        if returncode == 256:
 
72
            raise NoGPGException()
 
73
        return returncode == 0
 
74
 
 
75
    def remove(self, data):
 
76
        if len(data) < 8:
 
77
            return False
 
78
        (_, returncode) = self._gpg('--yes', '--delete-keys',  data)
 
79
        if returncode == 256:
 
80
            raise NoGPGException()
 
81
        return returncode == 0
 
82
 
 
83
    def verify(self, data):
 
84
        tmp = self._tmp(data)
 
85
        (data, returncode) = self._gpg('--verify', tmp)
 
86
        os.unlink(tmp)
 
87
        if returncode == 256:
 
88
            raise NoGPGException()
 
89
        if returncode not in (None, 0, 512):
 
90
            return False
 
91
        test_pubkey = self.re_pubkey.search(data)
 
92
        if test_pubkey:
 
93
            raise PgpNoPubkeyException(test_pubkey.group(1))
 
94
        test_verify = self.re_verify.search(data)
 
95
        if test_verify:
 
96
            return test_verify.group(1)
 
97
 
 
98
    def verify_signature(self, data, signature):
 
99
        tmp1 = self._tmp(data)
 
100
        tmp2 = self._tmp(signature)
 
101
        (data, returncode) = self._gpg('--verify', tmp2, tmp1)
 
102
        os.unlink(tmp2)
 
103
        os.unlink(tmp1)
 
104
        if returncode == 256:
 
105
            raise NoGPGException()
 
106
        if returncode not in (None, 0, 512):
 
107
            return False
 
108
        test_pubkey = self.re_pubkey.search(data)
 
109
        if test_pubkey:
 
110
            raise PgpNoPubkeyException(test_pubkey.group(1))
 
111
        test_verify = self.re_verify.search(data)
 
112
        if test_verify:
 
113
            return test_verify.group(1)
 
114
        return None
 
115
        
 
116
    def _gpg(self, *extra):
 
117
        cmnd = ['gpg', '--homedir', self.homedir, '--batch', '--status-fd', '1']
 
118
        cmnd += list(extra)
 
119
        rlog(0, 'pgp', 'executing: %s' % ' '.join(cmnd))
 
120
        try:
 
121
            proces = gozerpopen(cmnd, [])
 
122
        except OSError, ex:
 
123
            raise NoGPGException
 
124
        except Exception, ex:
 
125
            rlog(0, 'pgp', 'error running "%s": %s' % (cmnd, str(ex)))
 
126
            raise
 
127
        data = proces.fromchild.read()
 
128
        returncode = proces.close()
 
129
        rlog(0, 'pgp', 'return code: %s, data: %s' % (returncode, data))
 
130
        return (data, returncode)
 
131
 
 
132
    def _tmp(self, data):
 
133
        fdid, tmp = tempfile.mkstemp()
 
134
        fd = os.fdopen(fdid, 'w')
 
135
        fd.write(data)
 
136
        fd.close()
 
137
        return tmp
 
138
 
 
139
# these are the gozerbot.org plugin repositories
 
140
gozerbot_pgp_keys = {
 
141
    # gozerbot.org/plugs install key
 
142
    '5290D844CF600B6CA6AFF8952D6D437CC5C9C2B2': '''
 
143
-----BEGIN PGP PUBLIC KEY BLOCK-----
 
144
 
 
145
mQGiBEZM0ZURBADomT07whLs4n/ml84aIl8Ch6ShngfaaOS12ZrBQVQ/VSh7zPOx
 
146
IzfhSmwDJAWLZOnnM5zAqWPuNSJa3hQzY/M+X7vv3/p7kkB54/5U6LW+8ODENnMe
 
147
yPJhbI7phlfeE+STK9hytC3W5+OSrQknXkwYm1bGOur0iiU4Nr16ePE19wCg4KIc
 
148
ecqxm1U2CRtVC6G3qrZpscMD/0loPcv6Yw7Sx+3UwgAFaOJNE73P+h87wz29WkKs
 
149
h8Sx/l+Zf2NW/cMUwR0OOQQpzXKtZ8mF/CuPY4YITThKEGh680dUecuGBk5k3LeE
 
150
ZuLCFagEwZDWqXq/rR7+v8KCaxHpsaoU9P9p3Z7mruvaTYFp+yNfIYAklYoVI2iO
 
151
pB9PA/94xF3Vsdm3rqT6MUCVbBLKRRNHh7RDiFhQb0GfoWQiae0ZdJO2zWKCprKc
 
152
cboswKVs0SEUAD30izAaMlfR/p/LrFDHYwr5bBm38xhRMgFxrqdV+5o1+bdYNGA6
 
153
dJc6XgDWhDHpErCDibGAugNfVCDPhTE1eKbQQXST1KHnhyOcWbQeQmFydCBUaGF0
 
154
ZSA8YmFydEBnb3plcmJvdC5vcmc+iGAEExECACAFAkZM0ZUCGwMGCwkIBwMCBBUC
 
155
CAMEFgIDAQIeAQIXgAAKCRAtbUN8xcnCslzoAKCq7QqTlT10I6WcDZddY9GqcKxp
 
156
VQCaAgX1r9e39X79HR3NxTU6EHgm1hm5Ag0ERkzRnxAIALEuEASs9rpQNcpn6+UJ
 
157
M43J6vu9qYPl6T4ljsTdsi11Lr7r2ltFmvu/RqA9R4M4QwS/JZjm71R3Ci0eJSnR
 
158
AlkhejYHn5TKJ5rzRO6EUL0Jd/Rwgk/9qivIyfKAW3A1To2JgRUP9WI19MKYu6e9
 
159
K5h3KNi/1FfpeOFptB9mHyXBO2rHR9gaN6Wu9uz+eOGlZCyhNRx1jlmVcKAudT2g
 
160
qg/8NsuiEmv9Wf/CMgYawyenqx8S7cYC5EvHEscIOI/8zma0/1JqD8vllbSh64Ih
 
161
fsaW3twx/2gAIgtxRSIW7RQuYhs+zBT2C+Sg91rEgGcXjaw+OW2OZJ4kq1Qba82V
 
162
UOcAAwUH/21DRtgKgAX6xk97l+6ItP19HrcmydxNh299BboSKJDh5RcO1H4xOYyx
 
163
8NCDLhluGNzqeKPbBeW60F3qpkoQMGD7XsVpfKFu4Umn2qtrZAlg6qucgF0476IO
 
164
rhvKskAbNMBlocKzxiT+Ruomkt0Bwa3S1owXlMFwImA8coiWCQ8QnbpvjsYW9OH4
 
165
AgznK5uXKWgMxlZ9SffU0IHXipl3TENXiIdSwUUc2gV5emV/ROFhASNl7YYxZhY6
 
166
ng8YRku3MXM2jN1S/gNX1hvcq20oFjucKx7YkkV1k23FKF6XK776bmD0hYiSH1e0
 
167
uSAlBPAgC5HPqMpFhfyeFnDW0N90DR6ISQQYEQIACQUCRkzRnwIbDAAKCRAtbUN8
 
168
xcnCsoVMAJ472BdMgh+eUJp0+rGCeQhLELJVlQCcCV1zrNw9yoxIGJYnMZZSn4V5
 
169
Yf8=
 
170
=UxRq
 
171
-----END PGP PUBLIC KEY BLOCK-----
 
172
''',
 
173
    # tehmaze.com/plugs install key
 
174
    'D561C5D7CD86CB3CCA1AB5512A22EC17F9EBC3D8': '''
 
175
-----BEGIN PGP PUBLIC KEY BLOCK-----
 
176
 
 
177
mQGiBEZMjOARBACYqwLsv7AMOEHL4wspu0DVXwzVm3U3AU2LivHaJjj66zTd16K4
 
178
1QQ8CjdWVjMLOSH9tvj0Z7wbdyeQEjAxykgIgIUvQ0zuHgzqVsbpW//W/Noc1Z3K
 
179
MIEPBQIDdWM7Ln9+1jZAWIKU6oPU6F9Qt2/8o2NDc0w8O73NKgn/NGWtqwCglitS
 
180
SvUvpP3HZZ8aYwqjk51j0V8D/ilgVNkb/7FumD2yF1R48bJmbRaFu58Hu2IplRr0
 
181
cGHZ1ijCR8fWeMPGY3CakEOjxQa9IkNtoce/PGTgbIl+TLwXSdiiyu5xkEIt2HHm
 
182
F3lzLw2o6T59g2w22KpLeXMO02+LcNdsV/BOrhLiO7E/cDB7wBfd/BG3zNC9C2ln
 
183
cmL4BACL5SEaOE4BCRrOfWdDDUF9iiXGxpCErdxlwId9eFM5OMe48ZmB7oGY52dY
 
184
rzGK54bqkVxRf5MOcUy3IjulZcy/LcOmm+agNnbQPVTuWNfty8+pSs6cPnF2bBUN
 
185
xf+UEZnQRlUHokHBy20DMjV3v8jXMBtZxlB24EmMDE21nAofZrQxdGVobWF6ZS5j
 
186
b20gKGdvemVycGx1Z3MpIDxnb3plcnBsdWdzQHRlaG1hemUuY29tPohgBBMRAgAg
 
187
BQJGTIzgAhsDBgsJCAcDAgQVAggDBBYCAwECHgECF4AACgkQKiLsF/nrw9gngQCf
 
188
esACa4M8ZjvMul8xad4oIUHkMTYAn0+bZBN8ip10/5qIR/CdDvwSL56b
 
189
=RTHp
 
190
-----END PGP PUBLIC KEY BLOCK-----
 
191
''',
 
192
    # trbs his key
 
193
    'B2FF7E64254524AC21EF9B76FF52B757C5181C13': '''
 
194
-----BEGIN PGP PUBLIC KEY BLOCK-----
 
195
Version: GnuPG v1.4.6 (GNU/Linux)
 
196
 
 
197
mQGiBEjE3rURBACHzP3dPU2E8t62zZ+nzksHsmaV4ooJPoysEWWpG9SdC2MGuwQ7
 
198
cnhCWmhxee6VbZ+KbC+pIEpEXeFPK3yaOkO09A1/CKuFTwndDR6Xzy4c/7WauYxq
 
199
hmn5Q/eODoKYseRoGY65u/rpK1/8OlNxjQltr1Ysgw+GHfjkJK2pWNz3wwCgnVZK
 
200
p6TYgb9EHLSjqmXekaOMV5kD/iH10PJXj3V+ToZKrsumP3dxha2TICtWMAo6LHa0
 
201
Qwwkjag93Ji+5yyVM/9UrYzJlqLUapTDit4gmeRm+3Abz6zcOcemowIbNhl/AK8F
 
202
04JaLTr3uOkkBaVRaIYWWPU8pmq9qT2vcPXxYVE87IBURp9XyvEZrhI/DPCtvXsJ
 
203
ZB+3A/0UVOo5anIiejqxk99VB61wYD2jN5SUD/VctrhIJYs0PnNBbD3rKcFwURLy
 
204
WB2cBRXPHoFBOm8h1nWxG/vCMvJoLSI9oU7/AV7j0v6uLSrO0EP8AreQONJi0J29
 
205
mIjIvdCn/f7pmYXJIL+xo+B1H2yd8uCUT66R+byqTlaISOZGe7Q6QmFzIHZhbiBP
 
206
b3N0dmVlbiAoR296ZXJib3QgUGx1Z2lucykgPHYub29zdHZlZW5AZ21haWwuY29t
 
207
PohgBBMRAgAgBQJIxN61AhsjBgsJCAcDAgQVAggDBBYCAwECHgECF4AACgkQ/1K3
 
208
V8UYHBOjywCfUZUZL034A5FL5Dv6jW+ul5FVee8An29ig3yylumzZclKGqnnRn0S
 
209
eb72uQENBEjE3rcQBAC22r4DBZNX6F9LDwtEXLnRpDwP1lf9e5gvD9NMD++zvEfq
 
210
Ezu/1WCg2hgPSJEDlX0sCnCeqyIonl5/3UjPfug9ZtZDOPjZAl2BDIAptwhZoNSd
 
211
ypuC4xOyLog7CXpTO0xHQAG7RhBncU9ieenZQQzTV81nFuWe5Icy1espbAi8xwAD
 
212
BQP6AhoaTKoNv6/JbHrDAigC0mcS7KabeumkRnOCHcZVdPYumto3E63SHZtAXFxI
 
213
ldkoCOhYfuj/3nosydJcSX0EF2rZMr9p0Mc/N+I341DVagi2OlLRYsqqrBbPm37j
 
214
BQNEDl/OUHI17ckd45OqiZ2RYiZlbXBrInFyFNhxElTBeQeISQQYEQIACQUCSMTe
 
215
twIbDAAKCRD/UrdXxRgcE7s4AJ9UzkvCP/tiwzoqe5nQSBqMZAwfUACfS+KCdWq0
 
216
92kg/7BDhzoD5a25BkI=
 
217
=+OEG
 
218
-----END PGP PUBLIC KEY BLOCK-----
 
219
''',
 
220
    # snore his key
 
221
   '5121C252FF91E0BD':"""
 
222
-----BEGIN PGP PUBLIC KEY BLOCK-----
 
223
Version: SKS 1.0.10
 
224
 
 
225
mQGiBERp0bERBAD3MPp/WCvXGP2wMp3TvifsnuJi8gRm4XYodXTVCEViZ7nCHzrN7Szg6y8q
 
226
K1IWP5h6FUXZWV33irZ99FYObDYbRAfLc+MY521JN61tJvcwRP7MMu5N5WNBIwp4xW5qP+z4
 
227
vpO9PodoTBScw6QDBkYuE9j9USB2BMaudlfX1jAk2wCgnwH3Y8Kwe5MZZysoL3KU5/8NIisD
 
228
/1jgMlGefoCSfplc37tKMH0nVOo94HW4Y4XA0NXZZkCVuHZKIscrS0hwW/ss0WjMzGu7FtmU
 
229
X/F2tllKDgKcPEHFXsLhT6dgLxIuzuGYZgr9l42cCcfpgWQCAIcvGiuO7YmTt5cnaJLjQswM
 
230
BcuU78tTa61tWxaAdH+eNNHV/+qgA/0Yj6mOnwpi1Md22Q9L18lS+n6oSJTzv57VyJfjZPGV
 
231
dxGligaBL4uq6zdqb2X8x8tTSUHGCQ8Gl6v+FL1sWn+K3aoPtVcpxU1RYmJ7Zf83Sgdx8qaJ
 
232
kdcfMBosbgEll+znl+vTAswcMGoVr/CkGyfJAjtVplId648+hjtpdoqvK7QiU3RlbiBTcGFu
 
233
cyA8c3RlbkBibGlua2VubGlnaHRzLm5sPohGBBARAgAGBQJEadkEAAoJECbTIkEcy1BHmBwA
 
234
n0NERJ74tkxsP+TF1nZx8iZ+fQ/4AKCM2hHWi71sBC/eU4hezK0sErn6lYhGBBARAgAGBQJE
 
235
aj2gAAoJEJsNdpdhxXCtNSoAoIIljClzClz6pFK1GGpYIaJTbSnYAJ9Q1wbn+K6bYcULYpPp
 
236
okyqMom0OYhMBBMRAgAMBQJEa3OXBYMPB/oaAAoJEOyRFOLTMLr8JIoAnRF1ZsEzTROjG9FP
 
237
0kcnTYxRTp+rAJ9SIZTDED/C0JTX2FeVf/bK9cW7BYhmBBMRAgAmBQJEadGxAhsDBQkPCZwA
 
238
BgsJCAcDAgQVAggDBBYCAwECHgECF4AACgkQUSHCUv+R4L120ACeLhN7vpj4Tc0JoVhEhC2J
 
239
fXydwGUAni0P3WEcB2+I4XEAYvp7/RF6aJSTuQINBERp0vwQCADFtCUtwwwhCLvyp/4a/hGh
 
240
SOpnug41CdG6eULrCi/+mm88r1ANoz9MXRFu5wqxKSIAInsy+WmDc9b6+PBmAiiUsR8qCgot
 
241
AWkzWo8a2BAXFSH9QhZTlhdY6chVzUZ9v4r1tJ+aBcd6+baLefTUGqsqZYwnoEXPK+sGsJNP
 
242
m7dK2/9DmQrpE/+vzS4hhWLRfYBbYRyMpZrbksEQ32p4wH2Vv5aWYudPmXYSlH77wYl4++qg
 
243
LEXIs5NoMaKsQJG7rre7vjAtltfQIw9guRc8UgA3gfaoI4IAUTk6g6J6jKvgUFsGvVA2CC5q
 
244
IYkUZz/ffe12uExg7PxbKZ7GxtkjKInDAAMGCADBA1y1VUAw1wmcMFFRw/Tp5xAd1dy9oH3h
 
245
sdZHBOWH0OSTcRLe014h4pn82HPu4rJQKyvpP/0DjTs8QfmkPn8E5Pgr6MuvcyTvwaCXfwtU
 
246
gI5s95x4nMTtSgrOcOuDulyUuzSLjk+aDqkk78U/N1ck4ifr6PZhrB3CvOPt4YqAWVr+3Bcd
 
247
grNFNqH332fQlLoNNekva0W4ogTv2vYVUNa1LNlOjIldmwlITDPqdLJxQ3kRub/e3KJ1Li1g
 
248
hxYBTzLQYqoJ+WvF6J2IhmH5xo5lu/Gh7aIh50Keyt3ZjE7gK5oQUBozOprOIWx7whVjAu2/
 
249
ZKWRt0IWuWEOLrioyflKiE8EGBECAA8FAkRp0vwCGwwFCQ8JnAAACgkQUSHCUv+R4L2bYwCf
 
250
bqSE/brg8NBAjUB2Bly/GE15KOUAniH5CnF11sfqL3aUUVkW58X+OQLM
 
251
=A6aR
 
252
-----END PGP PUBLIC KEY BLOCK-----
 
253
"""
 
254
}
 
255
 
 
256
# initiate a Pgp instance and import the distkeys, if needed
 
257
pgp = Pgp()
 
258
for keyid, keydata in gozerbot_pgp_keys.iteritems():
 
259
    try:
 
260
        if not pgp.exists(keyid):
 
261
            pgp.imports(keydata)
 
262
    except NoGPGException:
 
263
        rlog(10, 'pgp', 'no pgp installed')
 
264
        break
 
265
 
 
266
from gozerbot.utils.url import geturl, geturl2
 
267
from gozerbot.utils.generic import waitforuser, touch
 
268
from gozerbot.utils.locking import lockdec
 
269
from gozerbot.utils.log import rlog
 
270
from gozerbot.utils.exception import exceptionmsg, handle_exception
 
271
from gozerbot.utils.generic import uniqlist
 
272
from gozerbot.commands import cmnds
 
273
from gozerbot.examples import examples
 
274
from gozerbot.plugins import plugins
 
275
from gozerbot.plughelp import plughelp
 
276
from gozerbot.aliases import aliasdel, aliasset
 
277
from gozerbot.utils.dol import Dol
 
278
from gozerbot.datadir import datadir
 
279
from gozerbot.utils.fileutils import tarextract
 
280
import gozerbot
 
281
 
 
282
import re, urllib2, urlparse, os, thread, shutil
 
283
 
 
284
class Cfg(object):
 
285
 
 
286
    """ contains plugin version data """
 
287
    def __init__(self):
 
288
        self.data = {}
 
289
 
 
290
    def add(self, plugin, version):
 
291
        """ add plugin version """
 
292
        self.data[plugin] = version
 
293
        #self.save()
 
294
 
 
295
    def list(self):
 
296
        """ list plugin versions """
 
297
        return self.data
 
298
 
 
299
cfg = Cfg()
 
300
installlock = thread.allocate_lock()
 
301
locked = lockdec(installlock)
 
302
 
 
303
plughelp.add('install', 'install plugin from remote site')
 
304
 
 
305
installsites = ['http://gozerbot.org/gozerplugs', 
 
306
'http://tehmaze.com/plugs/gozerbot-0.9', 'http://plugs.trbs.net/0.9', \
 
307
'http://gozerplugs.blinkenlights.nl', 'http://gozerbot.org/catchall']
 
308
 
 
309
class InstallerException(Exception):
 
310
    pass
 
311
 
 
312
class Installer(object):
 
313
    '''
 
314
    We're taking care of installing and verifying plugins.
 
315
    '''
 
316
    
 
317
    BACKUP_PATTERN = '%s~'
 
318
    SUPPORTED_EXT = ['tar', 'py'] # supported extensions, in this order
 
319
 
 
320
    def install(self, plugname, site=''):
 
321
        '''
 
322
        Install `plugname` from `site`. If no `site` is specified, we scan the list of
 
323
        pre-configured `installsites`.
 
324
        '''
 
325
        # check if logs dir exists if not create it
 
326
        if not os.path.isdir('myplugs'):
 
327
            os.mkdir('myplugs')
 
328
            touch('myplugs/__init__.py') 
 
329
        errors = [] 
 
330
        if plugname.endswith('.py'):
 
331
            plugname = plugname[:3]
 
332
        plugname = plugname.split(os.sep)[-1]
 
333
        plugdata = ""
 
334
        if not site:
 
335
            for site in installsites:
 
336
                try:
 
337
                    plugdata, plugsig, plugtype = self.fetchplug(site, plugname)
 
338
                    break # no exception? found it!
 
339
                except InstallerException, e:
 
340
                    errors.append(str(e))
 
341
        else:
 
342
                try:
 
343
                    plugdata, plugsig, plugtype = self.fetchplug(site, plugname)
 
344
                except InstallerException, e:
 
345
                    errors.append(str(e))
 
346
        if not plugdata:
 
347
            if errors:
 
348
                raise InstallerException(', '.join(str(e) for e in errors))
 
349
            else:
 
350
                raise InstallerException('nothing to do')
 
351
        # still there? good
 
352
        if self.validate(plugdata, plugsig):
 
353
            if plugtype == 'py':
 
354
                self.save_py(plugname, plugdata)
 
355
            elif plugtype == 'tar':
 
356
                self.save_tar(plugname, plugdata)
 
357
            if hasattr(plugdata, 'info') and plugdata.info.has_key('last-modified'):
 
358
                cfg.add(plugname, ['%s/%s.%s' % (site, plugname, plugtype), plugdata.info['last-modified']])
 
359
            return '%s/%s.%s' % (site, plugname, plugtype)
 
360
        else:
 
361
            raise InstallerException('%s.%s signature validation failed' % (plugname, plugtype))
 
362
 
 
363
    def fetchplug(self, site, plugname):
 
364
        '''
 
365
        Fetch the plugin with `plugname` from `site`, we try any extension available in
 
366
        `Installer.SUPPORTED_EXT`. We also fetch the signature file.
 
367
        '''
 
368
        errors = []
 
369
        if not site.startswith('http://'):
 
370
            site = 'http://%s' % site
 
371
        base = '%s/%s' % (site, plugname)
 
372
        for ext in self.SUPPORTED_EXT:
 
373
            try:
 
374
                plugdata, plugsig = self.fetchplugdata(base, ext)
 
375
                return plugdata, plugsig, ext
 
376
            except InstallerException, e:
 
377
                errors.append(str(e))
 
378
        raise InstallerException(errors)
 
379
 
 
380
    def fetchplugdata(self, base, ext='py'):
 
381
        '''
 
382
        Here the actual HTTP request is being made.
 
383
        '''
 
384
        url = '%s.%s' % (base, ext)
 
385
        plug = base.split('/')[-1]
 
386
        try:
 
387
            plugdata = geturl2(url)
 
388
        except urllib2.HTTPError:
 
389
            raise InstallerException("no %s" % os.path.basename(url))
 
390
        if plugdata:
 
391
            try:
 
392
                plugsig = geturl2('%s.asc' % url)
 
393
            except urllib2.HTTPError:
 
394
                raise InstallerException('%s plugin has no signature' % plug)
 
395
            else:
 
396
                return plugdata, plugsig
 
397
 
 
398
    def backup(self, plugname):
 
399
        '''
 
400
        Backup a plugin with name `plugname` and return if we backed up a file or
 
401
        directory, and what its original name was.
 
402
        '''
 
403
        oldtype = None
 
404
        oldname = os.path.join('myplugs', plugname)
 
405
        newname = self.BACKUP_PATTERN % oldname
 
406
        if os.path.isfile('%s.py' % oldname):
 
407
            newname = self.BACKUP_PATTERN % '%s.py' % oldname
 
408
            oldname = '%s.py' % oldname
 
409
            oldtype = 'file'
 
410
            if os.path.isfile(newname):
 
411
                os.unlink(newname)
 
412
            os.rename(oldname, newname)
 
413
        elif os.path.isdir(oldname):
 
414
            oldtype = 'dir'
 
415
            if os.path.isdir(newname):
 
416
                shutil.rmtree(newname)
 
417
            os.rename(oldname, newname)
 
418
        return oldtype, newname
 
419
 
 
420
    def restore(self, plugname, oldtype, oldname):
 
421
        '''
 
422
        Restore a plugin with `plugname` from `oldname` with type `oldtype`.
 
423
        '''
 
424
        newname = os.path.join('myplugs', plugname)
 
425
        if oldtype == 'dir':
 
426
            # remove partial 'new' data
 
427
            if os.path.isdir(newname):
 
428
                shutils.rmtree(newname)
 
429
            os.rename(oldname, newname)
 
430
        elif oldtype == 'file':
 
431
            newname = '%s.py' % newname
 
432
            if os.path.isfile(newname):
 
433
                os.unlink(newname)
 
434
            os.rename(oldname, newname)
 
435
 
 
436
    @locked
 
437
    def save_tar(self, plugname, plugdata):
 
438
        oldtype, oldname = self.backup(plugname)
 
439
        try:
 
440
            if not tarextract(plugname, str(plugdata), 'myplugs'): # because it is an istr
 
441
                raise InstallerException('%s.tar had no (valid) files to extract' % (plugname))
 
442
        except InstallerException:
 
443
            raise
 
444
        except Exception, e:
 
445
            self.restore(plugname, oldtype, oldname)
 
446
            raise InstallerException('restored backup, error while extracting %s: %s' % (plugname, str(e))) 
 
447
 
 
448
    def save_py(self, plugname, plugdata):
 
449
        oldtype, oldname = self.backup(plugname)
 
450
        try:
 
451
            plugfile = open(os.path.join('myplugs', '%s.py' % plugname), 'w')
 
452
            plugfile.write(plugdata)
 
453
            plugfile.close()
 
454
        except InstallerException:
 
455
            raise
 
456
        except Exception, e:
 
457
            self.restore(plugname, oldtype, oldname)
 
458
            raise InstallerException('restored backup, error while writing %s: %s' % (plugfile, str(e))) 
 
459
 
 
460
    def validate(self, plugdata, plugsig):
 
461
        fingerprint = pgp.verify_signature(plugdata, plugsig)
 
462
        if not fingerprint:
 
463
            return False
 
464
        return True
 
465
 
 
466
def dolist():
 
467
    result = []
 
468
    errors = []
 
469
    for i in installsites:
 
470
        try:
 
471
            pluglist = geturl2(i)
 
472
        except Exception, ex:
 
473
            errors.append(i) 
 
474
            continue
 
475
        result += re.findall('<a href="(.*?)\.py">', pluglist)
 
476
        try:
 
477
            result.remove('__init__')
 
478
        except ValueError:
 
479
            pass
 
480
    if result:  
 
481
        result.sort()
 
482
        print 'available plugins: %s' % ' .. '.join(uniqlist(result))
 
483
    if errors:
 
484
        print "couldn't extract plugin list from: %s " % ' .. '.join(errors)
 
485
        
 
486
def installplug(plugs):
 
487
    """ install a remote plugin """
 
488
    notinstalled = []
 
489
    installed = []
 
490
    reloaded = []
 
491
    reloadfailed = []
 
492
    missing = []
 
493
    errors = {}
 
494
    reloaderrors = {}
 
495
    installer = Installer()
 
496
    for plug in plugs:
 
497
        ok = False
 
498
        url = ''
 
499
        plug = plug.split(os.sep)[-1]
 
500
        for site in installsites:
 
501
            try:
 
502
                url = installer.install(plug, site)
 
503
                if url:
 
504
                    print 'installed %s' % url
 
505
                    installed.append(plug)
 
506
                    errors = []
 
507
                    break # stop iterating sites
 
508
            except NoGPGException, ex:
 
509
                print "couldn't run gpg .. please install gnupg if you \
 
510
want to install remote plugins"
 
511
                return
 
512
            except Exception, ex:
 
513
                errors[site] = str(ex)
 
514
    if errors:
 
515
        errordict = Dol()
 
516
        errorlist = []
 
517
        for i, j in errors.iteritems():
 
518
            errordict.add(j, i)
 
519
        for error, sites in errordict.iteritems():
 
520
            errorlist.append("%s => %s" % (' , '.join(sites), error)) 
 
521
        print "errors: %s" % ' .. '.join(errors)
 
522
 
 
523
from optparse import OptionParser
 
524
parser = OptionParser(usage='usage: %prog [options]', version='%prog ' + gozerbot.__version__)
 
525
parser.add_option('-d', '--directory', type='string', default=".", dest='botdir',
 
526
                  metavar='PATH',
 
527
                  help="bot directory to install plugins in")
 
528
parser.add_option('-l', action='store_true', dest='showlist',
 
529
                  help="show list of available plugins")
 
530
opts, args = parser.parse_args()
 
531
opts.args = args
 
532
 
 
533
try:
 
534
    showlist = opts.showlist
 
535
except AttributeError:
 
536
    showlist = False
 
537
 
 
538
if showlist:
 
539
    dolist()
 
540
    os._exit(0)
 
541
 
 
542
try:
 
543
    botdir = opts.botdir
 
544
except AttributeError:
 
545
    botdir = '.'
 
546
 
 
547
if not opts.args:
 
548
    print "usage: gozerbot-install -l | [-d <botdir>] plug1 plug2"
 
549
    os._exit(1)
 
550
 
 
551
os.chdir(botdir)
 
552
installplug(opts.args)