Add error message when incorrect MAK is entered
[pyTivo/wgw.git] / plugins / admin / admin.py
blobaca350d87171b07b1c9b387233faf9235a1011c5
1 import os, socket, re, sys, ConfigParser, config, time
2 import urllib2, cookielib, thread
3 from xml.dom import minidom
4 from ConfigParser import NoOptionError
5 from Cheetah.Template import Template
6 from plugin import Plugin
7 from urllib import unquote_plus, quote, unquote
8 from urlparse import urlparse
9 from xml.sax.saxutils import escape
10 from lrucache import LRUCache
12 SCRIPTDIR = os.path.dirname(__file__)
14 CLASS_NAME = 'Admin'
16 p = os.path.dirname(__file__)
17 p = p.split(os.path.sep)
18 p.pop()
19 p.pop()
20 p = os.path.sep.join(p)
21 config_file_path = os.path.join(p, 'pyTivo.conf')
23 status = {} #Global variable to control download threads
24 tivo_cache = {} #Cache of TiVo NPL
26 class Admin(Plugin):
27 CONTENT_TYPE = 'text/html'
29 def Reset(self, handler, query):
30 config.reset()
31 handler.server.reset()
32 if 'last_page' in query:
33 last_page = query['last_page'][0]
34 else:
35 last_page = 'Admin'
37 subcname = query['Container'][0]
38 cname = subcname.split('/')[0]
39 handler.send_response(200)
40 handler.end_headers()
41 t = Template(file=os.path.join(SCRIPTDIR,'templates', 'redirect.tmpl'))
42 t.container = cname
43 t.time = '3'
44 t.url = '/TiVoConnect?Command='+ last_page +'&Container=' + cname
45 t.text = '<h3>The pyTivo Server has been soft reset.</h3> <br>pyTivo has reloaded the pyTivo.conf'+\
46 'file and all changed should now be in effect. <br> The'+ \
47 '<a href="/TiVoConnect?Command='+ last_page +'&Container='+ cname +'"> previous</a> page will reload in 3 seconds.'
48 handler.wfile.write(t)
50 def Admin(self, handler, query):
51 #Read config file new each time in case there was any outside edits
52 config = ConfigParser.ConfigParser()
53 config.read(config_file_path)
55 shares_data = []
56 for section in config.sections():
57 if not(section.startswith('_tivo_') or section.startswith('Server')):
58 if not(config.has_option(section,'type')):
59 shares_data.append((section, dict(config.items(section, raw=True))))
60 elif config.get(section,'type').lower() != 'admin':
61 shares_data.append((section, dict(config.items(section, raw=True))))
63 subcname = query['Container'][0]
64 cname = subcname.split('/')[0]
65 handler.send_response(200)
66 handler.end_headers()
67 t = Template(file=os.path.join(SCRIPTDIR,'templates', 'settings.tmpl'))
68 t.container = cname
69 t.server_data = dict(config.items('Server', raw=True))
70 t.server_known = ["port", "guid", "ffmpeg", "beacon", "hack83", "debug", \
71 "optres", "audio_br", "video_br", "max_video_br", "width",\
72 "height", "ffmpeg_prams", "bufsize", "precache"]
73 t.shares_data = shares_data
74 t.shares_known = ["type", "path", "auto_subshares"]
75 t.tivos_data = [ (section, dict(config.items(section, raw=True))) for section in config.sections() \
76 if section.startswith('_tivo_')]
77 t.tivos_known = ["aspect169", "audio_br", "video_br", "width", "height", "ffmpeg_prams", "shares"]
78 handler.wfile.write(t)
80 def UpdateSettings(self, handler, query):
81 config = ConfigParser.ConfigParser()
82 config.read(config_file_path)
83 for key in query:
84 if key.startswith('Server.'):
85 section, option = key.split('.')
86 if option == "new__setting":
87 new_setting = query[key][0]
88 continue
89 if option == "new__value":
90 new_value = query[key][0]
91 continue
92 if query[key][0] == " ":
93 config.remove_option(section, option)
94 else:
95 config.set(section, option, query[key][0])
96 if not(new_setting == ' ' and new_value == ' '):
97 config.set('Server', new_setting, new_value)
99 sections = query['Section_Map'][0].split(']')
100 sections.pop() #last item is junk
101 for section in sections:
102 ID, name = section.split('|')
103 if query[ID][0] == "Delete_Me":
104 config.remove_section(name)
105 continue
106 if query[ID][0] != name:
107 config.remove_section(name)
108 config.add_section(query[ID][0])
109 for key in query:
110 if key.startswith(ID + '.'):
111 junk, option = key.split('.')
112 if option == "new__setting":
113 new_setting = query[key][0]
114 continue
115 if option == "new__value":
116 new_value = query[key][0]
117 continue
118 if query[key][0] == " ":
119 config.remove_option(query[ID][0], option)
120 else:
121 config.set(query[ID][0], option, query[key][0])
122 if not(new_setting == ' ' and new_value == ' '):
123 config.set(query[ID][0], new_setting, new_value)
124 if query['new_Section'][0] != " ":
125 config.add_section(query['new_Section'][0])
126 f = open(config_file_path, "w")
127 config.write(f)
128 f.close()
130 subcname = query['Container'][0]
131 cname = subcname.split('/')[0]
132 handler.send_response(200)
133 handler.end_headers()
134 t = Template(file=os.path.join(SCRIPTDIR,'templates', 'redirect.tmpl'))
135 t.container = cname
136 t.time = '10'
137 t.url = '/TiVoConnect?Command=Admin&Container=' + cname
138 t.text = '<h3>Your Settings have been saved.</h3> <br>You settings have been saved to the pyTivo.conf file.'+\
139 'However you will need to do a <b>Soft Reset</b> before these changes will take effect.'+\
140 '<br> The <a href="/TiVoConnect?Command=Admin&Container='+ cname +'"> Admin</a> page will reload in 10 seconds.'
141 handler.wfile.write(t)
143 def NPL(self, handler, query):
144 subcname = query['Container'][0]
145 cname = subcname.split('/')[0]
146 folder = ''
147 for name, data in config.getShares():
148 if cname == name:
149 if 'tivo_mak' in data:
150 tivo_mak = data['tivo_mak']
151 else:
152 tivo_mak = ""
153 if 'togo_path' in data:
154 togo_path = data['togo_path']
155 else:
156 togo_path = ""
158 if 'TiVo' in query:
159 tivoIP = query['TiVo'][0]
160 theurl = 'https://'+ tivoIP +'/TiVoConnect?Command=QueryContainer&Container=/NowPlaying'
161 if 'Folder' in query:
162 folder += str(query['Folder'][0])
163 theurl += '/' + folder
165 password = tivo_mak #TiVo MAK
167 r=urllib2.Request(theurl)
168 auth_handler = urllib2.HTTPDigestAuthHandler()
169 auth_handler.add_password('TiVo DVR', tivoIP, 'tivo', password)
170 opener = urllib2.build_opener(auth_handler)
171 urllib2.install_opener(opener)
173 if theurl in tivo_cache: #check to see if we have accessed this page before
174 if tivo_cache[theurl]['thepage'] == '' or (time.time() - tivo_cache[theurl]['thepage_time']) >= 60: #if page is empty or old then retreive it
175 try:
176 handle = urllib2.urlopen(r)
177 except IOError, e:
178 handler.send_response(200)
179 handler.end_headers()
180 t = Template(file=os.path.join(SCRIPTDIR,'templates', 'redirect.tmpl'))
181 t.container = cname
182 t.time = '20'
183 t.url = '/TiVoConnect?Command=NPL&Container=' + cname
184 t.text = '<h3>Unable to Connect to TiVo.</h3> <br>pyTivo was unable to connect to the TiVo at ' + tivoIP +\
185 '<br>This most likely caused by an incorrect Media Access Key. Please return to the ToGo page and double check your Media Access Key.' +\
186 '<br> The <a href="/TiVoConnect?Command=NPL&Container='+ cname + '"> ToGo</a> page will reload in 20 seconds.'
187 handler.wfile.write(t)
188 return
189 tivo_cache[theurl]['thepage'] = handle.read()
190 tivo_cache[theurl]['thepage_time'] = time.time()
191 else: #not in cache
192 try:
193 handle = urllib2.urlopen(r)
194 except IOError, e:
195 handler.send_response(200)
196 handler.end_headers()
197 t = Template(file=os.path.join(SCRIPTDIR,'templates', 'redirect.tmpl'))
198 t.container = cname
199 t.time = '20'
200 t.url = '/TiVoConnect?Command=NPL&Container=' + cname
201 t.text = '<h3>Unable to Connect to TiVo.</h3> <br>pyTivo was unable to connect to the TiVo at ' + tivoIP +\
202 '<br>This most likely caused by an incorrect Media Access Key. Please return to the ToGo page and double check your Media Access Key.' +\
203 '<br> The <a href="/TiVoConnect?Command=NPL&Container='+ cname + '"> ToGo</a> page will reload in 20 seconds.'
204 handler.wfile.write(t)
205 return
206 tivo_cache[theurl] = {}
207 tivo_cache[theurl]['thepage'] = handle.read()
208 tivo_cache[theurl]['thepage_time'] = time.time()
210 xmldoc = minidom.parseString(tivo_cache[theurl]['thepage'])
211 items = xmldoc.getElementsByTagName('Item')
213 data = []
214 for item in items:
215 entry = {}
216 entry['Title'] = item.getElementsByTagName("Title")[0].firstChild.data
217 entry['ContentType'] = item.getElementsByTagName("ContentType")[0].firstChild.data
218 if (len(item.getElementsByTagName("UniqueId")) >= 1):
219 entry['UniqueId'] = item.getElementsByTagName("UniqueId")[0].firstChild.data
220 if entry['ContentType'] == 'x-tivo-container/folder':
221 entry['TotalItems'] = item.getElementsByTagName("TotalItems")[0].firstChild.data
222 entry['LastChangeDate'] = item.getElementsByTagName("LastChangeDate")[0].firstChild.data
223 entry['LastChangeDate'] = time.strftime("%b %d, %Y", time.localtime(int(entry['LastChangeDate'], 16)))
224 else:
225 link = item.getElementsByTagName("Links")[0]
226 if (len(link.getElementsByTagName("CustomIcon")) >= 1):
227 entry['Icon'] = link.getElementsByTagName("CustomIcon")[0].getElementsByTagName("Url")[0].firstChild.data
228 if (len(link.getElementsByTagName("Content")) >= 1):
229 entry['Url'] = link.getElementsByTagName("Content")[0].getElementsByTagName("Url")[0].firstChild.data
230 parse_url = urlparse(entry['Url'])
231 entry['Url'] = quote('http://' + parse_url[1].split(':')[0] + parse_url[2] + "?" + parse_url[4])
232 keys = ['SourceSize', 'Duration', 'CaptureDate', 'EpisodeTitle', 'Description', 'SourceChannel', 'SourceStation']
233 for key in keys:
234 try:
235 entry[key] = item.getElementsByTagName(key)[0].firstChild.data
236 except:
237 entry[key] = ''
238 entry['SourceSize'] = "%.3f GB" % float(float(entry['SourceSize'])/(1024*1024*1024))
239 entry['Duration'] = str(int(entry['Duration'])/(60*60*1000)).zfill(2) + ':' \
240 + str((int(entry['Duration'])%(60*60*1000))/(60*1000)).zfill(2) + ':' \
241 + str((int(entry['Duration'])/1000)%60).zfill(2)
242 entry['CaptureDate'] = time.strftime("%b %d, %Y", time.localtime(int(entry['CaptureDate'], 16)))
244 data.append(entry)
245 else:
246 data = []
247 tivoIP = ''
249 subcname = query['Container'][0]
250 cname = subcname.split('/')[0]
251 handler.send_response(200)
252 handler.end_headers()
253 t = Template(file=os.path.join(SCRIPTDIR,'templates', 'npl.tmpl'))
254 t.folder = folder
255 t.status = status
256 t.tivo_mak = tivo_mak
257 t.togo_path = togo_path
258 t.tivos = handler.tivos
259 t.tivoIP = tivoIP
260 t.container = cname
261 t.data = data
262 t.unquote = unquote
263 t.len = len
264 handler.wfile.write(t)
266 def get_tivo_file(self, url, mak, tivoIP, outfile):
267 #global status
268 cj = cookielib.LWPCookieJar()
270 r=urllib2.Request(url)
271 auth_handler = urllib2.HTTPDigestAuthHandler()
272 auth_handler.add_password('TiVo DVR', tivoIP, 'tivo', mak)
273 opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj), auth_handler)
274 urllib2.install_opener(opener)
276 try:
277 handle = urllib2.urlopen(r)
278 except IOError, e:
279 #If we get "Too many transfers error" try a second time. For some reason
280 #urllib2 does not properly close connections when a transfer is canceled.
281 if e.code == 503:
282 try:
283 handle = urllib2.urlopen(r)
284 except IOError, e:
285 status[url]['running'] = False
286 status[url]['error'] = e.code
287 return
288 else:
289 status[url]['running'] = False
290 status[url]['error'] = e.code
291 return
293 f = open(outfile, 'wb')
294 kilobytes = 0
295 start_time = time.time()
296 output = handle.read(1024)
297 while status[url]['running'] and output != '':
298 kilobytes += 1
299 f.write(output)
300 if ((time.time() - start_time) >= 5):
301 status[url]['rate'] = int(kilobytes/(time.time() - start_time))
302 kilobytes = 0
303 start_time = time.time()
304 output = handle.read(1024)
305 status[url]['running'] = False
306 handle.close()
307 f.close()
308 return
310 def ToGo(self, handler, query):
311 subcname = query['Container'][0]
312 cname = subcname.split('/')[0]
313 for name, data in config.getShares():
314 if cname == name:
315 if 'tivo_mak' in data:
316 tivo_mak = data['tivo_mak']
317 else:
318 tivo_mak = ""
319 if 'togo_path' in data:
320 togo_path = data['togo_path']
321 else:
322 togo_path = ""
323 if tivo_mak != "" and togo_path != "":
324 parse_url = urlparse(str(query['Url'][0]))
325 theurl = 'http://' + parse_url[1].split(':')[0] + parse_url[2] + "?" + parse_url[4]
326 password = tivo_mak #TiVo MAK
327 tivoIP = query['TiVo'][0]
328 name = unquote(parse_url[2])[10:300].split('.')
329 name.insert(-1," - " + unquote(parse_url[4]).split("id=")[1] + ".")
330 outfile = os.path.join(togo_path, "".join(name))
332 status[theurl] = {'running':True, 'error':'', 'rate':'', 'finished':False}
334 thread.start_new_thread(Admin.get_tivo_file, (self, theurl, password, tivoIP, outfile))
336 handler.send_response(200)
337 handler.end_headers()
338 t = Template(file=os.path.join(SCRIPTDIR,'templates', 'redirect.tmpl'))
339 t.container = cname
340 t.time = '3'
341 t.url = '/TiVoConnect?Command=NPL&Container=' + cname + '&TiVo=' + query['TiVo'][0] + '&Folder=' + query['Folder'][0]
342 t.text = '<h3>Transfer Initiated.</h3> <br>You selected transfer has been initiated.'+\
343 '<br> The <a href="/TiVoConnect?Command=NPL&Container='+ cname + '&TiVo=' + query['TiVo'][0] + '&Folder=' + query['Folder'][0] +'"> ToGo</a> page will reload in 3 seconds.'
344 handler.wfile.write(t)
345 else:
346 handler.send_response(200)
347 handler.end_headers()
348 t = Template(file=os.path.join(SCRIPTDIR,'templates', 'redirect.tmpl'))
349 t.container = cname
350 t.time = '10'
351 t.url = '/TiVoConnect?Command=NPL&Container=' + cname + '&TiVo=' + query['TiVo'][0] + '&Folder=' + query['Folder'][0]
352 t.text = '<h3>Missing Data.</h3> <br>You must set both "tivo_mak" and "togo_path" before using this function.'+\
353 '<br> The <a href="/TiVoConnect?Command=NPL&Container='+ cname + '&TiVo=' + query['TiVo'][0] + '&Folder=' + query['Folder'][0] +'"> ToGo</a> page will reload in 10 seconds.'
354 handler.wfile.write(t)
356 def ToGoStop(self, handler, query):
357 parse_url = urlparse(str(query['Url'][0]))
358 theurl = 'http://' + parse_url[1].split(':')[0] + parse_url[2] + "?" + parse_url[4]
360 status[theurl]['running'] = False
362 subcname = query['Container'][0]
363 cname = subcname.split('/')[0]
364 handler.send_response(200)
365 handler.end_headers()
366 t = Template(file=os.path.join(SCRIPTDIR,'templates', 'redirect.tmpl'))
367 t.container = cname
368 t.time = '3'
369 t.url = '/TiVoConnect?Command=NPL&Container=' + cname + '&TiVo=' + query['TiVo'][0] + '&Folder=' + query['Folder'][0]
370 t.text = '<h3>Transfer Stopped.</h3> <br>Your transfer has been stopped.'+\
371 '<br> The <a href="/TiVoConnect?Command=NPL&Container='+ cname + '&TiVo=' + query['TiVo'][0] + '&Folder=' + query['Folder'][0] +'"> ToGo</a> page will reload in 3 seconds.'
372 handler.wfile.write(t)
375 def SaveNPL(self, handler, query):
376 config = ConfigParser.ConfigParser()
377 config.read(config_file_path)
378 if 'tivo_mak' in query:
379 config.set(query['Container'][0], 'tivo_mak', query['tivo_mak'][0])
380 if 'togo_path' in query:
381 config.set(query['Container'][0], 'togo_path', query['togo_path'][0])
382 f = open(config_file_path, "w")
383 config.write(f)
384 f.close()
386 subcname = query['Container'][0]
387 cname = subcname.split('/')[0]
388 handler.send_response(200)
389 handler.end_headers()
390 t = Template(file=os.path.join(SCRIPTDIR,'templates', 'redirect.tmpl'))
391 t.container = cname
392 t.time = '2'
393 t.url = '/TiVoConnect?last_page=NPL&Command=Reset&Container=' + cname
394 t.text = '<h3>Your Settings have been saved.</h3> <br>You settings have been saved to the pyTivo.conf file.'+\
395 'pyTivo will now do a <b>Soft Reset</b> to allow these changes to take effect.'+\
396 '<br> The <a href="/TiVoConnect?last_page=NPL&Command=Reset&Container='+ cname +'"> Reset</a> will occur in 2 seconds.'
397 handler.wfile.write(t)