4.2.5r2
[enigma2.git] / usr / lib / enigma2 / python / Plugins / SystemPlugins / CrashlogAutoSubmit / plugin.py
1 from Plugins.Plugin import PluginDescriptor
2 from Components.config import config, getConfigListEntry, ConfigSubsection, ConfigSelection, ConfigYesNo
3 from Components.ConfigList import ConfigListScreen
4 from Components.ActionMap import ActionMap
5 from Components.Sources.StaticText import StaticText
6 from Components.Pixmap import Pixmap
7 from Screens.Screen import Screen
8 from Screens.ChoiceBox import ChoiceBox
9 from Screens.MessageBox import MessageBox
10 from enigma import ePoint, eTPM
11 from Tools import Notifications
12
13 from glob import glob
14 from os import remove, rename
15 from os.path import basename
16 from twisted.mail import smtp, relaymanager
17 import MimeWriter, mimetools, StringIO
18 from __init__ import decrypt_block, validate_cert, read_random
19
20 config.plugins.crashlogautosubmit = ConfigSubsection()
21 config.plugins.crashlogautosubmit.sendmail = ConfigSelection(default = "send", choices = [
22         ("send", _("Always ask before sending")), ("send_always", _("Don't ask, just send")), ("send_never", _("Disable crashlog reporting"))])
23 config.plugins.crashlogautosubmit.sendlog = ConfigSelection(default = "rename", choices = [
24         ("delete", _("Delete crashlogs")), ("rename", _("Rename crashlogs"))])
25 config.plugins.crashlogautosubmit.sendAnonCrashlog = ConfigYesNo(default = True)
26
27 class CrashlogAutoSubmitConfiguration(Screen, ConfigListScreen):
28
29         oldMailEntryValue = config.plugins.crashlogautosubmit.sendmail.value
30
31         skin = """
32                 <screen name="CrashlogAutoSubmitConfiguration" position="center,center" size="560,440" title="CrashlogAutoSubmit settings" >
33                         <ePixmap pixmap="skin_default/buttons/red.png" position="0,0" size="140,40" alphatest="on" />
34                         <ePixmap pixmap="skin_default/buttons/green.png" position="140,0" size="140,40" alphatest="on" />
35                         <widget source="key_red" render="Label" position="0,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#9f1313" transparent="1" />
36                         <widget source="key_green" render="Label" position="140,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#1f771f" transparent="1" />
37                         <widget name="config" zPosition="2" position="5,50" size="550,300" scrollbarMode="showOnDemand" transparent="1" />
38                         <ePixmap pixmap="skin_default/div-h.png" position="0,390" zPosition="10" size="560,2" transparent="1" alphatest="on" />
39                         <widget source="status" render="Label" position="10,400" size="540,40" zPosition="10" font="Regular;20" halign="center" valign="center" backgroundColor="#25062748" transparent="1"/>
40                         <widget name="VKeyIcon" pixmap="skin_default/buttons/key_text.png" position="10,420" zPosition="10" size="35,25" transparent="1" alphatest="on" />
41                         <widget name="HelpWindow" pixmap="skin_default/vkey_icon.png" position="160,325" zPosition="1" size="1,1" transparent="1" alphatest="on" />
42                 </screen>"""
43
44         def __init__(self, session):
45                 Screen.__init__(self, session)
46                 self.session = session
47                 self.MailEntry = None
48                 self.LogEntry = None
49                 self.addEmailEntry = None
50                 self.EmailEntry = None
51                 self.NameEntry = None
52                 self.AnonCrashlogEntry = None
53                 self.msgCrashlogMailer = False
54
55                 self["shortcuts"] = ActionMap(["ShortcutActions", "SetupActions" ],
56                 {
57                         "ok": self.keySave,
58                         "cancel": self.keyCancel,
59                         "red": self.keyCancel,
60                         "green": self.keySave,
61                 }, -2)
62
63                 self.list = []
64                 ConfigListScreen.__init__(self, self.list,session = self.session)
65                 self.createSetup()
66
67                 self["key_red"] = StaticText(_("Close"))
68                 self["key_green"] = StaticText(_("Save"))
69                 self["status"] = StaticText()
70                 self["VKeyIcon"] = Pixmap()
71                 self["HelpWindow"] = Pixmap()
72
73                 self["VKeyIcon"].hide()
74                 self.onShown.append(self.setWindowTitle)
75                 self.onClose.append(self.msgCrashlogNotifier)
76
77
78         def setWindowTitle(self):
79                 self.setTitle(_("CrashlogAutoSubmit settings..."))
80
81         def keyLeft(self):
82                 ConfigListScreen.keyLeft(self)
83                 self.newConfig()
84
85         def keyRight(self):
86                 ConfigListScreen.keyRight(self)
87                 self.newConfig()
88
89         def createSetup(self):
90                 self.list = []
91                 self.MailEntry = getConfigListEntry(_("How to handle found crashlogs?"), config.plugins.crashlogautosubmit.sendmail)
92                 self.LogEntry = getConfigListEntry(_("What to do with submitted crashlogs?"), config.plugins.crashlogautosubmit.sendlog)
93                 self.AnonCrashlogEntry = getConfigListEntry(_("Anonymize crashlog?"), config.plugins.crashlogautosubmit.sendAnonCrashlog)
94
95                 self.list.append( self.MailEntry )
96                 if config.plugins.crashlogautosubmit.sendmail.value is not "send_never":
97                         self.list.append( self.LogEntry )
98                         self.list.append( self.AnonCrashlogEntry )
99
100                 self["config"].list = self.list
101                 self["config"].l.setList(self.list)
102                 if not self.selectionChanged in self["config"].onSelectionChanged:
103                         self["config"].onSelectionChanged.append(self.selectionChanged)
104
105                 if not self.sendmailChanged in config.plugins.crashlogautosubmit.sendmail.notifiers:
106                         config.plugins.crashlogautosubmit.sendmail.addNotifier(self.sendmailChanged)
107
108         def sendmailChanged(self, configElement):
109                 if configElement.value != CrashlogAutoSubmitConfiguration.oldMailEntryValue:
110                         self.msgCrashlogMailer = True
111                 else:
112                         self.msgCrashlogMailer = False
113
114         def newConfig(self):
115                 if self["config"].getCurrent() == self.MailEntry:
116                         self.createSetup()
117                 if self["config"].getCurrent() == self.addEmailEntry:
118                         self.createSetup()
119
120         def selectionChanged(self):
121                 current = self["config"].getCurrent()
122                 if current == self.MailEntry:
123                         self["status"].setText(_("Decide what should be done when crashlogs are found."))
124                 elif current == self.LogEntry:
125                         self["status"].setText(_("Decide what should happen to the crashlogs after submission."))
126                 elif current == self.AnonCrashlogEntry:
127                         self["status"].setText(_("Adds enigma2 settings and dreambox model informations like SN, rev... if enabled."))
128
129         def showKeypad(self):
130                 current = self["config"].getCurrent()
131                 helpwindowpos = self["HelpWindow"].getPosition()
132                 if hasattr(current[1], 'help_window'):
133                         if current[1].help_window.instance is not None:
134                                 current[1].help_window.instance.show()
135                                 current[1].help_window.instance.move(ePoint(helpwindowpos[0],helpwindowpos[1]))
136
137         def hideKeypad(self):
138                 current = self["config"].getCurrent()
139                 if hasattr(current[1], 'help_window'):
140                         if current[1].help_window.instance is not None:
141                                 current[1].help_window.instance.hide()
142
143         def cancelConfirm(self, result):
144                 if not result:
145                         self.showKeypad()
146                         return
147                 for x in self["config"].list:
148                         x[1].cancel()
149                 self.close()
150
151         def keyCancel(self):
152                 print "cancel"
153                 if self["config"].isChanged():
154                         self.hideKeypad()
155                         self.session.openWithCallback(self.cancelConfirm, MessageBox, _("Really close without saving settings?"))
156                 else:
157                         self.close()
158
159         def keySave(self):
160                 print "saving"
161                 CrashlogAutoSubmitConfiguration.oldMailEntryValue = config.plugins.crashlogautosubmit.sendmail.value
162                 ConfigListScreen.keySave(self)
163
164         def msgCrashlogNotifier(self):
165                 if self.msgCrashlogMailer is True:
166                         try:
167                                 callCrashMailer(True, self.session)
168                         except AttributeError:
169                                 print "error, not restarting crashlogmailer"
170
171
172 def mxServerFound(mxServer,session):
173         print "[CrashlogAutoSubmit] - mxServerFound -->", mxServer
174         crashLogFilelist = []
175         message = StringIO.StringIO()
176         writer = MimeWriter.MimeWriter(message)
177         mailFrom = "enigma2@crashlog.dream-multimedia-tv.de"
178         mailTo = "enigma2@crashlog.dream-multimedia-tv.de"
179         subject = "Automatically generated crashlogmail"
180         # Define the main body headers.
181         writer.addheader('To', "dream-multimedia-crashlogs <enigma2@crashlog.dream-multimedia-tv.de>")
182         writer.addheader('From', "CrashlogAutoSubmitter <enigma2@crashlog.dream-multimedia-tv.de>")
183         writer.addheader('Subject', str(subject))
184         writer.addheader('Date', smtp.rfc822date())
185         writer.addheader('MIME-Version', '1.0')
186         writer.startmultipartbody('mixed')
187         # start with a text/plain part
188         part = writer.nextpart()
189         body = part.startbody('text/plain')
190         part.flushheaders()
191         # Define the message body
192         body_text1 = "\nHello\n\nHere are some crashlogs i found for you.\n"
193         body_text2 = "\n\nThis is an automatically generated email from the CrashlogAutoSubmit plugin.\n\n\nHave a nice day.\n"
194         body_text = body_text1 + body_text2
195         body.write(body_text)
196
197         list = (
198                 (_("Yes"), "send"),
199                 (_("Yes, and don't ask again"), "send_always"),
200                 (_("No, not now"), "send_not"),
201                 (_("No, send them never"), "send_never")
202         )
203
204         def handleError(error):
205                 print "[CrashlogAutoSubmit] - Message send Error -->", error.getErrorMessage()
206
207         def handleSuccess(result):
208                 print "[CrashlogAutoSubmit] - Message sent successfully -->",result
209                 for crashlog in crashLogFilelist:
210                         if config.plugins.crashlogautosubmit.sendlog.value == "delete":
211                                 remove(crashlog)
212                         elif config.plugins.crashlogautosubmit.sendlog.value == "rename":
213                                 currfilename = basename(crashlog)
214                                 newfilename = "/media/hdd/" + currfilename + ".sent"
215                                 rename(crashlog, newfilename)
216
217         def send_mail():
218                 rootkey = ['\x9f', '|', '\xe4', 'G', '\xc9', '\xb4', '\xf4', '#', '&', '\xce', '\xb3', '\xfe', '\xda', '\xc9', 'U', '`', '\xd8', '\x8c', 's', 'o', '\x90', '\x9b', '\\', 'b', '\xc0', '\x89', '\xd1', '\x8c', '\x9e', 'J', 'T', '\xc5', 'X', '\xa1', '\xb8', '\x13', '5', 'E', '\x02', '\xc9', '\xb2', '\xe6', 't', '\x89', '\xde', '\xcd', '\x9d', '\x11', '\xdd', '\xc7', '\xf4', '\xe4', '\xe4', '\xbc', '\xdb', '\x9c', '\xea', '}', '\xad', '\xda', 't', 'r', '\x9b', '\xdc', '\xbc', '\x18', '3', '\xe7', '\xaf', '|', '\xae', '\x0c', '\xe3', '\xb5', '\x84', '\x8d', '\r', '\x8d', '\x9d', '2', '\xd0', '\xce', '\xd5', 'q', '\t', '\x84', 'c', '\xa8', ')', '\x99', '\xdc', '<', '"', 'x', '\xe8', '\x87', '\x8f', '\x02', ';', 'S', 'm', '\xd5', '\xf0', '\xa3', '_', '\xb7', 'T', '\t', '\xde', '\xa7', '\xf1', '\xc9', '\xae', '\x8a', '\xd7', '\xd2', '\xcf', '\xb2', '.', '\x13', '\xfb', '\xac', 'j', '\xdf', '\xb1', '\x1d', ':', '?']
219                 etpm = eTPM()
220                 l2cert = etpm.getData(eTPM.DT_LEVEL2_CERT)
221                 if l2cert is None:
222                         return
223                 l2key = validate_cert(l2cert, rootkey)
224                 if l2key is None:
225                         return
226                 l3cert = etpm.getData(eTPM.DT_LEVEL3_CERT)
227                 if l3cert is None:
228                         return
229                 l3key = validate_cert(l3cert, l2key)
230                 if l3key is None:
231                         return
232                 rnd = read_random()
233                 if rnd is None:
234                         return
235                 val = etpm.computeSignature(rnd)
236                 result = decrypt_block(val, l3key)
237                 if result[80:88] == rnd:
238                         print "[CrashlogAutoSubmit] - send_mail"
239                         for crashlog in crashLogFilelist:
240                                 filename = basename(crashlog)
241                                 subpart = writer.nextpart()
242                                 subpart.addheader("Content-Transfer-Encoding", 'base64')
243                                 subpart.addheader("Content-Disposition",'attachment; filename="%s"' % filename)
244                                 subpart.addheader('Content-Description', 'Enigma2 crashlog')
245                                 body = subpart.startbody("%s; name=%s" % ('application/octet-stream', filename))
246                                 mimetools.encode(open(crashlog, 'rb'), body, 'base64')
247                         writer.lastpart()
248                         sending = smtp.sendmail(str(mxServer), mailFrom, mailTo, message.getvalue())
249                         sending.addCallback(handleSuccess).addErrback(handleError)
250
251         def handleAnswer(answer):
252                 answer = answer and answer[1]
253                 print "[CrashlogAutoSubmit] - handleAnswer --> ",answer
254                 if answer == "send":
255                         send_mail()
256                 elif answer == "send_always":
257                         config.plugins.crashlogautosubmit.sendmail.value = "send_always"
258                         config.plugins.crashlogautosubmit.sendmail.save()
259                         config.plugins.crashlogautosubmit.save()
260                         config.plugins.save()
261                         send_mail()
262                 elif answer in ( None, "send_never"):
263                         config.plugins.crashlogautosubmit.sendmail.value = "send_never"
264                         config.plugins.crashlogautosubmit.sendmail.save()
265                         config.plugins.crashlogautosubmit.save()
266                         config.plugins.save()
267                 elif answer == "send_not":
268                         print "[CrashlogAutoSubmit] - not sending crashlogs for this time."
269
270         for crashlog in glob('/media/hdd/enigma2_crash_*.log'):
271                 print "[CrashlogAutoSubmit] - found crashlog: ", basename(crashlog)
272                 crashLogFilelist.append(crashlog)
273
274         if len(crashLogFilelist):
275                 if config.plugins.crashlogautosubmit.sendmail.value == "send":
276                         Notifications.AddNotificationWithCallback(handleAnswer, ChoiceBox, title=_("Crashlogs found!\nSend them to Dream Multimedia?"), list = list)
277                 elif config.plugins.crashlogautosubmit.sendmail.value == "send_always":
278                         send_mail()
279         else:
280                 print "[CrashlogAutoSubmit] - no crashlogs found."
281
282
283 def startMailer(session):
284         if config.plugins.crashlogautosubmit.sendmail.value == "send_never":
285                 print "[CrashlogAutoSubmit] - not starting CrashlogAutoSubmit"
286                 return False
287
288         def gotMXServer(mx):
289                 print "[CrashlogAutoSubmit] gotMXServer: ", mx.name
290                 mxServerFound(mx.name, session)
291
292         def handleMXError(error):
293                 print "[CrashlogAutoSubmit] - MX resolve ERROR:", error.getErrorMessage()
294
295         if not config.misc.firstrun.value:
296                 relaymanager.MXCalculator().getMX('crashlog.dream-multimedia-tv.de').addCallback(gotMXServer).addErrback(handleMXError)
297
298
299 def callCrashMailer(result,session):
300         if result is True:
301                 print "[CrashlogAutoSubmit] - config changed"
302                 startMailer(session)
303         else:
304                 print "[CrashlogAutoSubmit] - config not changed"
305
306
307 def autostart(reason, **kwargs):
308         print "[CrashlogAutoSubmit] - autostart"
309         if "session" in kwargs:
310                 try:
311                         startMailer(kwargs["session"])
312                 except ImportError, e:
313                         print "[CrashlogAutoSubmit] Twisted-mail not available, not starting CrashlogAutoSubmitter", e
314
315
316 def openconfig(session, **kwargs):
317         session.open(CrashlogAutoSubmitConfiguration)
318
319
320 def selSetup(menuid, **kwargs):
321         if menuid != "system":
322                 return [ ]
323
324         return [(_("Crashlog settings"), openconfig, "crashlog_config", 70)]
325
326
327 def Plugins(**kwargs):
328         return [PluginDescriptor(where = [PluginDescriptor.WHERE_SESSIONSTART, PluginDescriptor.WHERE_AUTOSTART], needsRestart = False, fnc = autostart),
329                 PluginDescriptor(name=_("CrashlogAutoSubmit"), description=_("CrashlogAutoSubmit settings"),where=PluginDescriptor.WHERE_MENU, needsRestart = False, fnc=selSetup)]
330