]>
Commit | Line | Data |
---|---|---|
879aa787 MT |
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 |