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