Subversion Repositories svnkaklik

Rev

Details | Last modification | View Log

Rev Author Line No. Line
36 kaklik 1
#written by John Hoffman
2
 
3
from inifile import ini_write, ini_read
4
from bencode import bencode, bdecode
5
from types import IntType, LongType, StringType, FloatType
6
from CreateIcons import GetIcons, CreateIcon
7
from parseargs import defaultargs
8
from __init__ import product_name, version_short
9
import sys,os
10
from time import time, strftime
11
 
12
try:
13
    True
14
except:
15
    True = 1
16
    False = 0
17
 
18
try:
19
    realpath = os.path.realpath
20
except:
21
    realpath = lambda x:x
22
OLDICONPATH = os.path.abspath(os.path.dirname(realpath(sys.argv[0])))
23
 
24
DIRNAME = '.'+product_name
25
 
26
hexchars = '0123456789abcdef'
27
hexmap = []
28
revmap = {}
29
for i in xrange(256):
30
    x = hexchars[(i&0xF0)/16]+hexchars[i&0x0F]
31
    hexmap.append(x)
32
    revmap[x] = chr(i)
33
 
34
def tohex(s):
35
    r = []
36
    for c in s:
37
        r.append(hexmap[ord(c)])
38
    return ''.join(r)
39
 
40
def unhex(s):
41
    r = [ revmap[s[x:x+2]] for x in xrange(0, len(s), 2) ]
42
    return ''.join(r)
43
 
44
def copyfile(oldpath, newpath): # simple file copy, all in RAM
45
    try:
46
        f = open(oldpath,'rb')
47
        r = f.read()
48
        success = True
49
    except:
50
        success = False
51
    try:
52
        f.close()
53
    except:
54
        pass
55
    if not success:
56
        return False
57
    try:
58
        f = open(newpath,'wb')
59
        f.write(r)
60
    except:
61
        success = False
62
    try:
63
        f.close()
64
    except:
65
        pass
66
    return success
67
 
68
 
69
class ConfigDir:
70
 
71
    ###### INITIALIZATION TASKS ######
72
 
73
    def __init__(self, config_type = None):
74
        self.config_type = config_type
75
        if config_type:
76
            config_ext = '.'+config_type
77
        else:
78
            config_ext = ''
79
 
80
        def check_sysvars(x):
81
            y = os.path.expandvars(x)
82
            if y != x and os.path.isdir(y):
83
                return y
84
            return None
85
 
86
        for d in ['${APPDATA}', '${HOME}', '${HOMEPATH}', '${USERPROFILE}']:
87
            dir_root = check_sysvars(d)
88
            if dir_root:
89
                break
90
        else:
91
            dir_root = os.path.expanduser('~')
92
            if not os.path.isdir(dir_root):
93
                dir_root = os.path.abspath(os.path.dirname(sys.argv[0]))
94
 
95
        dir_root = os.path.join(dir_root,DIRNAME)
96
        self.dir_root = dir_root
97
 
98
        if not os.path.isdir(self.dir_root):
99
            os.mkdir(self.dir_root,0700)    # exception if failed
100
 
101
        self.dir_icons = os.path.join(dir_root,'icons')
102
        if not os.path.isdir(self.dir_icons):
103
            os.mkdir(self.dir_icons)
104
        for icon in GetIcons():
105
            i = os.path.join(self.dir_icons,icon)
106
            if not os.path.exists(i):
107
                if not copyfile(os.path.join(OLDICONPATH,icon),i):
108
                    CreateIcon(icon,self.dir_icons)
109
 
110
        self.dir_torrentcache = os.path.join(dir_root,'torrentcache')
111
        if not os.path.isdir(self.dir_torrentcache):
112
            os.mkdir(self.dir_torrentcache)
113
 
114
        self.dir_datacache = os.path.join(dir_root,'datacache')
115
        if not os.path.isdir(self.dir_datacache):
116
            os.mkdir(self.dir_datacache)
117
 
118
        self.dir_piececache = os.path.join(dir_root,'piececache')
119
        if not os.path.isdir(self.dir_piececache):
120
            os.mkdir(self.dir_piececache)
121
 
122
        self.configfile = os.path.join(dir_root,'config'+config_ext+'.ini')
123
        self.statefile = os.path.join(dir_root,'state'+config_ext)
124
 
125
        self.TorrentDataBuffer = {}
126
 
127
 
128
    ###### CONFIG HANDLING ######
129
 
130
    def setDefaults(self, defaults, ignore=[]):
131
        self.config = defaultargs(defaults)
132
        for k in ignore:
133
            if self.config.has_key(k):
134
                del self.config[k]
135
 
136
    def checkConfig(self):
137
        return os.path.exists(self.configfile)
138
 
139
    def loadConfig(self):
140
        try:
141
            r = ini_read(self.configfile)['']
142
        except:
143
            return self.config
144
        l = self.config.keys()
145
        for k,v in r.items():
146
            if self.config.has_key(k):
147
                t = type(self.config[k])
148
                try:
149
                    if t == StringType:
150
                        self.config[k] = v
151
                    elif t == IntType or t == LongType:
152
                        self.config[k] = long(v)
153
                    elif t == FloatType:
154
                        self.config[k] = float(v)
155
                    l.remove(k)
156
                except:
157
                    pass
158
        if l: # new default values since last save
159
            self.saveConfig()
160
        return self.config
161
 
162
    def saveConfig(self, new_config = None):
163
        if new_config:
164
            for k,v in new_config.items():
165
                if self.config.has_key(k):
166
                    self.config[k] = v
167
        try:
168
            ini_write( self.configfile, self.config,
169
                       'Generated by '+product_name+'/'+version_short+'\n'
170
                       + strftime('%x %X') )
171
            return True
172
        except:
173
            return False
174
 
175
    def getConfig(self):
176
        return self.config
177
 
178
 
179
    ###### STATE HANDLING ######
180
 
181
    def getState(self):
182
        try:
183
            f = open(self.statefile,'rb')
184
            r = f.read()
185
        except:
186
            r = None
187
        try:
188
            f.close()
189
        except:
190
            pass
191
        try:
192
            r = bdecode(r)
193
        except:
194
            r = None
195
        return r        
196
 
197
    def saveState(self, state):
198
        try:
199
            f = open(self.statefile,'wb')
200
            f.write(bencode(state))
201
            success = True
202
        except:
203
            success = False
204
        try:
205
            f.close()
206
        except:
207
            pass
208
        return success
209
 
210
 
211
    ###### TORRENT HANDLING ######
212
 
213
    def getTorrents(self):
214
        d = {}
215
        for f in os.listdir(self.dir_torrentcache):
216
            f = os.path.basename(f)
217
            try:
218
                f, garbage = f.split('.')
219
            except:
220
                pass
221
            d[unhex(f)] = 1
222
        return d.keys()
223
 
224
    def getTorrentVariations(self, t):
225
        t = tohex(t)
226
        d = []
227
        for f in os.listdir(self.dir_torrentcache):
228
            f = os.path.basename(f)
229
            if f[:len(t)] == t:
230
                try:
231
                    garbage, ver = f.split('.')
232
                except:
233
                    ver = '0'
234
                d.append(int(ver))
235
        d.sort()
236
        return d
237
 
238
    def getTorrent(self, t, v = -1):
239
        t = tohex(t)
240
        if v == -1:
241
            v = max(self.getTorrentVariations(t))   # potential exception
242
        if v:
243
            t += '.'+str(v)
244
        try:
245
            f = open(os.path.join(self.dir_torrentcache,t),'rb')
246
            r = bdecode(f.read())
247
        except:
248
            r = None
249
        try:
250
            f.close()
251
        except:
252
            pass
253
        return r
254
 
255
    def writeTorrent(self, data, t, v = -1):
256
        t = tohex(t)
257
        if v == -1:
258
            try:
259
                v = max(self.getTorrentVariations(t))+1
260
            except:
261
                v = 0
262
        if v:
263
            t += '.'+str(v)
264
        try:
265
            f = open(os.path.join(self.dir_torrentcache,t),'wb')
266
            f.write(bencode(data))
267
        except:
268
            v = None
269
        try:
270
            f.close()
271
        except:
272
            pass
273
        return v
274
 
275
 
276
    ###### TORRENT DATA HANDLING ######
277
 
278
    def getTorrentData(self, t):
279
        if self.TorrentDataBuffer.has_key(t):
280
            return self.TorrentDataBuffer[t]
281
        t = os.path.join(self.dir_datacache,tohex(t))
282
        if not os.path.exists(t):
283
            return None
284
        try:
285
            f = open(t,'rb')
286
            r = bdecode(f.read())
287
        except:
288
            r = None
289
        try:
290
            f.close()
291
        except:
292
            pass
293
        self.TorrentDataBuffer[t] = r
294
        return r
295
 
296
    def writeTorrentData(self, t, data):
297
        self.TorrentDataBuffer[t] = data
298
        try:
299
            f = open(os.path.join(self.dir_datacache,tohex(t)),'wb')
300
            f.write(bencode(data))
301
            success = True
302
        except:
303
            success = False
304
        try:
305
            f.close()
306
        except:
307
            pass
308
        if not success:
309
            self.deleteTorrentData(t)
310
        return success
311
 
312
    def deleteTorrentData(self, t):
313
        try:
314
            os.remove(os.path.join(self.dir_datacache,tohex(t)))
315
        except:
316
            pass
317
 
318
    def getPieceDir(self, t):
319
        return os.path.join(self.dir_piececache,tohex(t))
320
 
321
 
322
    ###### EXPIRATION HANDLING ######
323
 
324
    def deleteOldCacheData(self, days, still_active = [], delete_torrents = False):
325
        if not days:
326
            return
327
        exptime = time() - (days*24*3600)
328
        names = {}
329
        times = {}
330
 
331
        for f in os.listdir(self.dir_torrentcache):
332
            p = os.path.join(self.dir_torrentcache,f)
333
            f = os.path.basename(f)
334
            try:
335
                f, garbage = f.split('.')
336
            except:
337
                pass
338
            try:
339
                f = unhex(f)
340
                assert len(f) == 20
341
            except:
342
                continue
343
            if delete_torrents:
344
                names.setdefault(f,[]).append(p)
345
            try:
346
                t = os.path.getmtime(p)
347
            except:
348
                t = time()
349
            times.setdefault(f,[]).append(t)
350
 
351
        for f in os.listdir(self.dir_datacache):
352
            p = os.path.join(self.dir_datacache,f)
353
            try:
354
                f = unhex(os.path.basename(f))
355
                assert len(f) == 20
356
            except:
357
                continue
358
            names.setdefault(f,[]).append(p)
359
            try:
360
                t = os.path.getmtime(p)
361
            except:
362
                t = time()
363
            times.setdefault(f,[]).append(t)
364
 
365
        for f in os.listdir(self.dir_piececache):
366
            p = os.path.join(self.dir_piececache,f)
367
            try:
368
                f = unhex(os.path.basename(f))
369
                assert len(f) == 20
370
            except:
371
                continue
372
            for f2 in os.listdir(p):
373
                p2 = os.path.join(p,f2)
374
                names.setdefault(f,[]).append(p2)
375
                try:
376
                    t = os.path.getmtime(p2)
377
                except:
378
                    t = time()
379
                times.setdefault(f,[]).append(t)
380
            names.setdefault(f,[]).append(p)
381
 
382
        for k,v in times.items():
383
            if max(v) < exptime and not k in still_active:
384
                for f in names[k]:
385
                    try:
386
                        os.remove(f)
387
                    except:
388
                        try:
389
                            os.removedirs(f)
390
                        except:
391
                            pass
392
 
393
 
394
    def deleteOldTorrents(self, days, still_active = []):
395
        self.deleteOldCacheData(days, still_active, True)
396
 
397
 
398
    ###### OTHER ######
399
 
400
    def getIconDir(self):
401
        return self.dir_icons