Mercurial > libervia-pubsub
annotate sat_pubsub/tap_http.py @ 338:6d059f07c2d3
privilege: added presence and +notify initial support:
presence privilege is now used and capabilities are cached. When an entity is connected, last items are sent according to +notify nodes in disco.
This is initial support, XEP-0356 doesn't allow yet to get roster updated, or synchronises contacts on startup.
Only "open" access model is supported for now. "presence" should be added soon as it is trivial to support now.
Only last items sending is handled for now, notifications support for new items/deletions should follow.
Capabilities hash is not checked yet, with the security concerns that this imply. Check should be added in the future.
author | Goffi <goffi@goffi.org> |
---|---|
date | Sat, 12 Aug 2017 18:29:32 +0200 |
parents | 5d7c3787672e |
children | 9fbb31ce495b |
rev | line source |
---|---|
233 | 1 #!/usr/bin/python |
2 #-*- coding: utf-8 -*- | |
3 | |
312
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
4 # Copyright (c) 2003-2011 Ralph Meijer |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
5 # Copyright (c) 2012-2016 Jérôme Poisson |
233 | 6 |
7 | |
312
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
8 # This program is free software: you can redistribute it and/or modify |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
9 # it under the terms of the GNU Affero General Public License as published by |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
10 # the Free Software Foundation, either version 3 of the License, or |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
11 # (at your option) any later version. |
233 | 12 |
312
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
13 # This program is distributed in the hope that it will be useful, |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
16 # GNU Affero General Public License for more details. |
233 | 17 |
312
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
18 # You should have received a copy of the GNU Affero General Public License |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
19 # along with this program. If not, see <http://www.gnu.org/licenses/>. |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
20 # -- |
233 | 21 |
312
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
22 # This program is based on Idavoll (http://idavoll.ik.nu/), |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
23 # originaly written by Ralph Meijer (http://ralphm.net/blog/) |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
24 # It is sublicensed under AGPL v3 (or any later version) as allowed by the original |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
25 # license. |
233 | 26 |
312
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
27 # -- |
233 | 28 |
312
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
29 # Here is a copy of the original license: |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
30 |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
31 # Copyright (c) 2003-2011 Ralph Meijer |
233 | 32 |
312
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
33 # Permission is hereby granted, free of charge, to any person obtaining |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
34 # a copy of this software and associated documentation files (the |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
35 # "Software"), to deal in the Software without restriction, including |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
36 # without limitation the rights to use, copy, modify, merge, publish, |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
37 # distribute, sublicense, and/or sell copies of the Software, and to |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
38 # permit persons to whom the Software is furnished to do so, subject to |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
39 # the following conditions: |
233 | 40 |
312
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
41 # The above copyright notice and this permission notice shall be |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
42 # included in all copies or substantial portions of the Software. |
233 | 43 |
312
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
44 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
45 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
46 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
47 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
48 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
49 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
50 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
233 | 51 |
177
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
52 |
273
6ba0d6def7f5
Use twisted.web instead of web2, initial tests.
Ralph Meijer <ralphm@ik.nu>
parents:
255
diff
changeset
|
53 from twisted.application import internet, strports |
177
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
54 from twisted.conch import manhole, manhole_ssh |
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
55 from twisted.cred import portal, checkers |
273
6ba0d6def7f5
Use twisted.web instead of web2, initial tests.
Ralph Meijer <ralphm@ik.nu>
parents:
255
diff
changeset
|
56 from twisted.web import resource, server |
177
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
57 |
234
51af2ed8bd50
replaced idavoll imports by sat_pubsub imports
Goffi <goffi@goffi.org>
parents:
233
diff
changeset
|
58 from sat_pubsub import gateway, tap |
51af2ed8bd50
replaced idavoll imports by sat_pubsub imports
Goffi <goffi@goffi.org>
parents:
233
diff
changeset
|
59 from sat_pubsub.gateway import RemoteSubscriptionService |
177
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
60 |
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
61 class Options(tap.Options): |
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
62 optParameters = [ |
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
63 ('webport', None, '8086', 'Web port'), |
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
64 ] |
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
65 |
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
66 |
206
274a45d2a5ab
Implement root collection that includes all leaf nodes.
Ralph Meijer <ralphm@ik.nu>
parents:
204
diff
changeset
|
67 |
177
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
68 def getManholeFactory(namespace, **passwords): |
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
69 def getManHole(_): |
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
70 return manhole.Manhole(namespace) |
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
71 |
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
72 realm = manhole_ssh.TerminalRealm() |
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
73 realm.chainedProtocolFactory.protocolFactory = getManHole |
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
74 p = portal.Portal(realm) |
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
75 p.registerChecker( |
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
76 checkers.InMemoryUsernamePasswordDatabaseDontUse(**passwords)) |
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
77 f = manhole_ssh.ConchFactory(p) |
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
78 return f |
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
79 |
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
80 |
206
274a45d2a5ab
Implement root collection that includes all leaf nodes.
Ralph Meijer <ralphm@ik.nu>
parents:
204
diff
changeset
|
81 |
177
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
82 def makeService(config): |
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
83 s = tap.makeService(config) |
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
84 |
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
85 bs = s.getServiceNamed('backend') |
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
86 cs = s.getServiceNamed('component') |
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
87 |
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
88 # Set up XMPP service for subscribing to remote nodes |
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
89 |
204
b4bf0a5ce50d
Implement storage facilities for the HTTP gateway.
Ralph Meijer <ralphm@ik.nu>
parents:
185
diff
changeset
|
90 if config['backend'] == 'pgsql': |
234
51af2ed8bd50
replaced idavoll imports by sat_pubsub imports
Goffi <goffi@goffi.org>
parents:
233
diff
changeset
|
91 from sat_pubsub.pgsql_storage import GatewayStorage |
204
b4bf0a5ce50d
Implement storage facilities for the HTTP gateway.
Ralph Meijer <ralphm@ik.nu>
parents:
185
diff
changeset
|
92 gst = GatewayStorage(bs.storage.dbpool) |
b4bf0a5ce50d
Implement storage facilities for the HTTP gateway.
Ralph Meijer <ralphm@ik.nu>
parents:
185
diff
changeset
|
93 elif config['backend'] == 'memory': |
234
51af2ed8bd50
replaced idavoll imports by sat_pubsub imports
Goffi <goffi@goffi.org>
parents:
233
diff
changeset
|
94 from sat_pubsub.memory_storage import GatewayStorage |
204
b4bf0a5ce50d
Implement storage facilities for the HTTP gateway.
Ralph Meijer <ralphm@ik.nu>
parents:
185
diff
changeset
|
95 gst = GatewayStorage() |
b4bf0a5ce50d
Implement storage facilities for the HTTP gateway.
Ralph Meijer <ralphm@ik.nu>
parents:
185
diff
changeset
|
96 |
b4bf0a5ce50d
Implement storage facilities for the HTTP gateway.
Ralph Meijer <ralphm@ik.nu>
parents:
185
diff
changeset
|
97 ss = RemoteSubscriptionService(config['jid'], gst) |
177
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
98 ss.setHandlerParent(cs) |
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
99 ss.startService() |
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
100 |
185
9038908dc2f5
Add gateway support for retrieving items from a node. Reorder gateway module.
Ralph Meijer <ralphm@ik.nu>
parents:
183
diff
changeset
|
101 # Set up web service |
177
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
102 |
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
103 root = resource.Resource() |
185
9038908dc2f5
Add gateway support for retrieving items from a node. Reorder gateway module.
Ralph Meijer <ralphm@ik.nu>
parents:
183
diff
changeset
|
104 |
9038908dc2f5
Add gateway support for retrieving items from a node. Reorder gateway module.
Ralph Meijer <ralphm@ik.nu>
parents:
183
diff
changeset
|
105 # Set up resources that exposes the backend |
273
6ba0d6def7f5
Use twisted.web instead of web2, initial tests.
Ralph Meijer <ralphm@ik.nu>
parents:
255
diff
changeset
|
106 root.putChild('create', gateway.CreateResource(bs, config['jid'], |
6ba0d6def7f5
Use twisted.web instead of web2, initial tests.
Ralph Meijer <ralphm@ik.nu>
parents:
255
diff
changeset
|
107 config['jid'])) |
6ba0d6def7f5
Use twisted.web instead of web2, initial tests.
Ralph Meijer <ralphm@ik.nu>
parents:
255
diff
changeset
|
108 root.putChild('delete', gateway.DeleteResource(bs, config['jid'], |
6ba0d6def7f5
Use twisted.web instead of web2, initial tests.
Ralph Meijer <ralphm@ik.nu>
parents:
255
diff
changeset
|
109 config['jid'])) |
6ba0d6def7f5
Use twisted.web instead of web2, initial tests.
Ralph Meijer <ralphm@ik.nu>
parents:
255
diff
changeset
|
110 root.putChild('publish', gateway.PublishResource(bs, config['jid'], |
6ba0d6def7f5
Use twisted.web instead of web2, initial tests.
Ralph Meijer <ralphm@ik.nu>
parents:
255
diff
changeset
|
111 config['jid'])) |
6ba0d6def7f5
Use twisted.web instead of web2, initial tests.
Ralph Meijer <ralphm@ik.nu>
parents:
255
diff
changeset
|
112 root.putChild('list', gateway.ListResource(bs)) |
185
9038908dc2f5
Add gateway support for retrieving items from a node. Reorder gateway module.
Ralph Meijer <ralphm@ik.nu>
parents:
183
diff
changeset
|
113 |
9038908dc2f5
Add gateway support for retrieving items from a node. Reorder gateway module.
Ralph Meijer <ralphm@ik.nu>
parents:
183
diff
changeset
|
114 # Set up resources for accessing remote pubsub nodes. |
273
6ba0d6def7f5
Use twisted.web instead of web2, initial tests.
Ralph Meijer <ralphm@ik.nu>
parents:
255
diff
changeset
|
115 root.putChild('subscribe', gateway.RemoteSubscribeResource(ss)) |
6ba0d6def7f5
Use twisted.web instead of web2, initial tests.
Ralph Meijer <ralphm@ik.nu>
parents:
255
diff
changeset
|
116 root.putChild('unsubscribe', gateway.RemoteUnsubscribeResource(ss)) |
6ba0d6def7f5
Use twisted.web instead of web2, initial tests.
Ralph Meijer <ralphm@ik.nu>
parents:
255
diff
changeset
|
117 root.putChild('items', gateway.RemoteItemsResource(ss)) |
213
a321f9300054
Actually log requests if verbose.
Ralph Meijer <ralphm@ik.nu>
parents:
206
diff
changeset
|
118 |
177
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
119 site = server.Site(root) |
273
6ba0d6def7f5
Use twisted.web instead of web2, initial tests.
Ralph Meijer <ralphm@ik.nu>
parents:
255
diff
changeset
|
120 w = internet.TCPServer(int(config['webport']), site) |
177
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
121 w.setServiceParent(s) |
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
122 |
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
123 # Set up a manhole |
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
124 |
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
125 namespace = {'service': s, |
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
126 'component': cs, |
206
274a45d2a5ab
Implement root collection that includes all leaf nodes.
Ralph Meijer <ralphm@ik.nu>
parents:
204
diff
changeset
|
127 'backend': bs, |
274a45d2a5ab
Implement root collection that includes all leaf nodes.
Ralph Meijer <ralphm@ik.nu>
parents:
204
diff
changeset
|
128 'root': root} |
274a45d2a5ab
Implement root collection that includes all leaf nodes.
Ralph Meijer <ralphm@ik.nu>
parents:
204
diff
changeset
|
129 |
177
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
130 f = getManholeFactory(namespace, admin='admin') |
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
131 manholeService = strports.service('2222', f) |
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
132 manholeService.setServiceParent(s) |
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
133 |
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
134 return s |
faf1c9bc2612
Add HTTP gateway in a separate plugin.
Ralph Meijer <ralphm@ik.nu>
parents:
diff
changeset
|
135 |