SystemPlugins/CrashlogAutoSubmit/plugin.py:
[enigma2.git] / lib / python / Plugins / SystemPlugins / CrashlogAutoSubmit / plugin.py
1 from Plugins.Plugin import PluginDescriptor
2 from Components.config import config, getConfigListEntry, ConfigSubsection, ConfigText, ConfigSelection, ConfigYesNo,ConfigText
3 from Components.ConfigList import ConfigListScreen
4 from Components.ActionMap import ActionMap
5 from Components.Label import Label
6 from Components.Pixmap import Pixmap
7 from Screens.Screen import Screen
8 from Screens.VirtualKeyBoard import VirtualKeyBoard
9 from Screens.ChoiceBox import ChoiceBox
10 from Screens.MessageBox import MessageBox
11 from enigma import ePoint
12
13 import os
14 from twisted.mail import smtp, relaymanager
15 import MimeWriter, mimetools, StringIO
16
17 config.plugins.crashlogautosubmit = ConfigSubsection()
18 config.plugins.crashlogautosubmit.sendmail = ConfigSelection(default = "send", choices = [
19         ("send", _("Always ask before sending")), ("send_always", _("Don't ask, just send")), ("send_never", _("Disable crashlog reporting"))])
20 config.plugins.crashlogautosubmit.sendlog = ConfigSelection(default = "rename", choices = [
21         ("delete", _("Delete crashlogs")), ("rename", _("Rename crashlogs"))])
22 config.plugins.crashlogautosubmit.attachemail = ConfigYesNo(default = False)
23 config.plugins.crashlogautosubmit.email = ConfigText(default = "myemail@home.com", fixed_size = False)
24 config.plugins.crashlogautosubmit.name = ConfigText(default = "Dreambox User", fixed_size = False)
25 config.plugins.crashlogautosubmit.sendAnonCrashlog = ConfigYesNo(default = False)
26 config.plugins.crashlogautosubmit.addNetwork = ConfigYesNo(default = False)
27 config.plugins.crashlogautosubmit.addWlan = ConfigYesNo(default = False)
28
29 class CrashlogAutoSubmitConfiguration(Screen, ConfigListScreen):
30
31         oldMailEntryValue = config.plugins.crashlogautosubmit.sendmail.value
32
33         skin = """
34                 <screen name="CrashlogAutoSubmitConfiguration" position="80,80" size="560,400" title="CrashlogAutoSubmit settings..." >
35                         <widget name="config" zPosition="2" position="5,5" size="550,360" scrollbarMode="showOnDemand" transparent="1" />
36                         <ePixmap pixmap="skin_default/div-h.png" position="0,300" zPosition="10" size="560,2" transparent="1" alphatest="on" />
37                         <widget name="status" position="10,300" zPosition="10" size="540,50" halign="center" valign="center" font="Regular;22" transparent="1" shadowColor="black" shadowOffset="-1,-1" />
38                         <ePixmap pixmap="skin_default/buttons/red.png" position="0,360" zPosition="2" size="140,40" transparent="1" alphatest="on" />
39                         <widget name="closetext" position="0,360" zPosition="10" size="140,40" halign="center" valign="center" font="Regular;22" transparent="1" shadowColor="black" shadowOffset="-1,-1" />
40                         <ePixmap pixmap="skin_default/buttons/green.png" position="140,360" zPosition="2" size="140,40" transparent="1" alphatest="on" />
41                         <widget name="installtext" position="140,360" zPosition="10" size="140,40" halign="center" valign="center" font="Regular;22" transparent="1" shadowColor="black" shadowOffset="-1,-1" />
42                         <widget name="VKeyButton" pixmap="skin_default/buttons/button_yellow.png" position="285,370" zPosition="2" size="15,16" transparent="1" alphatest="on" />
43                         <widget name="VKeyIcon" pixmap="skin_default/vkey_icon.png" position="300,355" zPosition="10" size="60,48" transparent="1" alphatest="on" />
44                         <widget name="HelpWindow" position="175,250" zPosition="1" size="1,1" transparent="1" />
45                 </screen>"""
46
47         def __init__(self, session):
48                 Screen.__init__(self, session)
49                 self.session = session
50                 self.MailEntry = None
51                 self.LogEntry = None
52                 self.addEmailEntry = None
53                 self.EmailEntry = None
54                 self.NameEntry = None
55                 self.AnonCrashlogEntry = None
56                 self.NetworkEntry = None
57                 self.WlanEntry = None
58                 self.msgCrashlogMailer = False
59
60                 self["shortcuts"] = ActionMap(["ShortcutActions", "SetupActions" ],
61                 {
62                         "ok": self.keySave,
63                         "cancel": self.keyCancel,
64                         "red": self.keyCancel,
65                         "green": self.keySave,
66                 }, -2)
67
68                 self["VirtualKB"] = ActionMap(["ColorActions" ],
69                 {
70                         "yellow": self.KeyYellow,
71                 }, -1)
72
73                 self.list = []
74                 ConfigListScreen.__init__(self, self.list,session = self.session)
75                 self.createSetup()
76
77                 self["VKeyButton"] = Pixmap()
78                 self["VKeyIcon"] = Pixmap()
79                 self["closetext"] = Label(_("Close"))
80                 self["installtext"] = Label(_("Save"))
81                 self["HelpWindow"] = Label()
82                 self["status"] = Label()
83
84                 self["VKeyButton"].hide()
85                 self["VKeyIcon"].hide()
86                 self["VirtualKB"].setEnabled(False)
87                 self.onShown.append(self.setWindowTitle)
88                 self.onClose.append(self.msgCrashlogNotifier)
89
90
91         def setWindowTitle(self):
92                 self.setTitle(_("CrashlogAutoSubmit settings..."))
93
94         def keyLeft(self):
95                 ConfigListScreen.keyLeft(self)
96                 self.newConfig()
97
98         def keyRight(self):
99                 ConfigListScreen.keyRight(self)
100                 self.newConfig()
101
102         def KeyYellow(self):
103                         if self["config"].getCurrent() == self.EmailEntry:
104                                 self.session.openWithCallback(self.EmailCallback, VirtualKeyBoard, title = (_("Please enter your email address here:")), text = config.plugins.crashlogautosubmit.email.value)
105                         if self["config"].getCurrent() == self.NameEntry:
106                                 self.session.openWithCallback(self.NameCallback, VirtualKeyBoard, title = (_("Please enter your name here (optional):")), text = config.plugins.crashlogautosubmit.name.value)
107
108         def EmailCallback(self, callback = None):
109                 if callback is not None and len(callback):
110                         config.plugins.crashlogautosubmit.email.setValue(callback)
111                         self["config"].invalidate(self.EmailEntry)
112
113         def NameCallback(self, callback = None):
114                 if callback is not None and len(callback):
115                         config.plugins.crashlogautosubmit.name.setValue(callback)
116                         self["config"].invalidate(self.NameEntry)
117
118         def createSetup(self):
119                 self.list = []
120                 self.MailEntry = getConfigListEntry(_("How to handle found crashlogs?"), config.plugins.crashlogautosubmit.sendmail)
121                 self.LogEntry = getConfigListEntry(_("What to do with submitted crashlogs?"), config.plugins.crashlogautosubmit.sendlog)
122                 self.addEmailEntry = getConfigListEntry(_("Include your email and name (optional) in the mail?"), config.plugins.crashlogautosubmit.attachemail)
123                 self.EmailEntry = getConfigListEntry(_("Your email address:"), config.plugins.crashlogautosubmit.email)
124                 self.NameEntry = getConfigListEntry(_("Your name (optional):"), config.plugins.crashlogautosubmit.name)
125                 self.AnonCrashlogEntry = getConfigListEntry(_("Anonymize crashlog?"), config.plugins.crashlogautosubmit.sendAnonCrashlog)
126                 self.NetworkEntry = getConfigListEntry(_("Add network configuration?"), config.plugins.crashlogautosubmit.addNetwork)
127                 self.WlanEntry = getConfigListEntry(_("Add WLAN configuration?"), config.plugins.crashlogautosubmit.addWlan)
128
129                 self.list.append( self.MailEntry )
130                 if config.plugins.crashlogautosubmit.sendmail.value is not "send_never":
131                         self.list.append( self.LogEntry )
132                         self.list.append( self.addEmailEntry )
133                         if config.plugins.crashlogautosubmit.attachemail.value is True:
134                                 self.list.append( self.EmailEntry )
135                                 self.list.append( self.NameEntry )
136                         self.list.append( self.AnonCrashlogEntry )
137                         self.list.append( self.NetworkEntry )
138                         self.list.append( self.WlanEntry )
139
140                 self["config"].list = self.list
141                 self["config"].l.setList(self.list)
142                 if not self.selectionChanged in self["config"].onSelectionChanged:
143                         self["config"].onSelectionChanged.append(self.selectionChanged)
144
145                 if not self.sendmailChanged in config.plugins.crashlogautosubmit.sendmail.notifiers:
146                         config.plugins.crashlogautosubmit.sendmail.notifiers.append(self.sendmailChanged)
147
148         def sendmailChanged(self, configElement):
149                 if configElement.value != CrashlogAutoSubmitConfiguration.oldMailEntryValue:
150                         self.msgCrashlogMailer = True
151                 else:
152                         self.msgCrashlogMailer = False
153
154         def newConfig(self):
155                 if self["config"].getCurrent() == self.MailEntry:
156                         self.createSetup()
157                 if self["config"].getCurrent() == self.addEmailEntry:
158                         self.createSetup()
159
160         def selectionChanged(self):
161                 current = self["config"].getCurrent()
162                 if current == self.MailEntry:
163                         self["status"].setText(_("Decide what should be done when crashlogs are found."))
164                         self.disableVKeyIcon()
165                 elif current == self.LogEntry:
166                         self["status"].setText(_("Decide what should happen to the crashlogs after submission."))
167                         self.disableVKeyIcon()
168                 elif current == self.addEmailEntry:
169                         self["status"].setText(_("Do you want to submit your email address and name so that we can contact you if needed?"))
170                         self.disableVKeyIcon()
171                 elif current == self.EmailEntry:
172                         self["status"].setText(_("Enter your email address so that we can contact you if needed."))
173                         self.enableVKeyIcon()
174                         self.showKeypad()
175                 elif current == self.NameEntry:
176                         self["status"].setText(_("Optionally enter your name if you want to."))
177                         self.enableVKeyIcon()
178                         self.showKeypad()
179                 elif current == self.AnonCrashlogEntry:
180                         self["status"].setText(_("Adds enigma2 settings and dreambox model informations like SN, rev... if enabled."))
181                         self.disableVKeyIcon()
182                 elif current == self.NetworkEntry:
183                         self["status"].setText(_("Adds network configuration if enabled."))
184                         self.disableVKeyIcon()
185                 elif current == self.WlanEntry:
186                         self["status"].setText(_("Adds wlan configuration if enabled."))
187                         self.disableVKeyIcon()
188
189         def enableVKeyIcon(self):
190                 self["VKeyButton"].show()
191                 self["VKeyIcon"].show()
192                 self["VirtualKB"].setEnabled(True)
193
194         def showKeypad(self):
195                 current = self["config"].getCurrent()
196                 helpwindowpos = self["HelpWindow"].getPosition()
197                 if hasattr(current[1], 'help_window'):
198                         if current[1].help_window.instance is not None:
199                                 current[1].help_window.instance.show()
200                                 current[1].help_window.instance.move(ePoint(helpwindowpos[0],helpwindowpos[1]))
201
202         def disableVKeyIcon(self):
203                 self["VKeyButton"].hide()
204                 self["VKeyIcon"].hide()
205                 self["VirtualKB"].setEnabled(False)
206
207         def hideKeypad(self):
208                 current = self["config"].getCurrent()
209                 if hasattr(current[1], 'help_window'):
210                         if current[1].help_window.instance is not None:
211                                 current[1].help_window.instance.hide()
212
213         def cancelConfirm(self, result):
214                 if not result:
215                         self.showKeypad()
216                         return
217                 for x in self["config"].list:
218                         x[1].cancel()
219                 self.close()
220
221         def keyCancel(self):
222                 print "cancel"
223                 if self["config"].isChanged():
224                         self.hideKeypad()
225                         self.session.openWithCallback(self.cancelConfirm, MessageBox, _("Really close without saving settings?"))
226                 else:
227                         self.close()
228
229         def keySave(self):
230                 print "saving"
231                 CrashlogAutoSubmitConfiguration.oldMailEntryValue = config.plugins.crashlogautosubmit.sendmail.value
232                 ConfigListScreen.keySave(self)
233
234         def msgCrashlogNotifier(self):
235                 if self.msgCrashlogMailer is True:
236                         try:
237                                 callCrashMailer(True, self.session)
238                         except AttributeError:
239                                 print "error, not restarting crashlogmailer"
240
241
242 def mxServerFound(mxServer,session):
243         print "[CrashlogAutoSubmit] - mxServerFound -->", mxServer
244         crashLogFilelist = []
245         message = StringIO.StringIO()
246         writer = MimeWriter.MimeWriter(message)
247         mailFrom = "enigma2@crashlog.dream-multimedia-tv.de"
248         mailTo = "enigma2@crashlog.dream-multimedia-tv.de"
249         subject = "Automatically generated crashlogmail"
250         # Define the main body headers.
251         writer.addheader('To', "dream-multimedia-crashlogs <enigma2@crashlog.dream-multimedia-tv.de>")
252         writer.addheader('From', "CrashlogAutoSubmitter <enigma2@crashlog.dream-multimedia-tv.de>")
253         writer.addheader('Subject', str(subject))
254         writer.addheader('Date', smtp.rfc822date())
255         if config.plugins.crashlogautosubmit.attachemail.value is True:
256                 if  str(config.plugins.crashlogautosubmit.email.value) != "myemail@home.com":
257                         writer.addheader('Reply-To', str(str(config.plugins.crashlogautosubmit.email.value)))
258         writer.addheader('MIME-Version', '1.0')
259         writer.startmultipartbody('mixed')
260         # start with a text/plain part
261         part = writer.nextpart()
262         body = part.startbody('text/plain')
263         part.flushheaders()
264         # Define the message body
265         body_text1 = "\nHello\n\nHere are some crashlogs i found for you.\n"
266         if  str(config.plugins.crashlogautosubmit.email.value) == "myemail@home.com":
267                 user_email = ""
268         else:
269                 user_email = "\nUser supplied email address: " + str(config.plugins.crashlogautosubmit.email.value)
270         if str(config.plugins.crashlogautosubmit.name.value) ==  "Dreambox User":
271                 user_name = ""
272         else:
273                 user_name = "\n\nOptional supplied name: " + str(config.plugins.crashlogautosubmit.name.value)
274         body_text2 = "\n\nThis is an automatically generated email from the CrashlogAutoSubmit plugin.\n\n\nHave a nice day.\n"
275         body_text = body_text1 + user_email + user_name + body_text2
276         body.write(body_text)
277
278         list = (
279                 (_("Yes"), "send"),
280                 (_("Yes, and don't ask again"), "send_always"),
281                 (_("No, not now"), "send_not"),
282                 (_("No, send them never"), "send_never")
283         )
284
285         def handleError(error):
286                 print "[CrashlogAutoSubmit] - Message send Error -->", error.getErrorMessage()
287
288         def handleSuccess(result):
289                 print "[CrashlogAutoSubmit] - Message sent successfully -->",result
290                 if len(crashLogFilelist):
291                         for crashlog in crashLogFilelist:
292                                 if config.plugins.crashlogautosubmit.sendlog.value == "delete":
293                                         os.remove(crashlog)
294                                 elif config.plugins.crashlogautosubmit.sendlog.value == "rename":
295                                         currfilename = str(os.path.basename(crashlog))
296                                         newfilename = "/media/hdd/" + currfilename + ".sent"
297                                         os.rename(crashlog,newfilename)
298
299         def send_mail():
300                 print "[CrashlogAutoSubmit] - send_mail"
301                 if len(crashLogFilelist):
302                         for crashlog in crashLogFilelist:
303                                 filename = str(os.path.basename(crashlog))
304                                 subpart = writer.nextpart()
305                                 subpart.addheader("Content-Transfer-Encoding", 'base64')
306                                 subpart.addheader("Content-Disposition",'attachment; filename="%s"' % filename)
307                                 subpart.addheader('Content-Description', 'Enigma2 crashlog')
308                                 body = subpart.startbody("%s; name=%s" % ('application/octet-stream', filename))
309                                 mimetools.encode(open(crashlog, 'rb'), body, 'base64')
310                 writer.lastpart()
311                 sending = smtp.sendmail(str(mxServer), mailFrom, mailTo, message.getvalue())
312                 sending.addCallback(handleSuccess).addErrback(handleError)
313
314         def handleAnswer(answer):
315                 answer = answer and answer[1]
316                 print "[CrashlogAutoSubmit] - handleAnswer --> ",answer
317                 if answer == "send":
318                         send_mail()
319                 elif answer == "send_always":
320                         config.plugins.crashlogautosubmit.sendmail.value = "send_always"
321                         config.plugins.crashlogautosubmit.sendmail.save()
322                         config.plugins.crashlogautosubmit.save()
323                         config.plugins.save()
324                         config.save()
325                         send_mail()
326                 elif answer in ( None, "send_never"):
327                         config.plugins.crashlogautosubmit.sendmail.value = "send_never"
328                         config.plugins.crashlogautosubmit.sendmail.save()
329                         config.plugins.crashlogautosubmit.save()
330                         config.plugins.save()
331                         config.save()
332                 elif answer == "send_not":
333                         print "[CrashlogAutoSubmit] - not sending crashlogs for this time."
334
335         for crashlog in os.listdir('/media/hdd'):
336                 if crashlog.startswith("enigma2_crash_") and crashlog.endswith(".log"):
337                         print "[CrashlogAutoSubmit] - found crashlog: ",os.path.basename(crashlog)
338                         crashLogFilelist.append('/media/hdd/' + crashlog)
339
340         if len(crashLogFilelist):
341                 if config.plugins.crashlogautosubmit.sendmail.value == "send":
342                         session.openWithCallback(handleAnswer, ChoiceBox, title=_("Crashlogs found!\nSend them to Dream Multimedia ?"), list = list)
343                 elif config.plugins.crashlogautosubmit.sendmail.value == "send_always":
344                         send_mail()
345         else:
346                 print "[CrashlogAutoSubmit] - no crashlogs found."
347
348
349 def getMailExchange(host):
350         print "[CrashlogAutoSubmit] - getMailExchange"
351         return relaymanager.MXCalculator().getMX(host).addCallback(_gotMXRecord)
352
353 def _gotMXRecord(mxRecord):
354         return str(mxRecord.name)
355
356
357 def startMailer(session):
358         if config.plugins.crashlogautosubmit.sendmail.value == "send_never":
359                 print "[CrashlogAutoSubmit] - not starting CrashlogAutoSubmit"
360                 return False
361
362         def gotMXServer(mxServer):
363                 print "[CrashlogAutoSubmit] gotMXServer: ",mxServer
364                 mxServerFound(mxServer,session)
365
366         def handleMXError(error):
367                 print "[CrashlogAutoSubmit] - MX resolve ERROR:", error.getErrorMessage()
368
369         if not config.misc.firstrun.value:
370                 getMailExchange('crashlog.dream-multimedia-tv.de').addCallback(gotMXServer).addErrback(handleMXError)
371
372
373 def callCrashMailer(result,session):
374         if result is True:
375                 print "[CrashlogAutoSubmit] - config changed"
376                 startMailer(session)
377         else:
378                 print "[CrashlogAutoSubmit] - config not changed"
379
380
381 def autostart(reason, **kwargs):
382         print "[CrashlogAutoSubmit] - autostart"
383         if "session" in kwargs:
384                 try:
385                         startMailer(kwargs["session"])
386                 except ImportError, e:
387                         print "[CrashlogAutoSubmit] Twisted-mail not available, not starting CrashlogAutoSubmitter", e
388
389
390 def openconfig(session, **kwargs):
391         session.open(CrashlogAutoSubmitConfiguration)
392
393
394 def selSetup(menuid, **kwargs):
395         if menuid != "system":
396                 return [ ]
397
398         return [(_("Crashlog settings") + "...", openconfig, "crashlog_config", 70)]
399
400
401 def Plugins(**kwargs):
402         return [PluginDescriptor(where = [PluginDescriptor.WHERE_SESSIONSTART, PluginDescriptor.WHERE_AUTOSTART], fnc = autostart),
403                 PluginDescriptor(name=_("CrashlogAutoSubmit"), description=_("CrashlogAutoSubmit settings"),where=PluginDescriptor.WHERE_MENU, fnc=selSetup)]
404