update some syntax and make the code more future-proof ;)
[enigma2-plugins.git] / growlee / src / GrowlTalk.py
1 from __future__ import print_function
2
3 from twisted.internet.protocol import DatagramProtocol
4 from twisted.internet import reactor
5 from struct import pack, unpack
6 from hashlib import md5
7
8 from Screens.MessageBox import MessageBox
9 from Tools import Notifications
10
11 from GrowleeConnection import emergencyDisable
12 from . import NOTIFICATIONID
13
14 GROWL_UDP_PORT = 9887
15
16 class GrowlTalk(DatagramProtocol):
17         addr = None
18
19         def __init__(self, host):
20                 self.host = host
21
22         def gotIP(self, ip):
23                 self.addr = (ip, GROWL_UDP_PORT)
24                 if self.host.enable_outgoing.value:
25                         p = pack("!BBHBB",
26                                         1, # version
27                                         0, # registration
28                                         7, # length of application name == len("growlee")
29                                         1, # one notification
30                                         1, # one of them default
31                         )
32                         p += "growlee" # application name
33                         p += pack("!H",
34                                         32, # length of first notification type name
35                         )
36                         p += "Notifications from your Dreambox" # first notification type name
37                         p += "\x00" # index of default notifications
38
39                         password = self.host.password.value
40                         checksum = md5()
41                         checksum.update(p)
42                         if password:
43                                 checksum.update(password)
44                         p += checksum.digest()
45
46                         self.transport.write(p, self.addr)
47
48         def noIP(self, error):
49                 print("--------------------------------", error)
50                 emergencyDisable()
51
52         def startProtocol(self):
53                 reactor.resolve(self.host.address.value).addCallback(self.gotIP).addErrback(self.noIP)
54
55         def sendNotification(self, title='No title.', description='No description.', flags=0):
56                 if not self.transport or not self.addr or not self.host.enable_outgoing.value:
57                         return
58
59                 p = pack("!BBHHHHH",
60                                 1, # version
61                                 1, # notification
62                                 flags, # 3-bit signed priority, 1 bit sticky in rightmost nibble
63                                 32, # len("Notifications from your Dreambox")
64                                 len(title),
65                                 len(description),
66                                 7, # len("growlee")
67                 )
68                 p += "Notifications from your Dreambox"
69                 p += title
70                 p += description
71                 p += "growlee"
72
73                 password = self.host.password.value
74                 checksum = md5()
75                 checksum.update(p)
76                 if password:
77                         checksum.update(password)
78                 p += checksum.digest()
79
80                 self.transport.write(p, self.addr)
81
82         def datagramReceived(self, data, addr):
83                 if not self.host.enable_incoming.value:
84                         return
85
86                 Len = len(data)
87                 if Len < 16:
88                         return
89
90                 # ver == GROWL_PROTOCOL_VERSION
91                 if data[0] != '\x01':
92                         return
93
94                 # type == GROWL_TYPE_NOTIFICATION
95                 if data[1] == '\x01':
96                         digest = data[-16:]
97                         password = self.host.password.value
98                         checksum = md5()
99                         checksum.update(data[:-16])
100                         if password:
101                                 checksum.update(password)
102                         if digest != checksum.digest():
103                                 return
104
105                         nlen, tlen, dlen, alen = unpack("!HHHH",str(data[4:12]))
106                         notification, title, description = unpack("%ds%ds%ds" % (nlen, tlen, dlen), data[12:Len-alen-16])
107                 # type == GROWL_TYPE_NOTIFICATION_NOAUTH
108                 elif data[1] == '\x05':
109                         nlen, tlen, dlen, alen = unpack("!HHHH",str(data[4:12]))
110                         notification, title, description = unpack("%ds%ds%ds" % (nlen, tlen, dlen), data[12:Len-alen])
111                 else:
112                         # don't handle any other packet yet
113                         return
114
115                 Notifications.AddNotificationWithID(
116                         NOTIFICATIONID,
117                         MessageBox,
118                         text = title + '\n' + description,
119                         type = MessageBox.TYPE_INFO,
120                         timeout = 5,
121                         close_on_any_key = True,
122                 )
123
124 class GrowlTalkAbstraction:
125         def __init__(self, host):
126                 self.growltalk = GrowlTalk(host)
127                 listeningPort = GROWL_UDP_PORT if host.enable_incoming.value else 0
128                 self.serverPort = reactor.listenUDP(listeningPort, self.growltalk)
129
130         def sendNotification(self, title='No title.', description='No description.', priority=-1, timeout=-1):
131                 if priority < 0:
132                         flags = 8 + (-priority << 1)
133                 else:
134                         flags = priority << 1
135
136                 # NOTE: sticky didn't work in any of my tests, but let's assume this is my configurations fault
137                 if timeout == -1:
138                         flags |= 1
139
140                 self.growltalk.sendNotification(title=title, description=description, flags=flags)
141
142         def stop(self):
143                 return self.serverPort.stopListening()
144