################################################################################
# Copyright (C) 2015 Surfacingx and Whufclee #
# #
# This Program is free software; you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation; either version 2, or (at your option) #
# any later version. #
# #
# This Program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with XBMC; see the file COPYING. If not, write to #
# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. #
# http://www.gnu.org/copyleft/gpl.html #
################################################################################
# Kodi Logfile Uploader #
# Original Code by Team Kodi #
# By Surfacingx and Whufclee #
# #
# Modified to Support Kodi Forks #
# Added Email Logfile Url Support #
################################################################################
import os
import re
import socket
import pyqrcode
import json
import uservar
from resources.libs import wizard as wiz
import xbmc, xbmcgui, xbmcvfs
from urllib.parse import urlencode
from urllib.request import FancyURLopener
transPath = xbmcvfs.translatePath
LOGNOTICE = xbmc.LOGINFO
ADDON_ID = uservar.ADDON_ID
ADDONTITLE = uservar.ADDONTITLE
COLOR1 = uservar.COLOR1
COLOR2 = uservar.COLOR2
ADDON = wiz.addonId(ADDON_ID)
ADDONVERSION = ADDON.getAddonInfo('version')
DIALOG = xbmcgui.Dialog()
URL = 'https://paste.ubuntu.com/'
EXPIRATION = 2592000
REPLACES = (('//.+?:.+?@', '//USER:PASSWORD@'),('.+?', 'USER'),('.+?', 'PASSWORD'),)
HOME = transPath('special://home/')
LOG = transPath('special://logpath/')
USERDATA = os.path.join(HOME, 'userdata')
ADDONDATA = os.path.join(USERDATA, 'addon_data', ADDON_ID)
BUILDERNAME = uservar.BUILDERNAME
WIZLOG = os.path.join(ADDONDATA, 'wizard.log')
socket.setdefaulttimeout(5)
class QRCode(xbmcgui.WindowXMLDialog):
def __init__(self, *args, **kwargs):
self.image = kwargs["image"]
self.text = kwargs["text"]
def onInit(self):
self.imagecontrol = 501
self.textbox = 502
self.okbutton = 503
self.title = 504
self.showdialog()
def showdialog(self):
self.getControl(self.imagecontrol).setImage(self.image)
self.getControl(self.textbox).setText(self.text)
self.getControl(self.title).setLabel(ADDONTITLE)
self.setFocus(self.getControl(self.okbutton))
def onClick(self, controlId):
if (controlId == self.okbutton):
self.close()
# Custom urlopener to set user-agent
class pasteURLopener(FancyURLopener):
version = '%s: %s' % (ADDON_ID, ADDONVERSION)
class Main:
def __init__(self):
self.getSettings()
files = self.getFiles()
for item in files:
filetype = item[0]
if filetype == 'log':
log = wiz.Grab_Log(file=True).replace(LOG, "")
name = log if log != False else "kodi.log"
error = "Error posting the %s file" % name
elif filetype == 'oldlog':
log = wiz.Grab_Log(file=True, old=True).replace(LOG, "")
name = log if log != False else "kodi.old.log"
error = "Error posting the %s file" % name
elif filetype == 'wizlog':
name = "wizard.log"
error = "Error posting the %s file" % name
elif filetype == 'crashlog':
name = "crash log"
error = "Error posting the crashlog file"
succes, data = self.readLog(item[1])
if succes:
content = self.cleanLog(data)
succes, result = self.postLog(content, name)
if succes:
msg = "Post this url or scan QRcode for your [COLOR %s]%s[/COLOR], together with a description of the problem:[CR][COLOR %s]%s[/COLOR]" % (COLOR1, name, COLOR1, result)
# if len(self.email) > 5:
# em_result, em_msg = self.email_Log(self.email, result, name)
# if em_result == 'message':
# msg += "[CR]%s" % em_msg
# else:
# msg += "[CR]Email ERROR: %s" % em_msg
self.showResult(msg, result)
else:
self.showResult('%s[CR]%s' % (error, result))
else:
self.showResult('%s[CR]%s' % (error, result))
def getSettings(self):
self.oldlog = ADDON.getSetting('oldlog') == 'true'
self.wizlog = ADDON.getSetting('wizlog') == 'true'
self.crashlog = ADDON.getSetting('crashlog') == 'true'
self.email = ADDON.getSetting('email')
def getFiles(self):
logfiles = []
log = wiz.Grab_Log(file=True)
old = wiz.Grab_Log(file=True, old=True)
wizard = False if not os.path.exists(WIZLOG) else WIZLOG
if log != False:
if os.path.exists(log): logfiles.append(['log', log])
else: self.showResult("No log file found")
else: self.showResult("No log file found")
if self.oldlog:
if old != False:
if os.path.exists(old): logfiles.append(['oldlog', old])
else: self.showResult("No old log file found")
else: self.showResult("No old log file found")
if self.wizlog:
if wizard != False:
logfiles.append(['wizlog', wizard])
else: self.showResult("No wizard log file found")
if self.crashlog:
crashlog_path = ''
items = []
if xbmc.getCondVisibility('system.platform.osx'):
crashlog_path = os.path.join(os.path.expanduser('~'), 'Library/Logs/DiagnosticReports/')
filematch = 'Kodi'
elif xbmc.getCondVisibility('system.platform.ios'):
crashlog_path = '/var/mobile/Library/Logs/CrashReporter/'
filematch = 'Kodi'
elif wiz.platform() == 'linux':
crashlog_path = os.path.expanduser('~') # not 100% accurate (crashlogs can be created in the dir kodi was started from as well)
filematch = 'kodi_crashlog'
elif wiz.platform() == 'windows':
wiz.log("Windows crashlogs are not supported, please disable this option in the addon settings", LOGNOTICE)
#self.showResult("Windows crashlogs are not supported, please disable this option in the addon settings")
elif wiz.platform() == 'android':
wiz.log("Android crashlogs are not supported, please disable this option in the addon settings", LOGNOTICE)
#self.showResult("Android crashlogs are not supported, please disable this option in the addon settings")
if crashlog_path and os.path.isdir(crashlog_path):
dirs, files = xbmcvfs.listdir(crashlog_path)
for item in files:
if filematch in item and os.path.isfile(os.path.join(crashlog_path, item)):
items.append(os.path.join(crashlog_path, item))
items.sort(key=lambda f: os.path.getmtime(f))
lastcrash = items[-1]
logfiles.append(['crashlog', lastcrash])
if len(items) == 0:
wiz.log("No crashlog file found", LOGNOTICE)
return logfiles
def readLog(self, path):
try:
lf = xbmcvfs.File(path)
content = lf.read()
lf.close()
if content:
return True, content
else:
wiz.log('file is empty', LOGNOTICE)
return False, "File is Empty"
except:
wiz.log('unable to read file', LOGNOTICE)
return False, "Unable to Read File"
def cleanLog(self, content):
for pattern, repl in REPLACES:
content = re.sub(pattern, repl, content)
return content
def postLog(self, data, name):
params = {}
params['poster'] = BUILDERNAME
params['content'] = data
params['syntax'] = 'text'
params['expiration'] = 'week'
params = urlencode(params)
url_opener = pasteURLopener()
try:
page = url_opener.open(URL, params)
except Exception as e:
a = 'failed to connect to the server'
wiz.log("%s: %s" % (a, str(e)), xbmc.LOGERROR)
return False, a
try:
page_url = page.url.strip()
# self.copy2clip(page_url)
wiz.log("URL for %s: %s" % (name, page_url), LOGNOTICE)
return True, page_url
except Exception as e:
a = 'unable to retrieve the paste url'
wiz.log("%s: %s" % (a, str(e)), xbmc.LOGERROR)
return False, a
# def copy2clip(txt):
# platform = sys.platform
# if platform == 'win32':
# try:
# cmd = 'echo ' + txt.strip() + '|clip'
# return subprocess.check_call(cmd, shell=True)
# pass
# except:
# pass
# elif platform == 'linux2':
# try:
# from subprocess import Popen, PIPE
# p = Popen(['xsel', '-pi'], stdin=PIPE)
# p.communicate(input=txt)
# except:
# pass
# else:
# pass
# pass
# def email_Log(self, email, results, file):
# URL = 'http://aftermathwizard.net/mail_logs.php'
# data = {'email': email, 'results': results, 'file': file, 'wizard': ADDONTITLE}
# params = urlencode(data)
# url_opener = pasteURLopener()
# try:
# result = url_opener.open(URL, params)
# returninfo = result.read()
# wiz.log(str(returninfo), LOGNOTICE)
# except Exception as e:
# a = 'failed to connect to the server'
# wiz.log("%s: %s" % (a, str(e)), xbmc.LOGERROR)
# return False, a
# try:
# js_data = json.loads(returninfo)
# if 'type' in js_data:
# return js_data['type'], str(js_data['text'])
# else: return str(js_data)
# except Exception as e:
# wiz.log("ERROR: "+ str(e), xbmc.LOGERROR)
# return "Error Sending Email."
def showResult(self, message, url=None):
if not url == None:
try:
fn = url.split('/')[-2]
imagefile = wiz.generateQR(url, fn)
#imagefile = os.path.join(QRCODES,'%s.png' % fn)
#qrIMG = pyqrcode.create(url)
#qrIMG.png(imagefile, scale=10)
qr = QRCode( "loguploader.xml" , ADDON.getAddonInfo('path'), 'DefaultSkin', image=imagefile, text=message)
qr.doModal()
del qr
try:
xbmcvfs.delete(imagefile)
except:
pass
except Exception as e:
wiz.log(str(e), LOGNOTICE)
confirm = DIALOG.ok(ADDONTITLE, "[COLOR %s]%s[/COLOR]" % (COLOR2, message))
else:
confirm = DIALOG.ok(ADDONTITLE, "[COLOR %s]%s[/COLOR]" % (COLOR2, message))
if ( __name__ == '__main__' ):
Main()