ParentalControl: Simplify some code
[openblackhole/openblackhole-enigma2.git] / lib / python / Components / ParentalControl.py
1 from Components.config import config, ConfigSubsection, ConfigSelection, ConfigPIN, ConfigText, ConfigYesNo, ConfigSubList, ConfigInteger
2 from Components.ServiceList import refreshServiceList
3 #from Screens.ChannelSelection import service_types_tv
4 from Screens.InputBox import PinInput
5 from Screens.MessageBox import MessageBox
6 from Tools.BoundFunction import boundFunction
7 from ServiceReference import ServiceReference
8 from Tools import Notifications
9 from Tools.Directories import resolveFilename, SCOPE_CONFIG
10 from Tools.Notifications import AddPopup
11 from enigma import eTimer, eServiceCenter, iServiceInformation, eServiceReference, eDVBDB
12 import time, os
13
14 TYPE_SERVICE = "SERVICE"
15 TYPE_BOUQUETSERVICE = "BOUQUETSERVICE"
16 TYPE_BOUQUET = "BOUQUET"
17 LIST_BLACKLIST = "blacklist"
18
19 def InitParentalControl():
20         config.ParentalControl = ConfigSubsection()
21         config.ParentalControl.storeservicepin = ConfigSelection(default = "never", choices = [("never", _("never")), ("5", _("%d minutes") % 5), ("30", _("%d minutes") % 30), ("60", _("%d minutes") % 60), ("standby", _("until standby/restart"))])
22         config.ParentalControl.configured = ConfigYesNo(default = False)
23         config.ParentalControl.setuppinactive = ConfigYesNo(default = False)
24         config.ParentalControl.retries = ConfigSubsection()
25         config.ParentalControl.retries.servicepin = ConfigSubsection()
26         config.ParentalControl.retries.servicepin.tries = ConfigInteger(default = 3)
27         config.ParentalControl.retries.servicepin.time = ConfigInteger(default = 3)
28         config.ParentalControl.servicepin = ConfigSubList()
29         config.ParentalControl.servicepin.append(ConfigPIN(default = 0))
30         config.ParentalControl.age = ConfigSelection(default = "18", choices = [("0", _("No age block"))] + list((str(x), "%d+" % x) for x in range(3,19)))
31         config.ParentalControl.hideBlacklist = ConfigYesNo(default = False)
32         config.ParentalControl.config_sections = ConfigSubsection()
33         config.ParentalControl.config_sections.main_menu = ConfigYesNo(default = False)
34         config.ParentalControl.config_sections.configuration = ConfigYesNo(default = False)
35         config.ParentalControl.config_sections.timer_menu = ConfigYesNo(default = False)
36         config.ParentalControl.config_sections.plugin_browser = ConfigYesNo(default = False)
37         config.ParentalControl.config_sections.standby_menu = ConfigYesNo(default = False)
38         config.ParentalControl.config_sections.movie_list = ConfigYesNo(default = False)
39         config.ParentalControl.config_sections.context_menus = ConfigYesNo(default = False)
40
41         #Added for backwards compatibility with some 3rd party plugins that depend on this config
42         config.ParentalControl.servicepinactive = config.ParentalControl.configured
43         config.ParentalControl.setuppin = config.ParentalControl.servicepin[0]
44         config.ParentalControl.retries.setuppin = config.ParentalControl.retries.servicepin
45         config.ParentalControl.type = ConfigSelection(default = "blacklist", choices = [(LIST_BLACKLIST, _("blacklist"))])
46
47         global parentalControl
48         parentalControl = ParentalControl()
49
50 class ParentalControl:
51         def __init__(self):
52                 #Do not call open on init, because bouquets are not ready at that moment
53                 self.filesOpened = False
54                 self.PinDlg = None
55                 #This is the timer that is used to see, if the time for caching the pin is over
56                 #Of course we could also work without a timer and compare the times every
57                 #time we call isServicePlayable. But this might probably slow down zapping,
58                 #That's why I decided to use a timer
59                 self.sessionPinTimer = eTimer()
60                 self.sessionPinTimer.callback.append(self.resetSessionPin)
61                 self.getConfigValues()
62
63         def serviceMethodWrapper(self, service, method, *args):
64                 #This method is used to call all functions that need a service as Parameter:
65                 #It takes either a Service- Reference or a Bouquet- Reference and passes
66                 #Either the service or all services contained in the bouquet to the method given
67                 #That way all other functions do not need to distinguish between service and bouquet.
68                 if "FROM BOUQUET" in service:
69                         method( service , TYPE_BOUQUET , *args )
70                         servicelist = self.readServicesFromBouquet(service,"C")
71                         for ref in servicelist:
72                                 sRef = str(ref[0])
73                                 method( sRef , TYPE_BOUQUETSERVICE , *args )
74                 else:
75                         ref = ServiceReference(service)
76                         sRef = str(ref)
77                         method( sRef , TYPE_SERVICE , *args )
78
79         def isProtected(self, ref):
80                 if not config.ParentalControl.servicepinactive.value:
81                         return False
82                 #Check if configuration has already been read or if the significant values have changed.
83                 #If true: read the configuration
84                 if self.storeServicePin != config.ParentalControl.storeservicepin.value:
85                         self.getConfigValues()
86                 service = ref.toCompareString()
87                 path = ref.getPath()
88                 info = eServiceCenter.getInstance().info(ref)
89                 age = 0
90                 if path.startswith("/"):
91                         if service.startswith("1:"):
92                                 refstr = info and info.getInfoString(ref, iServiceInformation.sServiceref)
93                                 service = refstr and eServiceReference(refstr).toCompareString()
94                         if os.path.basename(path).startswith("."):
95                                 age = 18
96                 elif int(config.ParentalControl.age.value):
97                         event = info and info.getEvent(ref)
98                         rating = event and event.getParentalData()
99                         age = rating and rating.getRating()
100                         age = age and age <= 15 and age + 3 or 0
101                 return (age and age >= int(config.ParentalControl.age.value)) or service and self.blacklist.has_key(service)
102
103         def isServicePlayable(self, ref, callback, session=None):
104                 self.session = session
105                 if self.isProtected(ref):
106                         #Check if the session pin is cached
107                         if self.sessionPinCached == True:
108                                 return True
109                         self.callback = callback
110                         service = ref.toCompareString()
111                         title = 'FROM BOUQUET "userbouquet.' in service and _("this bouquet is protected by a parental control pin") or _("this service is protected by a parental control pin")
112                         if session:
113                                 Notifications.RemovePopup("Parental control")
114                                 if self.PinDlg:
115                                         self.PinDlg.close()
116                                 self.PinDlg = session.openWithCallback(boundFunction(self.servicePinEntered, ref), PinInput, triesEntry=config.ParentalControl.retries.servicepin, pinList=self.getPinList(), service=ServiceReference(ref).getServiceName(), title=title, windowTitle=_("Parental control"), simple=False)
117                         else:
118                                 Notifications.AddNotificationParentalControl(boundFunction(self.servicePinEntered, ref), PinInput, triesEntry=config.ParentalControl.retries.servicepin, pinList=self.getPinList(), service=ServiceReference(ref).getServiceName(), title=title, windowTitle=_("Parental control"))
119                         return False
120                 else:
121                         return True
122
123         def protectService(self, service):
124                 if not self.blacklist.has_key(service):
125                         self.serviceMethodWrapper(service, self.addServiceToList, self.blacklist)
126                         if config.ParentalControl.hideBlacklist.value and not self.sessionPinCached:
127                                 eDVBDB.getInstance().addFlag(eServiceReference(service), 2)
128
129         def unProtectService(self, service):
130                 if self.blacklist.has_key(service):
131                         self.serviceMethodWrapper(service, self.removeServiceFromList, self.blacklist)
132
133         def getProtectionLevel(self, service):
134                 return not self.blacklist.has_key(service) and -1 or 0
135
136         def getConfigValues(self):
137                 #Read all values from configuration
138                 self.checkPinInterval = False
139                 self.checkPinIntervalCancel = False
140                 self.checkSessionPin = False
141
142                 self.sessionPinCached = False
143                 self.pinIntervalSeconds = 0
144                 self.pinIntervalSecondsCancel = 0
145
146                 self.storeServicePin = config.ParentalControl.storeservicepin.value
147
148                 if self.storeServicePin == "never":
149                         pass
150                 elif self.storeServicePin == "standby":
151                         self.checkSessionPin = True
152                 else:
153                         self.checkPinInterval = True
154                         iMinutes = float(self.storeServicePin)
155                         iSeconds = int(iMinutes*60)
156                         self.pinIntervalSeconds = iSeconds
157
158         def standbyCounterCallback(self, configElement):
159                 self.resetSessionPin()
160
161         def resetSessionPin(self):
162                 #Reset the session pin, stop the timer
163                 self.sessionPinCached = False
164                 self.hideBlacklist()
165
166         def getCurrentTimeStamp(self):
167                 return time.time()
168
169         def getPinList(self):
170                 return [ x.value for x in config.ParentalControl.servicepin ]
171
172         def setSessionPinCached(self):
173                 if self.checkSessionPin == True:
174                         self.sessionPinCached = True
175                 if self.checkPinInterval == True:
176                         self.sessionPinCached = True
177                         self.sessionPinTimer.startLongTimer(self.pinIntervalSeconds)
178
179         def servicePinEntered(self, service, result):
180                 if result:
181                         self.setSessionPinCached()
182                         self.hideBlacklist()
183                         self.callback(ref = service)
184                 elif result == False:
185                         messageText = _("The pin code you entered is wrong.")
186                         if self.session:
187                                 self.session.open(MessageBox, messageText, MessageBox.TYPE_INFO, timeout=3)
188                         else:
189                                 AddPopup(messageText, MessageBox.TYPE_ERROR, timeout = 3)
190
191         def saveListToFile(self,sWhichList,vList):
192                 #Replaces saveWhiteList and saveBlackList:
193                 #I don't like to have two functions with identical code...
194                 file = open(resolveFilename(SCOPE_CONFIG, sWhichList), 'w')
195                 for sService,sType in vList.iteritems():
196                         #Only Services that are selected directly and Bouqets are saved.
197                         #Services that are added by a bouquet are not saved.
198                         #This is the reason for the change in self.whitelist and self.blacklist
199                         if TYPE_SERVICE in sType or TYPE_BOUQUET in sType:
200                                 file.write(str(sService) + "\n")
201                 file.close()
202
203         def openListFromFile(self,sWhichList):
204                 #Replaces openWhiteList and openBlackList:
205                 #I don't like to have two functions with identical code...
206                 result = {}
207                 try:
208                         for x in open(resolveFilename(SCOPE_CONFIG, sWhichList ), 'r'):
209                                 sPlain = x.strip()
210                                 self.serviceMethodWrapper(sPlain, self.addServiceToList, result)
211                 except:
212                         pass
213                 return result
214
215         def addServiceToList(self, service, type, vList):
216                 #Replaces addWhitelistService and addBlacklistService
217                 #The lists are not only lists of service references any more.
218                 #They are named lists with the service as key and an array of types as value:
219                 if vList.has_key(service):
220                         if not type in vList[service]:
221                                 vList[service].append(type)
222                 else:
223                         vList[service] = [type]
224
225         def removeServiceFromList(self, service, type, vList):
226                 #Replaces deleteWhitelistService and deleteBlacklistService
227                 if vList.has_key(service):
228                         if type in vList[service]:
229                                 vList[service].remove(type)
230                         if not vList[service]:
231                                 del vList[service]
232
233         def readServicesFromBouquet(self,sBouquetSelection,formatstring):
234                 #This method gives back a list of services for a given bouquet
235                 from enigma import eServiceCenter, eServiceReference
236                 from Screens.ChannelSelection import service_types_tv
237                 serviceHandler = eServiceCenter.getInstance()
238                 refstr = sBouquetSelection
239                 root = eServiceReference(refstr)
240                 list = serviceHandler.list(root)
241                 if list is not None:
242                         services = list.getContent("CN", True) #(servicecomparestring, name)
243                         return services
244
245         def save(self):
246                 self.saveListToFile(LIST_BLACKLIST, self.blacklist)
247
248         def open(self):
249                 self.blacklist = self.openListFromFile(LIST_BLACKLIST)
250                 self.hideBlacklist()
251                 if not self.filesOpened:
252                         # Reset PIN cache on standby: Use StandbyCounter- Config- Callback
253                         config.misc.standbyCounter.addNotifier(self.standbyCounterCallback, initial_call = False)
254                         self.filesOpened = True
255
256         def __getattr__(self, name):
257                 # This method is called if we lack a property. I'm lazy, so
258                 # I load the files when someone 'hits' this code
259                 if name in ('blacklist', 'whitelist'):
260                         if not self.filesOpened:
261                                 self.open()
262                                 return getattr(self, name)
263                 raise AttributeError, name
264
265         def hideBlacklist(self):
266                 if self.blacklist:
267                         if config.ParentalControl.servicepinactive.value and config.ParentalControl.storeservicepin.value != "never" and config.ParentalControl.hideBlacklist.value and not self.sessionPinCached:
268                                 for ref in self.blacklist:
269                                         if TYPE_BOUQUET not in ref:
270                                                 eDVBDB.getInstance().addFlag(eServiceReference(ref), 2)
271                         else:
272                                 for ref in self.blacklist:
273                                         if TYPE_BOUQUET not in ref:
274                                                 eDVBDB.getInstance().removeFlag(eServiceReference(ref), 2)
275                         refreshServiceList()