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