rework backends and split them up,
[vuplus_dvbapp-plugin] / growlee / src / SNP.py
1 from netgrowl import GROWL_UDP_PORT
2 from twisted.internet.defer import Deferred
3 from twisted.internet.protocol import ClientFactory, ServerFactory
4 from twisted.internet import reactor
5 from twisted.protocols.basic import LineReceiver
6
7 from Tools import Notifications
8 from Components.config import config
9
10 from GrowleeConnection import emergencyDisable
11 from . import NOTIFICATIONID
12
13 class SnarlNetworkProtocol(LineReceiver):
14         def __init__(self, client = False):
15                 self.client = client
16
17         def connectionMade(self):
18                 self.factory.addClient(self)
19                 if self.client:
20                         payload = "type=SNP#?version=1.0#?action=register#?app=growlee"
21                         self.sendLine(payload)
22
23                         payload = "type=SNP#?version=1.0#?action=add_class#?app=growlee#?class=growleeClass#?title=Notifications from your Dreambox"
24                         self.sendLine(payload)
25
26         def connectionLost(self, reason):
27                 self.factory.removeClient(self)
28
29         def stop(self):
30                 if self.client:
31                         payload = "type=SNP#?version=1.0#?action=unregister#?app=growlee"
32                         self.sendLine(payload)
33
34                 self.transport.loseConnection()
35
36         def sendNotification(self, title='No title.', description='No message.', timeout=1):
37                 if not self.client or not self.transport:
38                         return
39
40                 payload = "type=SNP#?version=1.0#?action=notification#?app=growlee#?class=growleeClass#?title=%s#?text=%s#?timeout=%d" % (title, description, timeout)
41                 self.sendLine(payload)
42
43         def lineReceived(self, data):
44                 if self.client or not self.transport:
45                         return
46
47                 Len = len(data)
48                 if Len < 23 or not data[:23] == "type=SNP#?version=1.0#?":
49                         return
50
51                 items = data[23:].split('#?')
52
53                 title = ''
54                 description = ''
55                 timeout = 5
56                 for item in items:
57                         key, value = item.split('=')
58                         if key == "action":
59                                 if value == "unregister":
60                                         payload = "SNP/1.0/0/OK"
61                                         self.sendLine(payload)
62                                         self.transport.loseConnection()
63                                         return
64                                 elif value != "notification":
65                                         # NOTE: we pretend to handle&accept pretty much everything one throws at us
66                                         payload = "SNP/1.0/0/OK"
67                                         self.sendLine(payload)
68                                         return
69                         elif key == "title":
70                                 title = value
71                         elif key == "text":
72                                 description = value
73                         elif key == "timeout":
74                                 timeout = int(value)
75
76                 Notifications.AddNotificationWithID(
77                         NOTIFICATIONID,
78                         MessageBox,
79                         text = title + '\n' + description,
80                         type = MessageBox.TYPE_INFO,
81                         timeout = timeout,
82                         close_on_any_key = True,
83                 )
84
85                 # return ok
86                 payload = "SNP/1.0/0/OK"
87                 self.sendLine(payload)
88
89 class SnarlNetworkProtocolClientFactory(ClientFactory):
90         client = None
91
92         def buildProtocol(self, addr):
93                 p = SnarlNetworkProtocol(client = True)
94                 p.factory = self
95                 return p
96
97         def sendNotification(self, title='No title.', description='No message.', priority=0, timeout=-1):
98                 if self.client:
99                         title = title.decode('utf-8', 'ignore').encode('iso8859-15', 'ignore')
100                         description = description.decode('utf-8', 'ignore').encode('iso8859-15', 'ignore')
101
102                         # NOTE: timeout = 0 means sticky, so add one second to map -1 to 0 and make 0 non-sticky
103                         if timeout < 1:
104                                 timeout += 1
105
106                         self.client.sendNotification(title=title, description=description, timeout=timeout)
107
108         def addClient(self, client):
109                 self.client = client
110
111         def removeClient(self, client):
112                 self.client = None
113
114 class SnarlNetworkProtocolServerFactory(ServerFactory):
115         protocol = SnarlNetworkProtocol
116
117         def __init__(self):
118                 self.clients = []
119
120         def addClient(self, client):
121                 self.clients.append(client)
122
123         def removeClient(self, client):
124                 self.clients.remove(client)
125
126         def sendNotification(self, *args, **kwargs):
127                 pass
128
129         def stopFactory(self):
130                 for client in self.clients:
131                         client.stop()
132
133 class SnarlNetworkProtocolAbstraction:
134         clientPort = None
135         serverPort = None
136         pending = 0
137
138         def __init__(self):
139                 self.clientFactory = SnarlNetworkProtocolClientFactory()
140                 self.serverFactory = SnarlNetworkProtocolServerFactory()
141
142                 if config.plugins.growlee.enable_outgoing.value:
143                         reactor.resolve(config.plugins.growlee.address.value).addCallback(self.gotIP).addErrback(self.noIP)
144
145                 if config.plugins.growlee.enable_incoming.value:
146                         self.serverPort = reactor.listenTCP(GROWL_UDP_PORT, self.serverFactory)
147                         self.pending += 1
148
149         def gotIP(self, ip):
150                 self.clientPort = reactor.connectTCP(ip, GROWL_UDP_PORT, self.clientFactory)
151                 self.pending += 1
152
153         def noIP(self, error):
154                 emergencyDisable()
155
156         def sendNotification(self, *args, **kwargs):
157                 self.clientFactory.sendNotification(*args, **kwargs)
158
159         def maybeClose(self, resOrFail, defer = None):
160                 self.pending -= 1
161                 if self.pending == 0:
162                         if defer:
163                                 defer.callback(True)
164
165         def stop(self):
166                 defer = Deferred()
167                 if self.clientPort:
168                         d = self.clientPort.disconnect()
169                         if d:
170                                 d.addBoth(self.maybeClose, defer = defer)
171                         else:
172                                 self.pending -= 1
173
174                 if self.serverPort:
175                         d = self.serverPort.stopListening()
176                         if d:
177                                 d.addBoth(self.maybeClose, defer = defer)
178                         else:
179                                 self.pending -= 1
180
181                 if self.pending == 0:
182                         reactor.callLater(1, defer.callback, True)
183                 return defer
184