diff --git a/NSIS_Installer.nsi b/NSIS_Installer.nsi index c0c6714..7f5fae1 100644 --- a/NSIS_Installer.nsi +++ b/NSIS_Installer.nsi @@ -110,6 +110,7 @@ InstallDirRegKey HKEY_LOCAL_MACHINE "SOFTWARE\SABnzbd" "" !insertmacro MUI_LANGUAGE "German" !insertmacro MUI_LANGUAGE "Dutch" !insertmacro MUI_LANGUAGE "Swedish" + !insertmacro MUI_LANGUAGE "Danish" ;-------------------------------- @@ -234,11 +235,13 @@ Section "un.$(MsgDelProgram)" Uninstall Delete "$INSTDIR\language\email-nl-du.tmpl" Delete "$INSTDIR\language\email-fr-fr.tmpl" Delete "$INSTDIR\language\email-sv-se.tmpl" + Delete "$INSTDIR\language\email-dk-da.tmpl" Delete "$INSTDIR\language\de-de.txt" Delete "$INSTDIR\language\us-en.txt" Delete "$INSTDIR\language\nl-du.txt" Delete "$INSTDIR\language\fr-fr.txt" Delete "$INSTDIR\language\sv-se.txt" + Delete "$INSTDIR\language\dk-da.txt" RMDir "$INSTDIR\language" RMDir /r "$INSTDIR\interfaces\Classic" RMDir /r "$INSTDIR\interfaces\Plush" @@ -326,78 +329,91 @@ SectionEnd LangString MsgStartSab ${LANG_FRENCH} "Lancer SABnzbd (caché)" LangString MsgStartSab ${LANG_GERMAN} "SABnzbd starten (unsichtbar)" LangString MsgStartSab ${LANG_SWEDISH} "Starta SABnzbd (dold)" + LangString MsgStartSab ${LANG_DANISH} "Start SABnzbd (hidden)" LangString MsgShowRelNote ${LANG_ENGLISH} "Show Release Notes" LangString MsgShowRelNote ${LANG_DUTCH} "Toon Vrijgave Bericht (Engels)" LangString MsgShowRelNote ${LANG_FRENCH} "Afficher les notes de version" LangString MsgShowRelNote ${LANG_GERMAN} "Versionshinweise anzeigen" LangString MsgShowRelNote ${LANG_SWEDISH} "Visa release noteringar" + LangString MsgShowRelNote ${LANG_DANISH} "Show Release Notes" LangString MsgSupportUs ${LANG_ENGLISH} "Support the project, Donate!" LangString MsgSupportUs ${LANG_DUTCH} "Steun het project, Doneer!" LangString MsgSupportUs ${LANG_FRENCH} "Supportez le projet, faites un don !" LangString MsgSupportUs ${LANG_GERMAN} "Bitte unterstützen Sie das Projekt durch eine Spende!" LangString MsgSupportUs ${LANG_SWEDISH} "Donera och stöd detta projekt!" + LangString MsgSupportUs ${LANG_DANISH} "Support the project, Donate!" LangString MsgCloseSab ${LANG_ENGLISH} "Please close $\"SABnzbd.exe$\" first" LangString MsgCloseSab ${LANG_DUTCH} "Sluit $\"SABnzbd.exe$\" eerst af" LangString MsgCloseSab ${LANG_FRENCH} "Quittez $\"SABnzbd.exe$\" avant l\'installation, SVP" LangString MsgCloseSab ${LANG_GERMAN} "Schliessen Sie bitte zuerst $\"SABnzbd.exe$\"." LangString MsgCloseSab ${LANG_SWEDISH} "Var vänlig stäng $\"SABnzbd.exe$\" först" + LangString MsgCloseSab ${LANG_DANISH} "Please close $\"SABnzbd.exe$\" first" LangString MsgOldQueue ${LANG_ENGLISH} " >>>> WARNING <<<<$\r$\n$\r$\nIf not empty, download your current queue with the old program.$\r$\nThe new program will ignore your current queue!" LangString MsgOldQueue ${LANG_DUTCH} " >>>> WAARSCHUWING <<<<$\r$\n$\r$\nIndien niet leeg, download eerst de gehele huidige wachtrij met het oude programma.$\r$\nHet nieuwe programma zal je huidige wachtrij negeren!" LangString MsgOldQueue ${LANG_FRENCH} " >>>> ATTENTION <<<<$\r$\n$\r$\nsi votre file d'attente de téléchargement n'est pas vide, terminez la avec la version précédente du programme.$\r$\nLa nouvelle version l'ignorera!" LangString MsgOldQueue ${LANG_GERMAN} " >>>> ACHTUNG <<<<$\r$\n$\r$\nWarten Sie, bis das alte Programm alle Downloads fertiggestellt hat.$\r$\nDas neue Programm wird die noch ausstehenden Downloads ignorieren!" LangString MsgOldQueue ${LANG_SWEDISH} " >>>> VARNING <<<<$\r$\n$\r$\nOm kön inte är tom, hämta din nuvarande kö med det gamla programmet.$\r$\nDet nya programmet kommer att ignorera din nuvarande kö!" + LangString MsgOldQueue ${LANG_DANISH} " >>>> WARNING <<<<$\r$\n$\r$\nIf not empty, download your current queue with the old program.$\r$\nThe new program will ignore your current queue!" LangString MsgUninstall ${LANG_ENGLISH} "This will uninstall SABnzbd from your system" LangString MsgUninstall ${LANG_DUTCH} "Dit verwijdert SABnzbd van je systeem" LangString MsgUninstall ${LANG_FRENCH} "Ceci désinstallera SABnzbd de votre système" LangString MsgUninstall ${LANG_GERMAN} "Dies entfernt SABnzbd von Ihrem System" LangString MsgUninstall ${LANG_SWEDISH} "Detta kommer att avinstallera SABnzbd från systemet" + LangString MsgUninstall ${LANG_DANISH} "This will uninstall SABnzbd from your system" LangString MsgRunAtStart ${LANG_ENGLISH} "Run at startup" LangString MsgRunAtStart ${LANG_DUTCH} "Opstarten bij systeem start" LangString MsgRunAtStart ${LANG_FRENCH} "Lancer au démarrage" LangString MsgRunAtStart ${LANG_GERMAN} "Beim Systemstart ausführen" LangString MsgRunAtStart ${LANG_SWEDISH} "Kör vid uppstart" + LangString MsgRunAtStart ${LANG_DANISH} "Run at startup" LangString MsgIcon ${LANG_ENGLISH} "Desktop Icon" LangString MsgIcon ${LANG_DUTCH} "Pictogram op bureaublad" LangString MsgIcon ${LANG_FRENCH} "Icône sur le Bureau" LangString MsgIcon ${LANG_GERMAN} "Desktop-Symbol" LangString MsgIcon ${LANG_SWEDISH} "Skrivbordsikon" + LangString MsgIcon ${LANG_DANISH} "Desktop Icon" LangString MsgAssoc ${LANG_ENGLISH} "NZB File association" LangString MsgAssoc ${LANG_DUTCH} "NZB bestanden koppelen aan SABnzbd" LangString MsgAssoc ${LANG_FRENCH} "Association des fichiers NZB" LangString MsgAssoc ${LANG_GERMAN} "Mit NZB-Dateien verknüpfen" LangString MsgAssoc ${LANG_SWEDISH} "NZB Filassosication" + LangString MsgAssoc ${LANG_DANISH} "NZB File association" LangString MsgDelProgram ${LANG_ENGLISH} "Delete Program" LangString MsgDelProgram ${LANG_DUTCH} "Verwijder programma" LangString MsgDelProgram ${LANG_FRENCH} "Supprimer le programme" LangString MsgDelProgram ${LANG_GERMAN} "Programm löschen" LangString MsgDelProgram ${LANG_SWEDISH} "Ta bort programmet" + LangString MsgDelProgram ${LANG_DANISH} "Delete Program" LangString MsgDelSettings ${LANG_ENGLISH} "Delete Settings" LangString MsgDelSettings ${LANG_DUTCH} "Verwijder instellingen" LangString MsgDelSettings ${LANG_FRENCH} "Supprimer Paramètres" LangString MsgDelSettings ${LANG_GERMAN} "Einstellungen löschen" LangString MsgDelSettings ${LANG_SWEDISH} "Ta bort inställningar" + LangString MsgDelSettings ${LANG_DANISH} "Delete Settings" LangString MsgDelLogs ${LANG_ENGLISH} "Delete Logs" LangString MsgDelLogs ${LANG_DUTCH} "Verwijder logging" LangString MsgDelLogs ${LANG_FRENCH} "Supprimer les logs" LangString MsgDelLogs ${LANG_GERMAN} "Protokoll löschen" LangString MsgDelLogs ${LANG_SWEDISH} "Ta bort logg" + LangString MsgDelLogs ${LANG_DANISH} "Delete Logs" LangString MsgDelCache ${LANG_ENGLISH} "Delete Cache" LangString MsgDelCache ${LANG_DUTCH} "Verwijder Cache" LangString MsgDelCache ${LANG_FRENCH} "Supprimer le cache" LangString MsgDelCache ${LANG_GERMAN} "Cache löschen" LangString MsgDelCache ${LANG_SWEDISH} "Ta bort temporär-mapp" + LangString MsgDelCache ${LANG_DANISH} "Delete Cache" Function un.onInit !insertmacro MUI_UNGETLANGUAGE diff --git a/SABHelper.py b/SABHelper.py new file mode 100644 index 0000000..d878fa9 --- /dev/null +++ b/SABHelper.py @@ -0,0 +1,124 @@ +#!/usr/bin/python -OO +# Copyright 2008-2010 The SABnzbd-Team +# +# 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 +# of the License, 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 this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +import sys +if sys.version_info < (2,4): + print "Sorry, requires Python 2.4 or higher." + sys.exit(1) + +import os +import time +import subprocess + +#------------------------------------------------------------------------------ +try: + import win32api, win32file + import win32serviceutil, win32evtlogutil, win32event, win32service, pywintypes +except ImportError: + print "Sorry, requires Python module PyWin32." + sys.exit(1) + +from util.mailslot import MailSlot + +#------------------------------------------------------------------------------ + +WIN_SERVICE = None + +#------------------------------------------------------------------------------ +def HandleCommandLine(allow_service=True): + """ Handle command line for a Windows Service + Prescribed name that will be called by Py2Exe. + You MUST set 'cmdline_style':'custom' in the package.py! + Returns True when any service commands were detected. + """ + win32serviceutil.HandleCommandLine(SABHelper) + + +#------------------------------------------------------------------------------ +def main(): + + mail = MailSlot() + if not mail.create(200): + return '- Cannot create Mailslot' + + while True: + msg = mail.receive() + if msg == 'restart': + time.sleep(1.0) + res = subprocess.Popen('net start SABnzbd', stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True).stdout.read() + rc = win32event.WaitForMultipleObjects((WIN_SERVICE.hWaitStop, + WIN_SERVICE.overlapped.hEvent), 0, 500) + if rc == win32event.WAIT_OBJECT_0: + mail.disconnect() + return '' + + +##################################################################### +# +# Windows Service Support +# +import servicemanager +class SABHelper(win32serviceutil.ServiceFramework): + """ Win32 Service Handler """ + + _svc_name_ = 'SABHelper' + _svc_display_name_ = 'SABnzbd Helper' + _svc_deps_ = ["EventLog", "Tcpip"] + _svc_description_ = 'Automated downloading from Usenet. ' \ + 'This service helps SABnzbdcd.. to restart itself.' + + def __init__(self, args): + global WIN_SERVICE + win32serviceutil.ServiceFramework.__init__(self, args) + + self.hWaitStop = win32event.CreateEvent(None, 0, 0, None) + self.overlapped = pywintypes.OVERLAPPED() + self.overlapped.hEvent = win32event.CreateEvent(None, 0, 0, None) + WIN_SERVICE = self + + def SvcDoRun(self): + msg = 'SABHelper-service' + self.Logger(servicemanager.PYS_SERVICE_STARTED, msg + ' has started') + res = main() + self.Logger(servicemanager.PYS_SERVICE_STOPPED, msg + ' has stopped' + res) + + def SvcStop(self): + self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING) + win32event.SetEvent(self.hWaitStop) + + def Logger(self, state, msg): + win32evtlogutil.ReportEvent(self._svc_display_name_, + state, 0, + servicemanager.EVENTLOG_INFORMATION_TYPE, + (self._svc_name_, unicode(msg))) + + def ErrLogger(self, msg, text): + win32evtlogutil.ReportEvent(self._svc_display_name_, + servicemanager.PYS_SERVICE_STOPPED, 0, + servicemanager.EVENTLOG_ERROR_TYPE, + (self._svc_name_, unicode(msg)), + unicode(text)) + + + +##################################################################### +# +# Platform specific startup code +# +if __name__ == '__main__': + + win32serviceutil.HandleCommandLine(SABHelper, argv=sys.argv) diff --git a/SABnzbd.py b/SABnzbd.py index 4d0225c..eaa4c94 100755 --- a/SABnzbd.py +++ b/SABnzbd.py @@ -97,7 +97,10 @@ try: import win32api import win32serviceutil, win32evtlogutil, win32event, win32service, pywintypes win32api.SetConsoleCtrlHandler(sabnzbd.sig_handler, True) + from util.mailslot import MailSlot except ImportError: + class MailSlot: + pass if sabnzbd.WIN32: print "Sorry, requires Python module PyWin32." sys.exit(1) @@ -539,7 +542,9 @@ def get_webhost(cherryhost, cherryport, https_port): if cherryport == https_port: sabnzbd.cfg.enable_https.set(False) - logging.error(Ta('error-sameHTTP-HTTPS')) + # Should have a translated message, but that's not available yet + #logging.error(Ta('error-sameHTTP-HTTPS')) + logging.error('HTTP and HTTPS ports cannot be the same') return cherryhost, cherryport, browserhost, https_port @@ -556,15 +561,16 @@ def is_sabnzbd_running(url): except: return False -def find_free_port(host, currentport, i=0): - while i >=10 and currentport <= 49151: +def find_free_port(host, currentport): + n = 0 + while n < 10 and currentport <= 49151: try: cherrypy.process.servers.check_port(host, currentport) return currentport except: - currentport+=5 - i+=1 - return -1 + currentport += 5 + n += 1 + return 0 def check_for_sabnzbd(url, upload_nzbs): # Check for a running instance of sabnzbd(same version) on this port @@ -923,7 +929,7 @@ def main(): port = find_free_port(browserhost, https_port) if port > 0: sabnzbd.cfg.https_port.set(port) - cherryport = port + https_port = port ## NonSSL try: cherrypy.process.servers.check_port(browserhost, cherryport) @@ -1194,7 +1200,8 @@ def main(): exit_sab(2) else: logging.debug("Failed to start web-interface: ", exc_info = True) - Bail_Out(browserhost, cherryport) + # When error 13 occurs, we have no access rights + Bail_Out(browserhost, cherryport, '13' in str(error)) except socket.error, error: logging.debug("Failed to start web-interface: ", exc_info = True) Bail_Out(browserhost, cherryport, access=True) @@ -1283,8 +1290,14 @@ def main(): if pid == 0: os.execv(sys.executable, args) elif sabnzbd.WIN_SERVICE: - # Hope for the service manager to restart us - sys.exit(1) + logging.info('Asking the SABnzbdHelper service for a restart') + mail = MailSlot() + if mail.connect(): + mail.send('restart') + mail.disconnect() + else: + logging.error('Cannot reach the SABnzbdHelper service') + return else: cherrypy.engine._do_execv() @@ -1313,7 +1326,7 @@ if sabnzbd.WIN32: _svc_name_ = 'SABnzbd' _svc_display_name_ = 'SABnzbd Binary Newsreader' - _svc_deps_ = ["EventLog", "Tcpip"] + _svc_deps_ = ["EventLog", "Tcpip", "SABHelper"] _svc_description_ = 'Automated downloading from Usenet. ' \ 'Set to "automatic" to start the service at system startup. ' \ 'You may need to login with a real user account when you need ' \ @@ -1370,6 +1383,13 @@ def prep_service_parms(args): return serv +SERVICE_MSG = """ +You may need to set additional Service parameters. +Run services.msc from a command prompt. + +Don't forget to install the Service SABnzbd-helper.exe too! +""" + def HandleCommandLine(allow_service=True): """ Handle command line for a Windows Service Prescribed name that will be called by Py2Exe. @@ -1397,8 +1417,7 @@ def HandleCommandLine(allow_service=True): # Add our own parameter to the Registry sab_opts = prep_service_parms(sab_opts) if set_serv_parms(SABnzbd._svc_name_, sab_opts): - print '\nYou may need to set additional Service parameters.\n' \ - 'Run services.msc from a command prompt.\n' + print SERVICE_MSG else: print 'Cannot set required Registry info.' else: diff --git a/interfaces/Classic/templates/config_email.tmpl b/interfaces/Classic/templates/config_email.tmpl index 689e8c5..71abb30 100644 --- a/interfaces/Classic/templates/config_email.tmpl +++ b/interfaces/Classic/templates/config_email.tmpl @@ -11,9 +11,9 @@
$T('emailOptions') $T('opt-email_endjob')
- /> $T('email-never') - /> $T('email-always') - /> $T('email-errorOnly') + /> $T('email-never') + /> $T('email-always') + /> $T('email-errorOnly')


$T('explain-email_full')
diff --git a/interfaces/Plush/templates/config_email.tmpl b/interfaces/Plush/templates/config_email.tmpl index 2c2df86..c6d38b4 100644 --- a/interfaces/Plush/templates/config_email.tmpl +++ b/interfaces/Plush/templates/config_email.tmpl @@ -11,9 +11,9 @@   $T('emailOptions') $T('opt-email_endjob'):
- /> $T('email-never') - /> $T('email-always') - /> $T('email-errorOnly') + /> $T('email-never') + /> $T('email-always') + /> $T('email-errorOnly')

0 then "checked=1" else ""#--> /> diff --git a/interfaces/smpl/templates/config_email.tmpl b/interfaces/smpl/templates/config_email.tmpl index 5e14021..f687223 100644 --- a/interfaces/smpl/templates/config_email.tmpl +++ b/interfaces/smpl/templates/config_email.tmpl @@ -8,9 +8,9 @@ $T('opt-email_endjob'): - /> $T('email-never') - /> $T('email-always') - /> $T('email-errorOnly') + /> $T('email-never') + /> $T('email-always') + /> $T('email-errorOnly') $T('opt-email_endjob')
diff --git a/language/email-fr-fr.tmpl b/language/email-fr-fr.tmpl index d0e14aa..3ffa9a2 100644 --- a/language/email-fr-fr.tmpl +++ b/language/email-fr-fr.tmpl @@ -1,3 +1,4 @@ +#encoding UTF-8 ## ## Template Email pour SABnzbd ## Ceci est un template Cheetah @@ -9,7 +10,7 @@ to: $to from: $from date: $date -subject: SABnzbd du téléchargement $name +subject: SABnzbd SuccèsEchec du téléchargement $name X-priority: 5 X-MS-priority: 5 ## Le contenu du message, la ligne vide est obligatoire ! diff --git a/package.py b/package.py index 0970049..7236650 100644 --- a/package.py +++ b/package.py @@ -243,7 +243,7 @@ else: target = sys.argv[1] if target not in ('source', 'binary', 'installer', 'app'): - print 'Usage: package.py binary|source|app' + print 'Usage: package.py binary|installer|source|app' exit(1) # Derive release name from path @@ -251,8 +251,10 @@ base, release = os.path.split(os.getcwd()) prod = 'SABnzbd-' + release Win32ServiceName = 'SABnzbd-service.exe' +Win32ServiceHelpName = 'SABnzbd-helper.exe' Win32ConsoleName = 'SABnzbd-console.exe' Win32WindowName = 'SABnzbd.exe' +Win32HelperName = 'SABHelper.exe' Win32TempName = 'SABnzbd-windows.exe' fileIns = prod + '-win32-setup.exe' @@ -298,7 +300,7 @@ options = dict( author = 'The SABnzbd-Team', author_email = 'team@sabnzbd.org', #description = 'SABnzbd ' + str(sabnzbd.__version__), - scripts = ['SABnzbd.py'], # One day, add 'setup.py' + scripts = ['SABnzbd.py', 'SABHelper.py'], # One day, add 'setup.py' packages = ['sabnzbd', 'sabnzbd.utils'], platforms = ['posix'], license = 'GNU General Public License 2 (GPL2) or later', @@ -425,7 +427,7 @@ elif target in ('binary', 'installer'): options['options'] = {"py2exe": { "bundle_files": 3, - "packages": "email,xml,Cheetah", + "packages": "email,xml,Cheetah,win32file", "excludes": ["pywin", "pywin.debugger", "pywin.debugger.dbgcon", "pywin.dialogs", "pywin.dialogs.list", "Tkconstants", "Tkinter", "tcl"], "optimize": 2, @@ -434,6 +436,7 @@ elif target in ('binary', 'installer'): } options['zipfile'] = 'lib/sabnzbd.zip' + options['scripts'] = ['SABnzbd.py'] ############################ # Generate the console-app @@ -471,6 +474,20 @@ elif target in ('binary', 'installer'): ############################ + # Generate the Helper service-app + options['scripts'] = ['SABHelper.py'] + options['zipfile'] = 'lib/sabhelper.zip' + options['service'] = [{'modules':["SABHelper"], 'cmdline_style':'custom'}] + options['packages'] = ['util'] + options['data_files'] = [] + options['options']['py2exe']['packages'] = "win32file" + + setup(**options) + rename_file('dist', Win32HelperName, Win32ServiceHelpName) + + + + ############################ if target == 'installer': os.system('makensis.exe /v3 /DSAB_PRODUCT=%s /DSAB_FILE=%s NSIS_Installer.nsi' % \ diff --git a/sabnzbd/emailer.py b/sabnzbd/emailer.py index ccff1f4..8b09c07 100644 --- a/sabnzbd/emailer.py +++ b/sabnzbd/emailer.py @@ -47,6 +47,8 @@ def send(message): if cfg.email_server() and cfg.email_to() and cfg.email_from(): + message = _prepare_message(message) + failure = T('error-mailSend') server, port = split_host(cfg.email_server()) if not port: @@ -99,10 +101,7 @@ def send(message): return failure try: - if isinstance(message, unicode): - message = message.encode('utf8') - for recipient in cfg.email_to(): - mailconn.sendmail(cfg.EMAIL_FROM.get(), recipient, message) + mailconn.sendmail(cfg.email_from(), cfg.email_to(), message) except: logging.error(Ta('error-mailSend')) return failure @@ -222,3 +221,50 @@ def _decode_file(path): fp.close() return source.decode(encoding) + + +################################################################################ +from email.message import Message +from email.header import Header +from email.encoders import encode_quopri +RE_HEADER = re.compile(r'^([^:]+):(.*)') + +def _prepare_message(txt): + """ Do the proper message encodings + """ + def plain(val): + """ Return True when val is plain ASCII """ + try: + val.decode('ascii') + return True + except: + return False + + # Use Latin-1 because not all email clients know UTF-8. + code = 'ISO-8859-1' + + msg = Message() + msg.set_charset(code) + payload = [] + body = False + for line in txt.encode(code, 'replace').split('\n'): + if not line: + body = True + if body: + payload.append(line) + else: + m = RE_HEADER.search(line) + if m: + keyword = m.group(1).strip() + value = m.group(2).strip() + if plain(value): + # Don't encode if not needed, because some email clients + # choke when headers like "date" are encoded. + msg.add_header(keyword, value) + else: + header = Header(value, code) + msg[keyword] = header + + msg.set_payload('\n'.join(payload), code) + encode_quopri(msg) + return msg.as_string() diff --git a/sabnzbd/encoding.py b/sabnzbd/encoding.py index 2d0ae09..b893f67 100644 --- a/sabnzbd/encoding.py +++ b/sabnzbd/encoding.py @@ -180,12 +180,12 @@ class LatinFilter(Filter): if isinstance(val, unicode): return val elif isinstance(val, basestring): - if gUTF: - try: - return val.decode('utf-8', 'replace') - except: - return val.decode('latin-1', 'replace') - else: + try: + if sabnzbd.WIN32: + return val.decode('latin-1') + else: + return val.decode('utf-8') + except: return val.decode('latin-1', 'replace') elif val is None: return u'' diff --git a/sabnzbd/interface.py b/sabnzbd/interface.py index 0611740..4df5390 100644 --- a/sabnzbd/interface.py +++ b/sabnzbd/interface.py @@ -2558,9 +2558,9 @@ class ConnectionInfo: pack['download'] = ['action 1', 'action 2'] pack['unpack'] = ['action 1', 'action 2'] - self.__lastmail= emailer.endjob('Test Job', 123, 'unknown', True, - os.path.normpath(os.path.join(cfg.complete_dir.get_path(), '/unknown/Test Job')), - str(123*MEBI), pack, 'my_script', 'Line 1\nLine 2\nLine 3\n', 0) + self.__lastmail= emailer.endjob('I had a d\xe8ja vu', 123, 'unknown', True, + os.path.normpath(os.path.join(cfg.COMPLETE_DIR.get_path(), '/unknown/I had a d\xe8ja vu')), + str(123*MEBI), pack, 'my_script', 'Line 1\nLine 2\nLine 3\nd\xe8ja vu\n', 0) raise dcRaiser(self.__root, kwargs) @cherrypy.expose diff --git a/sabnzbd/misc.py b/sabnzbd/misc.py index fe0445f..3699ac6 100644 --- a/sabnzbd/misc.py +++ b/sabnzbd/misc.py @@ -476,8 +476,8 @@ MSG_BAD_PORT = r''' MSG_ILL_PORT = r''' SABnzbd needs a free tcp/ip port for its internal web server.
- Port %s on %s was tried , but the account SABnzbd has no permission to use it.
- On Linux systems, normal users must use ports above 1023.
+ Port %s on %s was tried , but the account used for SABnzbd has no permission to use it.
+ On OSX and Linux systems, normal users must use ports above 1023.

Please restart SABnzbd with a different port number.

diff --git a/sabnzbd/newsunpack.py b/sabnzbd/newsunpack.py index 0f9a3d3..23d4fa5 100644 --- a/sabnzbd/newsunpack.py +++ b/sabnzbd/newsunpack.py @@ -121,6 +121,7 @@ def external_processing(extern_proc, complete_dir, filename, msgid, nicename, ca str(nicename), str(msgid), str(cat), str(group), str(status)] stup, need_shell, command, creationflags = build_command(command) + env = fix_env() logging.info('Running external script %s(%s, %s, %s, %s, %s, %s, %s)', \ extern_proc, complete_dir, filename, nicename, msgid, cat, group, status) @@ -128,7 +129,7 @@ def external_processing(extern_proc, complete_dir, filename, msgid, nicename, ca try: p = subprocess.Popen(command, shell=need_shell, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, - startupinfo=stup, creationflags=creationflags) + startupinfo=stup, env=env, creationflags=creationflags) except: logging.debug("Failed script %s, Traceback: ", extern_proc, exc_info = True) return "Cannot run script %s\r\n" % extern_proc, -1 @@ -1016,30 +1017,20 @@ def PAR_Verify(parfile, parfile_nzf, nzo, setname, joinables, classic=False): #------------------------------------------------------------------------------- -_RE_PYTHON = re.compile(r'^#!(.*/python)\s+(.*)$') - -def fix_python_script(command): - """ Implement a work-around for Python userscripts on OSX """ - try: - fp = open(command[0], 'r') - line = fp.readline(100) - fp.close() - m = _RE_PYTHON.search(line) - if m: - # Work-around for the incorrect Python paths passed - # by the OSX.app to Python scripts. - # Run the Python interpreter directly and insert the -E parameter - command.insert(0, m.group(2)) - command.insert(0, '-E') - command.insert(0, m.group(1)) - except IOError: - pass +def fix_env(): + """ OSX: Return copy of environment without PYTHONPATH and PYTHONHOME + other: return None + """ + if sabnzbd.DARWIN: + env = os.environ.copy() + if 'PYTHONPATH' in env: del env['PYTHONPATH'] + if 'PYTHONHOME' in env: del env['PYTHONHOME'] + return env + else: + return None def build_command(command): - if sabnzbd.DARWIN: - fix_python_script(command) - if not sabnzbd.WIN32: if IONICE_COMMAND and cfg.ionice().strip(): lst = cfg.ionice().split() diff --git a/sabnzbd/tvsort.py b/sabnzbd/tvsort.py index 24269ce..2cf3c9f 100644 --- a/sabnzbd/tvsort.py +++ b/sabnzbd/tvsort.py @@ -328,6 +328,9 @@ class SeriesSorter: # Lowercase all characters encased in {} path = toLowercase(path) + # Strip any extra ' ' '.' or '_' around foldernames + path = stripFolders(path) + # Split the last part of the path up for the renamer if extension: head, tail = os.path.split(path) diff --git a/util/__init__.py b/util/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/util/mailslot.py b/util/mailslot.py new file mode 100644 index 0000000..874bcc8 --- /dev/null +++ b/util/mailslot.py @@ -0,0 +1,130 @@ +#!/usr/bin/python -OO +# Copyright 2008-2010 The SABnzbd-Team +# +# 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 +# of the License, 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 this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +""" +sabnzbd.mailslot - Mailslot communication +""" + +import os +from win32file import GENERIC_WRITE, FILE_SHARE_READ, \ + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL +from ctypes import c_uint, c_buffer, byref, sizeof, windll + +# Win32API Shortcuts +CreateFile = windll.kernel32.CreateFileA +ReadFile = windll.kernel32.ReadFile +WriteFile = windll.kernel32.WriteFile +CloseHandle = windll.kernel32.CloseHandle +CreateMailslot = windll.kernel32.CreateMailslotA + + +class MailSlot(object): + """ Simple Windows Mailslot communication + """ + slotname = r'mailslot\SABnzbd\ServiceSlot' + + def __init__(self): + self.handle = -1 + + def create(self, timeout): + """ Create the Mailslot, after this only receiving is possible + timeout is the read timeout used for receive calls. + """ + slot = r'\\.\%s' % MailSlot.slotname + self.handle = CreateMailslot(slot, 50, timeout, None) + + return self.handle != -1 + + def connect(self): + """ Connect to existing Mailslot so that writing is possible + """ + slot = r'\\%s\%s' % (os.environ['COMPUTERNAME'], MailSlot.slotname) + self.handle = CreateFile(slot, GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0) + return self.handle != -1 + + def disconnect(self): + """ Disconnect from Mailslot + """ + if self.handle != -1: + CloseHandle(self.handle) + self.handle = -1 + return True + + def send(self, command): + """ Send one message to Mailslot + """ + if self.handle == -1: + return False + w = c_uint() + return bool(WriteFile(self.handle, command, len(command), byref(w), 0)) + + def receive(self): + """ Receive one message from Mailslot + """ + r = c_uint() + buf = c_buffer(1024) + if ReadFile(self.handle, buf, sizeof(buf), byref(r), 0): + return buf.value + else: + return None + + +#------------------------------------------------------------------------------ +# Simple test +# +# First start "mailslot.py server" in one process, +# Then start "mailslot.py client" in another. +# Five "restart" and one "stop" will be send from client to server. +# The server will stop after receiving "stop" + +if __name__ == '__main__': + import sys + from time import sleep + + if not __debug__: + print 'Run this test in non-optimized mode' + exit(1) + + if len(sys.argv) > 1 and 'server' in sys.argv[1]: + + recv = MailSlot() + ret = recv.create(2) + assert ret, 'Failed to create' + while True: + data = recv.receive() + if data is not None: + print data + if data.startswith('stop'): + break + sleep(2.0) + recv.disconnect() + + elif len(sys.argv) > 1 and 'client' in sys.argv[1]: + + send = MailSlot() + ret = send.connect() + assert ret, 'Failed to connect' + for n in xrange(5): + ret = send.send('restart') + assert ret, 'Failed to send' + sleep(2.0) + send.send('stop') + assert ret, 'Failed to send' + send.disconnect() + + else: + print 'Usage: mailslot.py server|client'