diff --git a/gntp.py b/gntp.py index a49ce64..e370672 100644 --- a/gntp.py +++ b/gntp.py @@ -38,6 +38,29 @@ def set_password(self,password,encryptAlgo='MD5'): self.info['keyHashAlgorithmID'] = encryptAlgo.upper() self.info['keyHash'] = keyHash.upper() self.info['salt'] = salt.upper() + def _decode_hex(self,value): + result = '' + for i in range(0,len(value),2): + tmp = int(value[i:i+2],16) + result += chr(tmp) + return result + def validate_password(self): + if not self.info.get('keyHash',None): + return True + if not self.password: + raise GNTPParseError('Missing Password') + + password = self.password.encode('utf8') + saltHash = self._decode_hex(self.info['salt']) + + keyBasis = password+saltHash + key = hashlib.md5(keyBasis).digest() + keyHash = hashlib.md5(key).hexdigest() + + if not keyHash.upper() == self.info['keyHash'].upper(): + raise GNTPParseError('Invalid Hash') + return True + def format_info(self): info = u'GNTP/%s %s'%( @@ -75,10 +98,11 @@ def add_header(self,key,value): self.headers[key] = value class GNTPRegister(_GNTPBase): - def __init__(self,data=None): + def __init__(self,data=None,password=None): _GNTPBase.__init__(self,'REGISTER') self.headers = {} self.notifications = [] + self.password = password self.requiredHeaders = [ 'Application-Name', 'Notifications-Count' @@ -103,6 +127,7 @@ def decode(self,data): self.raw = data parts = self.raw.split('\r\n\r\n') self.info = self.parse_info(data) + self.validate_password() self.headers = self.parse_dict(parts[0]) if len(parts) > 1: @@ -143,10 +168,11 @@ def __str__(self): return self.encode() class GNTPNotice(_GNTPBase): - def __init__(self,data=None,app=None,name=None,title=None): + def __init__(self,data=None,app=None,name=None,title=None,password=None): _GNTPBase.__init__(self,'NOTIFY') self.headers = {} self.resources = {} + self.password = password self.requiredHeaders = [ 'Application-Name', 'Notification-Name', @@ -169,6 +195,7 @@ def decode(self,data): self.raw = data parts = self.raw.split('\r\n\r\n') self.info = self.parse_info(data) + self.validate_password() self.headers = self.parse_dict(parts[0]) if len(parts) > 1: print 'Extra parts' @@ -235,15 +262,15 @@ def __init__(self,data=None): if data: self.decode(data) -def parse_gntp(data,debug=False): +def parse_gntp(data,password=None,debug=False): match = re.match('GNTP/(?P\d+\.\d+) (?PREGISTER|NOTIFY|\-OK|\-ERROR)',data,re.IGNORECASE) if not match: raise GNTPParseError('INVALID_GNTP_INFO') info = match.groupdict() if info['messagetype'] == 'REGISTER': - return GNTPRegister(data) - elif info['messagetype'] == 'NOTICE': - return GNTPNotice(data) + return GNTPRegister(data,password=password) + elif info['messagetype'] == 'NOTIFY': + return GNTPNotice(data,password=password) elif info['messagetype'] == '-OK': return GNTPResponse(data) elif info['messagetype'] == '-ERROR': @@ -252,4 +279,5 @@ def parse_gntp(data,debug=False): print '----' print self.data print '----' + print info raise GNTPParseError('INVALID_GNTP_MESSAGE') diff --git a/local.py b/local.py index 4b4ed5f..5d8f3e2 100644 --- a/local.py +++ b/local.py @@ -1,6 +1,11 @@ import gntp import Growl +GNTPParseError = gntp.GNTPParseError +GNTPOK = gntp.GNTPOK +GNTPError = gntp.GNTPError +parse_gntp = gntp.parse_gntp + class GNTPRegister(gntp.GNTPRegister): def send(self): print 'Sending Local Registration' diff --git a/server.py b/server.py index 6661c02..347c18e 100644 --- a/server.py +++ b/server.py @@ -22,15 +22,15 @@ def handle(self): self.data = self.read() try: - message = parse_gntp(self.data) + message = gntp.parse_gntp(self.data,self.server.growl_password) message.send() - response = GNTPOK() + response = gntp.GNTPOK() self.write(response.encode()) - except GNTPError: + except gntp.GNTPError: if self.server.growl_debug: traceback.print_exc() - response = GNTPError() + response = gntp.GNTPError() self.write(response.encode()) if __name__ == "__main__": @@ -40,16 +40,17 @@ def handle(self): parser.add_option("-p","--port",dest="port",help="port to listen on",type="int",default=23053) parser.add_option("-r","--regrowl",dest='regrowl',help="ReGrowl on local OSX machine",action="store_true",default=False) parser.add_option("-d","--debug",dest='debug',help="Print raw growl packets",action="store_true",default=False) + parser.add_option("-P","--password",dest='password',help="Network password",default=None) (options, args) = parser.parse_args() if options.regrowl: - from local import GNTPRegister,GNTPNotice - from gntp import GNTPParseError,GNTPOK,GNTPError,parse_gntp + import local as gntp else: - from gntp import * + import gntp server = GNTPServer((options.host, options.port), GNTPHandler) server.growl_debug = options.debug + server.growl_password = options.password sa = server.socket.getsockname() print "Listening for GNTP on", sa[0], "port", sa[1], "..."