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