Merge branch 'bug_615_replace_rawfile'
[enigma2.git] / lib / python / Components / ParentalControl.py
1 from Components.config import config, ConfigSubsection, ConfigSelection, ConfigPIN, ConfigText, ConfigYesNo, ConfigSubList, ConfigInteger
2 #from Screens.ChannelSelection import service_types_tv
3 from Screens.InputBox import PinInput
4 from Screens.MessageBox import MessageBox
5 from Tools.BoundFunction import boundFunction
6 from ServiceReference import ServiceReference
7 from Tools import Notifications
8 from Tools.Directories import resolveFilename, SCOPE_CONFIG
9 from enigma import eTimer
10 import time
11
12 TYPE_SERVICE = "SERVICE"
13 TYPE_BOUQUETSERVICE = "BOUQUETSERVICE"
14 TYPE_BOUQUET = "BOUQUET"
15 LIST_BLACKLIST = "blacklist"
16 LIST_WHITELIST = "whitelist"
17
18 IMG_WHITESERVICE = LIST_WHITELIST + "-" + TYPE_SERVICE
19 IMG_WHITEBOUQUET = LIST_WHITELIST + "-" + TYPE_BOUQUET
20 IMG_BLACKSERVICE = LIST_BLACKLIST + "-" + TYPE_SERVICE
21 IMG_BLACKBOUQUET = LIST_BLACKLIST + "-" + TYPE_BOUQUET
22
23 def InitParentalControl():
24         global parentalControl
25         parentalControl = ParentalControl()
26         config.ParentalControl = ConfigSubsection()
27         config.ParentalControl.configured = ConfigYesNo(default = False)
28         config.ParentalControl.mode = ConfigSelection(default = "simple", choices = [("simple", _("simple")), ("complex", _("complex"))])
29         config.ParentalControl.storeservicepin = ConfigSelection(default = "never", choices = [("never", _("never")), ("5", _("5 minutes")), ("30", _("30 minutes")), ("60", _("60 minutes")), ("standby", _("until standby/restart"))])
30         config.ParentalControl.storeservicepincancel = ConfigSelection(default = "never", choices = [("never", _("never")), ("5", _("5 minutes")), ("30", _("30 minutes")), ("60", _("60 minutes")), ("standby", _("until standby/restart"))])
31         config.ParentalControl.servicepinactive = ConfigYesNo(default = False)
32         config.ParentalControl.setuppinactive = ConfigYesNo(default = False)
33         config.ParentalControl.type = ConfigSelection(default = "blacklist", choices = [(LIST_WHITELIST, _("whitelist")), (LIST_BLACKLIST, _("blacklist"))])
34         config.ParentalControl.setuppin = ConfigPIN(default = -1)
35         
36         config.ParentalControl.retries = ConfigSubsection()
37         config.ParentalControl.retries.setuppin = ConfigSubsection()
38         config.ParentalControl.retries.setuppin.tries = ConfigInteger(default = 3)
39         config.ParentalControl.retries.setuppin.time = ConfigInteger(default = 3)       
40         config.ParentalControl.retries.servicepin = ConfigSubsection()
41         config.ParentalControl.retries.servicepin.tries = ConfigInteger(default = 3)
42         config.ParentalControl.retries.servicepin.time = ConfigInteger(default = 3)
43 #       config.ParentalControl.configured = configElement("config.ParentalControl.configured", configSelection, 1, (("yes", _("yes")), ("no", _("no"))))
44         #config.ParentalControl.mode = configElement("config.ParentalControl.mode", configSelection, 0, (("simple", _("simple")), ("complex", _("complex"))))
45         #config.ParentalControl.storeservicepin = configElement("config.ParentalControl.storeservicepin", configSelection, 0, (("never", _("never")), ("5_minutes", _("5 minutes")), ("30_minutes", _("30 minutes")), ("60_minutes", _("60 minutes")), ("restart", _("until restart"))))
46         #config.ParentalControl.servicepinactive = configElement("config.ParentalControl.servicepinactive", configSelection, 1, (("yes", _("yes")), ("no", _("no"))))
47         #config.ParentalControl.setuppinactive = configElement("config.ParentalControl.setuppinactive", configSelection, 1, (("yes", _("yes")), ("no", _("no"))))
48         #config.ParentalControl.type = configElement("config.ParentalControl.type", configSelection, 0, (("whitelist", _("whitelist")), ("blacklist", _("blacklist"))))
49         #config.ParentalControl.setuppin = configElement("config.ParentalControl.setuppin", configSequence, "0000", configSequenceArg().get("PINCODE", (4, "")))
50
51         config.ParentalControl.servicepin = ConfigSubList()
52
53         for i in (0, 1, 2):
54                 config.ParentalControl.servicepin.append(ConfigPIN(default = -1))
55                 #config.ParentalControl.servicepin.append(configElement("config.ParentalControl.servicepin.level" + str(i), configSequence, "0000", configSequenceArg().get("PINCODE", (4, ""))))
56
57 class ParentalControl:
58         def __init__(self):
59                 #Do not call open on init, because bouquets are not ready at that moment 
60                 self.open()
61                 self.serviceLevel = {}
62                 #Instead: Use Flags to see, if we already initialized config and called open
63                 self.configInitialized = False
64                 #This is the timer that is used to see, if the time for caching the pin is over
65                 #Of course we could also work without a timer and compare the times every
66                 #time we call isServicePlayable. But this might probably slow down zapping, 
67                 #That's why I decided to use a timer
68                 self.sessionPinTimer = eTimer()
69                 self.sessionPinTimer.callback.append(self.resetSessionPin)
70         
71         def serviceMethodWrapper(self, service, method, *args):
72                 #This method is used to call all functions that need a service as Parameter: 
73                 #It takes either a Service- Reference or a Bouquet- Reference and passes 
74                 #Either the service or all services contained in the bouquet to the method given
75                 #That way all other functions do not need to distinguish between service and bouquet. 
76                 if "FROM BOUQUET" in service:
77                         method( service , TYPE_BOUQUET , *args )
78                         servicelist = self.readServicesFromBouquet(service,"C")
79                         for ref in servicelist:
80                                 sRef = str(ref[0])
81                                 method( sRef , TYPE_BOUQUETSERVICE , *args )
82                 else:
83                         ref = ServiceReference(service)
84                         sRef = str(ref)
85                         method( sRef , TYPE_SERVICE , *args )
86         
87         def setServiceLevel(self, service, type, level):
88                 self.serviceLevel[service] = level
89
90         def isServicePlayable(self, ref, callback):
91                 if not config.ParentalControl.configured.value or not config.ParentalControl.servicepinactive.value:
92                         return True
93                 #Check if configuration has already been read or if the significant values have changed.
94                 #If true: read the configuration 
95                 if self.configInitialized == False or self.storeServicePin != config.ParentalControl.storeservicepin.value or self.storeServicePinCancel != config.ParentalControl.storeservicepincancel.value:
96                         self.getConfigValues()
97                 service = ref.toCompareString()
98                 if (config.ParentalControl.type.value == LIST_WHITELIST and not self.whitelist.has_key(service)) or (config.ParentalControl.type.value == LIST_BLACKLIST and self.blacklist.has_key(service)):
99                         #Check if the session pin is cached and return the cached value, if it is.
100                         if self.sessionPinCached == True:
101                                 #As we can cache successful pin- entries as well as canceled pin- entries,
102                                 #We give back the last action 
103                                 return self.sessionPinCachedValue
104                         self.callback = callback
105                         #Someone started to implement different levels of protection. Seems they were never completed
106                         #I did not throw out this code, although it is of no use at the moment
107                         levelNeeded = 0
108                         if self.serviceLevel.has_key(service):
109                                 levelNeeded = self.serviceLevel[service]
110                         pinList = self.getPinList()[:levelNeeded + 1]
111                         Notifications.AddNotificationWithCallback(boundFunction(self.servicePinEntered, ref), PinInput, triesEntry = config.ParentalControl.retries.servicepin, pinList = pinList, service = ServiceReference(ref).getServiceName(), title = _("this service is protected by a parental control pin"), windowTitle = _("Parental control"))
112                         return False
113                 else:
114                         return True
115                 
116         def protectService(self, service):
117                 if config.ParentalControl.type.value == LIST_WHITELIST:
118                         if self.whitelist.has_key(service):
119                                 self.serviceMethodWrapper(service, self.removeServiceFromList, self.whitelist)
120                                 #self.deleteWhitelistService(service)
121                 else: # blacklist
122                         if not self.blacklist.has_key(service):
123                                 self.serviceMethodWrapper(service, self.addServiceToList, self.blacklist)
124                                 #self.addBlacklistService(service)
125                 #print "whitelist:", self.whitelist
126                 #print "blacklist:", self.blacklist
127
128         def unProtectService(self, service):
129                 #print "unprotect"
130                 #print "config.ParentalControl.type.value:", config.ParentalControl.type.value
131                 if config.ParentalControl.type.value == LIST_WHITELIST:
132                         if not self.whitelist.has_key(service):
133                                 self.serviceMethodWrapper(service, self.addServiceToList, self.whitelist)
134                                 #self.addWhitelistService(service)
135                 else: # blacklist
136                         if self.blacklist.has_key(service):
137                                 self.serviceMethodWrapper(service, self.removeServiceFromList, self.blacklist)
138                                 #self.deleteBlacklistService(service)
139                 #print "whitelist:", self.whitelist
140                 #print "blacklist:", self.blacklist
141
142         def getProtectionLevel(self, service):
143                 if (config.ParentalControl.type.value == LIST_WHITELIST and not self.whitelist.has_key(service)) or (config.ParentalControl.type.value == LIST_BLACKLIST and self.blacklist.has_key(service)):
144                         if self.serviceLevel.has_key(service):
145                                 return self.serviceLevel[service]
146                         else:
147                                 return 0
148                 else:
149                         return -1
150
151         def getProtectionType(self, service):
152                 #New method used in ParentalControlList: This method does not only return
153                 #if a service is protected or not, it also returns, why (whitelist or blacklist, service or bouquet)
154                 sImage = ""
155                 if (config.ParentalControl.type.value == LIST_WHITELIST):
156                         if self.whitelist.has_key(service):
157                                 if TYPE_SERVICE in self.whitelist[service]:
158                                         sImage = IMG_WHITESERVICE
159                                 else:
160                                         sImage = IMG_WHITEBOUQUET
161                 elif (config.ParentalControl.type.value == LIST_BLACKLIST):
162                         if self.blacklist.has_key(service):
163                                 if TYPE_SERVICE in self.blacklist[service]:
164                                         sImage = IMG_BLACKSERVICE
165                                 else:
166                                         sImage = IMG_BLACKBOUQUET
167                 bLocked = self.getProtectionLevel(service) != -1
168                 return (bLocked,sImage)
169         
170         def getConfigValues(self):      
171                 #Read all values from configuration 
172                 self.checkPinInterval = False
173                 self.checkPinIntervalCancel = False
174                 self.checkSessionPin = False
175                 self.checkSessionPinCancel = False
176                 
177                 self.sessionPinCached = False
178                 self.pinIntervalSeconds = 0
179                 self.pinIntervalSecondsCancel = 0
180
181                 self.storeServicePin = config.ParentalControl.storeservicepin.value
182                 self.storeServicePinCancel = config.ParentalControl.storeservicepincancel.value
183                 
184                 if self.storeServicePin == "never":
185                         pass
186                 elif self.storeServicePin == "standby":
187                         self.checkSessionPin = True
188                 else:
189                         self.checkPinInterval = True
190                         iMinutes = float(self.storeServicePin)
191                         iSeconds = iMinutes*60
192                         self.pinIntervalSeconds = iSeconds
193         
194                 if self.storeServicePinCancel == "never":
195                         pass
196                 elif self.storeServicePinCancel == "standby":
197                         self.checkSessionPinCancel = True
198                 else:
199                         self.checkPinIntervalCancel = True
200                         iMinutes = float(self.storeServicePinCancel)
201                         iSeconds = iMinutes*60
202                         self.pinIntervalSecondsCancel = iSeconds
203         
204                 self.configInitialized = True
205                 # Reset PIN cache on standby: Use StandbyCounter- Config- Callback
206                 config.misc.standbyCounter.addNotifier(self.standbyCounterCallback, initial_call = False)
207
208         def standbyCounterCallback(self, configElement):
209                 self.resetSessionPin()
210                 
211         def resetSessionPin(self):
212                 #Reset the session pin, stop the timer
213                 self.sessionPinCached = False
214                 self.sessionPinTimer.stop()
215
216         def getCurrentTimeStamp(self):
217                 return time.time()
218
219         def getPinList(self):
220                 return [ x.value for x in config.ParentalControl.servicepin ]
221                 
222         def servicePinEntered(self, service, result):
223
224                 if result is not None and result:
225                         #This is the new function of caching the service pin
226                         #save last session and time of last entered pin...
227                         if self.checkSessionPin == True:
228                                 self.sessionPinCached = True
229                                 self.sessionPinCachedValue = True
230                         if self.checkPinInterval == True:
231                                 self.sessionPinCached = True
232                                 self.sessionPinCachedValue = True
233                                 self.sessionPinTimer.start(self.pinIntervalSeconds*1000,1)
234                         self.callback(ref = service)
235                 else:
236                         #This is the new function of caching cancelling of service pin
237                         if result is not None:
238                                 Notifications.AddNotification(MessageBox,  _("The pin code you entered is wrong."), MessageBox.TYPE_ERROR)
239                         else:
240                                 if self.checkSessionPinCancel == True:
241                                         self.sessionPinCached = True
242                                         self.sessionPinCachedValue = False
243                                 if self.checkPinIntervalCancel == True:
244                                         self.sessionPinCached = True
245                                         self.sessionPinCachedValue = False
246                                         self.sessionPinTimer.start(self.pinIntervalSecondsCancel*1000,1) 
247                         
248         def saveListToFile(self,sWhichList):
249                 #Replaces saveWhiteList and saveBlackList: 
250                 #I don't like to have two functions with identical code...
251                 if sWhichList == LIST_BLACKLIST:
252                         vList = self.blacklist
253                 else:
254                         vList = self.whitelist
255                 file = open(resolveFilename(SCOPE_CONFIG, sWhichList), 'w')
256                 for sService,sType in vList.iteritems():
257                         #Only Services that are selected directly and Bouqets are saved. 
258                         #Services that are added by a bouquet are not saved. 
259                         #This is the reason for the change in self.whitelist and self.blacklist
260                         if TYPE_SERVICE in sType or TYPE_BOUQUET in sType:
261                                 file.write(str(sService) + "\n")
262                 file.close
263
264         def openListFromFile(self,sWhichList):
265                 #Replaces openWhiteList and openBlackList: 
266                 #I don't like to have two functions with identical code...
267                 if sWhichList == LIST_BLACKLIST:
268                         self.blacklist = {}
269                         vList = self.blacklist
270                 else:
271                         self.whitelist = {}
272                         vList = self.whitelist
273                 try:
274                         file = open(resolveFilename(SCOPE_CONFIG, sWhichList ), 'r')
275                         lines = file.readlines()
276                         for x in lines:
277                                 sPlain = x.strip()
278                                 self.serviceMethodWrapper(sPlain, self.addServiceToList, vList)
279                         file.close
280                 except:
281                         pass
282         
283         def addServiceToList(self, service, type, vList):
284                 #Replaces addWhitelistService and addBlacklistService
285                 #The lists are not only lists of service references any more. 
286                 #They are named lists with the service as key and an array of types as value:
287                 
288                 if vList.has_key(service):
289                         if not type in vList[service]:
290                                 vList[service].append(type)
291                 else:
292                         vList[service] = [type]
293         
294         def removeServiceFromList(self, service, type, vList):
295                 #Replaces deleteWhitelistService and deleteBlacklistService
296                 if vList.has_key(service):
297                         if type in vList[service]:
298                                 vList[service].remove(type)
299                         if not vList[service]:
300                                 del vList[service]
301                 if self.serviceLevel.has_key(service):
302                         self.serviceLevel.remove(service)
303                 
304         def readServicesFromBouquet(self,sBouquetSelection,formatstring):
305                 #This method gives back a list of services for a given bouquet
306                 from enigma import eServiceCenter, eServiceReference
307                 from Screens.ChannelSelection import service_types_tv
308                 serviceHandler = eServiceCenter.getInstance()
309                 refstr = sBouquetSelection
310                 root = eServiceReference(refstr)
311                 list = serviceHandler.list(root)
312                 if list is not None:
313                         services = list.getContent("CN", True) #(servicecomparestring, name)
314                         return services
315                 
316         def save(self):
317                 # we need to open the files in case we havent's read them yet
318                 self.saveListToFile(LIST_BLACKLIST)
319                 self.saveListToFile(LIST_WHITELIST)
320                 
321         def open(self):
322                 self.openListFromFile(LIST_BLACKLIST)
323                 self.openListFromFile(LIST_WHITELIST)
324