Merge lp:~xnox/nuntium/drop-sys-events into lp:nuntium/packaging
- drop-sys-events
- Merge into packaging
Status: | Superseded | ||||
---|---|---|---|---|---|
Proposed branch: | lp:~xnox/nuntium/drop-sys-events | ||||
Merge into: | lp:nuntium/packaging | ||||
Diff against target: |
6087 lines (+5849/-0) (has conflicts) 44 files modified
.bzr-builddeb/default.conf (+2/-0) debian/changelog (+150/-0) debian/compat (+1/-0) debian/control (+57/-0) debian/copyright (+22/-0) debian/golang-nuntium-mms-dev.install (+1/-0) debian/golang-nuntium-ofono-dev.install (+1/-0) debian/golang-nuntium-telepathy-dev.install (+1/-0) debian/nuntium-decode-cli.install (+1/-0) debian/nuntium.conf (+10/-0) debian/nuntium.install (+2/-0) debian/rules (+33/-0) loop.go (+76/-0) main.go (+84/-0) mediator.go (+403/-0) mms/attachments.go (+295/-0) mms/decode-cli/decode.go (+70/-0) mms/decoder.go (+437/-0) mms/decoder_payload_test.go (+56/-0) mms/decoder_test.go (+59/-0) mms/download.go (+92/-0) mms/encode_decode_test.go (+110/-0) mms/encoder.go (+448/-0) mms/encoder_test.go (+141/-0) mms/mms.go (+438/-0) mms/mms_test.go (+37/-0) mms/parameters.go (+153/-0) ofono/common.go (+69/-0) ofono/context_test.go (+342/-0) ofono/manager.go (+111/-0) ofono/modem.go (+428/-0) ofono/push.go (+112/-0) ofono/push_decode_test.go (+170/-0) ofono/pushagent.go (+153/-0) ofono/wsp_params.go (+150/-0) storage/const.go (+41/-0) storage/context.go (+113/-0) storage/mmstate.go (+54/-0) storage/storage.go (+144/-0) telepathy/const.go (+53/-0) telepathy/manager.go (+132/-0) telepathy/message.go (+117/-0) telepathy/service.go (+385/-0) test/test.go (+95/-0) Conflict adding file .bzr-builddeb. Moved existing file to .bzr-builddeb.moved. Conflict adding file debian. Moved existing file to debian.moved. Conflict adding file mms. Moved existing file to mms.moved. Conflict adding file ofono. Moved existing file to ofono.moved. Conflict adding file storage. Moved existing file to storage.moved. Conflict adding file telepathy. Moved existing file to telepathy.moved. Conflict adding file test. Moved existing file to test.moved. |
||||
To merge this branch: | bzr merge lp:~xnox/nuntium/drop-sys-events | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Martin Pitt | Pending | ||
Ubuntu Phablet Team | Pending | ||
PS Jenkins bot | continuous-integration | Pending | |
Review via email: mp+269883@code.launchpad.net |
This proposal supersedes a proposal from 2015-01-10.
This proposal has been superseded by a proposal from 2015-09-02.
Commit message
Migrate from :sys:started events to dbus owner signals.
Description of the change
Migrate from :sys:started events to dbus owner signals.
Dimitri John Ledkov (xnox) wrote : Posted in a previous version of this proposal | # |
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:82
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Alfonso Sanchez-Beato (alfonsosanchezbeato) wrote : Posted in a previous version of this proposal | # |
Alfonso Sanchez-Beato (alfonsosanchezbeato) wrote : Posted in a previous version of this proposal | # |
MP for lp:nuntium/packaging just landed.
Unmerged revisions
- 82. By Dimitri John Ledkov
-
Migrate from :sys:started events to dbus owner signals.
- 81. By PS Jenkins bot
-
Releasing 0.1+14.
10.20141013- 0ubuntu1 - 80. By Sergio Schvezov
-
Hold off on retrying when ofono fails to activate a context with a generic failure Fixes: 1380699
Approved by: Alfonso Sanchez-Beato, PS Jenkins bot - 79. By PS Jenkins bot
-
Releasing 0.1+14.
10.20141002- 0ubuntu1 - 78. By Sergio Schvezov
-
Retry on NotAttached ofono errors Fixes: 1371032
Approved by: Ricardo Salveti, PS Jenkins bot - 77. By Sergio Schvezov
-
Syncing upload/download operations with activation/
deactivation per request.
Moving all the ofono context property checks and reflection logic to proper functions for easier reuse and readability. Fixes: 1376224
Approved by: PS Jenkins bot - 76. By Sergio Schvezov
-
Allow context selection over the org.ofono.
mms.Service interface Fixes: 1370660
Approved by: Antti Kaijanmäki, PS Jenkins bot, Ricardo Salveti - 75. By PS Jenkins bot
-
Releasing 0.1+14.
10.20140924- 0ubuntu1 - 74. By Sergio Schvezov
-
Using the same optional parameters android uses for sending, fixing length encoding for lengths < 30, adding extra params to the content types. Fixes: 1349299
Approved by: PS Jenkins bot, Manuel de la Peña - 73. By PS Jenkins bot
-
Releasing 0.1+14.
10.20140918- 0ubuntu1
Preview Diff
1 | === added directory '.bzr-builddeb' |
2 | === renamed directory '.bzr-builddeb' => '.bzr-builddeb.moved' |
3 | === added file '.bzr-builddeb/default.conf' |
4 | --- .bzr-builddeb/default.conf 1970-01-01 00:00:00 +0000 |
5 | +++ .bzr-builddeb/default.conf 2015-09-02 10:42:28 +0000 |
6 | @@ -0,0 +1,2 @@ |
7 | +[BUILDDEB] |
8 | +split = True |
9 | |
10 | === added directory 'debian' |
11 | === renamed directory 'debian' => 'debian.moved' |
12 | === added file 'debian/changelog' |
13 | --- debian/changelog 1970-01-01 00:00:00 +0000 |
14 | +++ debian/changelog 2015-09-02 10:42:28 +0000 |
15 | @@ -0,0 +1,150 @@ |
16 | +nuntium (0.1+14.10.20141013-0ubuntu1) 14.09; urgency=low |
17 | + |
18 | + [ Sergio Schvezov ] |
19 | + * Hold off on retrying when ofono fails to activate a context with a |
20 | + generic failure (LP: #1380699) |
21 | + |
22 | + -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 13 Oct 2014 16:59:11 +0000 |
23 | + |
24 | +nuntium (0.1+14.10.20141002-0ubuntu1) 14.09; urgency=low |
25 | + |
26 | + [ Sergio Schvezov ] |
27 | + * Allow context selection over the org.ofono.mms.Service interface |
28 | + (LP: #1370660) |
29 | + * Syncing upload/download operations with activation/deactivation per |
30 | + request. Moving all the ofono context property checks and reflection |
31 | + logic to proper functions for easier reuse and readability. (LP: |
32 | + #1376224) |
33 | + * Retry on NotAttached ofono errors (LP: #1371032) |
34 | + |
35 | + -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Thu, 02 Oct 2014 15:06:31 +0000 |
36 | + |
37 | +nuntium (0.1+14.10.20140924-0ubuntu1) 14.09; urgency=low |
38 | + |
39 | + [ Sergio Schvezov ] |
40 | + * Using the same optional parameters android uses for sending, fixing |
41 | + length encoding for lengths < 30, adding extra params to the content |
42 | + types. (LP: #1349299) |
43 | + |
44 | + -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 24 Sep 2014 12:39:58 +0000 |
45 | + |
46 | +nuntium (0.1+14.10.20140918-0ubuntu1) 14.09; urgency=low |
47 | + |
48 | + [ Sergio Schvezov ] |
49 | + * Iterate over a list of possible valid MMS contexts and store the |
50 | + preferred one (LP: #1370659) |
51 | + |
52 | + -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Thu, 18 Sep 2014 04:37:21 +0000 |
53 | + |
54 | +nuntium (0.1+14.10.20140915.1-0ubuntu1) 14.09; urgency=low |
55 | + |
56 | + [ Ubuntu daily release ] |
57 | + * New rebuild forced |
58 | + |
59 | + [ Sergio Schvezov ] |
60 | + * Add a missing return statement when decoding fails with an |
61 | + additional decoding test. (LP: #1369143) |
62 | + |
63 | + -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 15 Sep 2014 14:59:46 +0000 |
64 | + |
65 | +nuntium (0.1+14.10.20140912-0ubuntu1) utopic; urgency=low |
66 | + |
67 | + [ Sergio Schvezov ] |
68 | + * Allow proxyless contexts to be selected for MMS (LP: #1362008) |
69 | + |
70 | + -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Fri, 12 Sep 2014 00:38:34 +0000 |
71 | + |
72 | +nuntium (0.1+14.10.20140904-0ubuntu1) utopic; urgency=low |
73 | + |
74 | + [ Sergio Schvezov ] |
75 | + * Better error handling on sending MMS and lowering the supported |
76 | + version number for broader carrier support (LP: #1349299) |
77 | + |
78 | + -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Thu, 04 Sep 2014 19:21:58 +0000 |
79 | + |
80 | +nuntium (0.1+14.10.20140902-0ubuntu1) utopic; urgency=low |
81 | + |
82 | + [ Sergio Schvezov ] |
83 | + * Read reflected content length for push data into the correct type. |
84 | + (LP: #1342441) |
85 | + |
86 | + -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 02 Sep 2014 08:39:25 +0000 |
87 | + |
88 | +nuntium (0.1+14.10.20140814-0ubuntu1) utopic; urgency=low |
89 | + |
90 | + [ Tiago Salem Herrmann ] |
91 | + * Add ModemObjectPath property to Service. |
92 | + |
93 | + -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Thu, 14 Aug 2014 20:25:23 +0000 |
94 | + |
95 | +nuntium (0.1+14.10.20140721-0ubuntu1) utopic; urgency=low |
96 | + |
97 | + [ Sergio Schvezov ] |
98 | + * Calling sync and close after encoding and before uploading. |
99 | + * Header parameter encoding corrections for content type and from |
100 | + token insert address length. |
101 | + * Attachment encoding fixes. (LP: #1342270) |
102 | + * Tracking response file for uploads (remaining parts to be done when |
103 | + telepathy-ofono integration work starts) |
104 | + |
105 | + -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 21 Jul 2014 09:44:10 +0000 |
106 | + |
107 | +nuntium (0.1+14.10.20140702.2-0ubuntu1) utopic; urgency=low |
108 | + |
109 | + [ CI bot ] |
110 | + * Upload support while moving udm to it's new package namespace |
111 | + * SendMessage telepathy service support with necessary encoder fixes. |
112 | + |
113 | + [ Sergio Schvezov ] |
114 | + * Improving incoming dbus method call handling for the mms service |
115 | + interface |
116 | + * Waiting for calls to GetServices on the proper interface |
117 | + * Adding a minimal telepathy message interface |
118 | + * Fixing recv for multiple recipients. |
119 | + * Making decoding less verbose and logging information on errors only |
120 | + * Adding encode -> decode tests for the cases where decoding is the |
121 | + inverse function of encoding. |
122 | + * Relaxing matching for smil media type |
123 | + * Length bound checks fix (LP: #1336146) |
124 | + * Sending MessageAdded with draft Status for new outgoing messages |
125 | + |
126 | + -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 02 Jul 2014 21:15:22 +0000 |
127 | + |
128 | +nuntium (0.1+14.10.20140621-0ubuntu1) utopic; urgency=low |
129 | + |
130 | + [ Sergio Schvezov ] |
131 | + * Decoding well known media types in the push notification. (LP: |
132 | + #1330917) |
133 | + |
134 | + -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Sat, 21 Jun 2014 19:53:48 +0000 |
135 | + |
136 | +nuntium (0.1+14.10.20140529-0ubuntu1) utopic; urgency=low |
137 | + |
138 | + [ Sergio Schvezov ] |
139 | + * Fixing constrained content type decoding (LP: #1324182) |
140 | + |
141 | + -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Thu, 29 May 2014 13:55:29 +0000 |
142 | + |
143 | +nuntium (0.1+14.10.20140514.1-0ubuntu1) utopic; urgency=low |
144 | + |
145 | + [ Sergio Schvezov ] |
146 | + * Fixing a string decode bounds issue |
147 | + * Removing redundant log line |
148 | + * Adding one more condition to the upstart start stanza since the |
149 | + first event can be missed |
150 | + * Splitting up ofono package to smaller logical bits |
151 | + * Encoder improvements |
152 | + |
153 | + -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 14 May 2014 14:21:59 +0000 |
154 | + |
155 | +nuntium (0.1-0ubuntu2) utopic; urgency=medium |
156 | + |
157 | + * Moving stray Built-Using from Depends into Built-Using. |
158 | + |
159 | + -- Sergio Schvezov <sergio.schvezov@canonical.com> Mon, 12 May 2014 10:04:25 -0300 |
160 | + |
161 | +nuntium (0.1-0ubuntu1) utopic; urgency=low |
162 | + |
163 | + * Initial packaging. |
164 | + |
165 | + -- Sergio Schvezov <sergio.schvezov@canonical.com> Thu, 03 Apr 2014 15:01:24 -0300 |
166 | |
167 | === added file 'debian/compat' |
168 | --- debian/compat 1970-01-01 00:00:00 +0000 |
169 | +++ debian/compat 2015-09-02 10:42:28 +0000 |
170 | @@ -0,0 +1,1 @@ |
171 | +9 |
172 | |
173 | === added file 'debian/control' |
174 | --- debian/control 1970-01-01 00:00:00 +0000 |
175 | +++ debian/control 2015-09-02 10:42:28 +0000 |
176 | @@ -0,0 +1,57 @@ |
177 | +Source: nuntium |
178 | +Section: devel |
179 | +Priority: optional |
180 | +Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com> |
181 | +Build-Depends: |
182 | + debhelper (>= 9), |
183 | + dh-golang, |
184 | + gccgo, |
185 | + gccgo-go, |
186 | + golang-go-dbus-dev, |
187 | + golang-go-xdg-dev, |
188 | + golang-gocheck-dev, |
189 | + golang-udm-dev, |
190 | +Standards-Version: 3.9.5 |
191 | +Homepage: https://launchpad.net/nuntium |
192 | +Vcs-Browser: http://bazaar.launchpad.net/~phablet-team/nuntium/trunk/files |
193 | +Vcs-Bzr: lp:nuntium |
194 | + |
195 | +Package: nuntium |
196 | +Architecture: any |
197 | +Depends: ofono, ubuntu-download-manager, ubuntu-upload-manager, ${misc:Depends}, ${shlibs:Depends} |
198 | +Built-Using: ${misc:Built-Using} |
199 | +Recommends: telepathy-ofono |
200 | +Conflicts: mmsd |
201 | +Description: Bridges push notifications from ofono to telepathy-ofono |
202 | + This component registers a push agent with ofono and handles the MMS workflow |
203 | + by bridging with telepathy-ofono |
204 | + |
205 | +Package: nuntium-decode-cli |
206 | +Architecture: any |
207 | +Depends: ${misc:Depends}, ${shlibs:Depends} |
208 | +Built-Using: ${misc:Built-Using} |
209 | +Description: Decode m-retrieve.conf messages |
210 | + Decodes and prints out decoding results to the |
211 | + |
212 | +Package: golang-nuntium-mms-dev |
213 | +Architecture: all |
214 | +Depends: ${misc:Depends} |
215 | +Built-Using: ${misc:Built-Using} |
216 | +Description: Go library for manipulating MMS |
217 | + This package handles MMS PDUs and has hooks for related actions in the MMS |
218 | + workflow |
219 | + |
220 | +Package: golang-nuntium-ofono-dev |
221 | +Architecture: all |
222 | +Depends: ${misc:Depends} |
223 | +Built-Using: ${misc:Built-Using} |
224 | +Description: Go library for interfacing with ofono |
225 | + Provides facilities to interface with ofono with regards to MMS through dbus |
226 | + |
227 | +Package: golang-nuntium-telepathy-dev |
228 | +Architecture: all |
229 | +Depends: ${misc:Depends} |
230 | +Built-Using: ${misc:Built-Using} |
231 | +Description: Go library for interfacing with telepathy-ofono |
232 | + Provides facilities to interface with telepathy ofono with regards to MMS |
233 | + through dbus |
234 | |
235 | === added file 'debian/copyright' |
236 | --- debian/copyright 1970-01-01 00:00:00 +0000 |
237 | +++ debian/copyright 2015-09-02 10:42:28 +0000 |
238 | @@ -0,0 +1,22 @@ |
239 | +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ |
240 | +Upstream-Name: nuntium |
241 | +Source: https://launchpad.net/nuntium |
242 | + |
243 | +Files: * |
244 | +Copyright: Copyright (C) 2013 Canonical, Ltd. |
245 | +License: GPL-3 |
246 | + This program is free software: you can redistribute it and/or modify it |
247 | + under the terms of the the GNU General Public License version 3, as |
248 | + published by the Free Software Foundation. |
249 | + . |
250 | + This program is distributed in the hope that it will be useful, but |
251 | + WITHOUT ANY WARRANTY; without even the implied warranties of |
252 | + MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR |
253 | + PURPOSE. See the applicable version of the GNU Lesser General Public |
254 | + License for more details. |
255 | + . |
256 | + You should have received a copy of the GNU General Public License |
257 | + along with this program. If not, see <http://www.gnu.org/licenses/>. |
258 | + . |
259 | + On Debian systems, the complete text of the GNU General Public License |
260 | + can be found in `/usr/share/common-licenses/GPL-3' |
261 | |
262 | === added file 'debian/golang-nuntium-mms-dev.install' |
263 | --- debian/golang-nuntium-mms-dev.install 1970-01-01 00:00:00 +0000 |
264 | +++ debian/golang-nuntium-mms-dev.install 2015-09-02 10:42:28 +0000 |
265 | @@ -0,0 +1,1 @@ |
266 | +usr/share/gocode/src/launchpad.net/nuntium/mms |
267 | |
268 | === added file 'debian/golang-nuntium-ofono-dev.install' |
269 | --- debian/golang-nuntium-ofono-dev.install 1970-01-01 00:00:00 +0000 |
270 | +++ debian/golang-nuntium-ofono-dev.install 2015-09-02 10:42:28 +0000 |
271 | @@ -0,0 +1,1 @@ |
272 | +usr/share/gocode/src/launchpad.net/nuntium/ofono |
273 | |
274 | === added file 'debian/golang-nuntium-telepathy-dev.install' |
275 | --- debian/golang-nuntium-telepathy-dev.install 1970-01-01 00:00:00 +0000 |
276 | +++ debian/golang-nuntium-telepathy-dev.install 2015-09-02 10:42:28 +0000 |
277 | @@ -0,0 +1,1 @@ |
278 | +usr/share/gocode/src/launchpad.net/nuntium/telepathy |
279 | |
280 | === added file 'debian/nuntium-decode-cli.install' |
281 | --- debian/nuntium-decode-cli.install 1970-01-01 00:00:00 +0000 |
282 | +++ debian/nuntium-decode-cli.install 2015-09-02 10:42:28 +0000 |
283 | @@ -0,0 +1,1 @@ |
284 | +usr/bin/nuntium-decode-cli |
285 | |
286 | === added file 'debian/nuntium.conf' |
287 | --- debian/nuntium.conf 1970-01-01 00:00:00 +0000 |
288 | +++ debian/nuntium.conf 2015-09-02 10:42:28 +0000 |
289 | @@ -0,0 +1,10 @@ |
290 | +description "nuntium service binds ofono and telepathy-ofono for MMS" |
291 | + |
292 | +# Start on session dbus is spawned or |
293 | +# org.ofono bus name appears on system dbus |
294 | +# Stop on org.ofono bus name disappearing on system dbus |
295 | + |
296 | +start on (started dbus or dbus SIGNAL='NameOwnerChanged' BUS='system' INTERFACE='org.freedesktop.DBus' OBJPATH='/org/freedesktop/DBus' SENDER='org.freedesktop.DBus' ARG0='org.ofono' ARG1='' ARG2='*') |
297 | +stop on dbus SIGNAL='NameOwnerChanged' BUS='system' INTERFACE='org.freedesktop.DBus' OBJPATH='/org/freedesktop/DBus' SENDER='org.freedesktop.DBus' ARG0='org.ofono' ARG1='*' ARG2='' |
298 | + |
299 | +exec nuntium |
300 | |
301 | === added file 'debian/nuntium.install' |
302 | --- debian/nuntium.install 1970-01-01 00:00:00 +0000 |
303 | +++ debian/nuntium.install 2015-09-02 10:42:28 +0000 |
304 | @@ -0,0 +1,2 @@ |
305 | +debian/nuntium.conf /usr/share/upstart/sessions/ |
306 | +usr/bin/nuntium |
307 | |
308 | === added file 'debian/rules' |
309 | --- debian/rules 1970-01-01 00:00:00 +0000 |
310 | +++ debian/rules 2015-09-02 10:42:28 +0000 |
311 | @@ -0,0 +1,33 @@ |
312 | +#!/usr/bin/make -f |
313 | +# -*- makefile -*- |
314 | + |
315 | +export DH_OPTIONS |
316 | +export DH_GOPKG := launchpad.net/nuntium |
317 | +export DH_GOLANG_INSTALL_ALL := 1 |
318 | + |
319 | +DEB_HOST_ARCH := $(shell dpkg-architecture -qDEB_HOST_ARCH) |
320 | + |
321 | +%: |
322 | + dh $@ \ |
323 | + --buildsystem=golang \ |
324 | + --with=golang \ |
325 | + --fail-missing |
326 | + |
327 | +override_dh_auto_test: |
328 | +# The test runners panic when running on powerpc64. |
329 | +ifneq ($(DEB_HOST_ARCH),powerpc) |
330 | + dh_auto_test |
331 | +endif |
332 | + |
333 | +override_dh_auto_install: |
334 | + dh_auto_install -O--buildsystem=golang |
335 | + mv ${CURDIR}/debian/tmp/usr/bin/decode-cli \ |
336 | + ${CURDIR}/debian/tmp/usr/bin/nuntium-decode-cli |
337 | + rm -r \ |
338 | + ${CURDIR}/debian/tmp/usr/bin/test \ |
339 | + ${CURDIR}/debian/tmp/usr/share/gocode/src/$(DH_GOPKG)/test \ |
340 | + ${CURDIR}/debian/tmp/usr/share/gocode/src/$(DH_GOPKG)/storage \ |
341 | + ${CURDIR}/debian/tmp/usr/share/gocode/src/$(DH_GOPKG)/*.go |
342 | + |
343 | +override_dh_strip: |
344 | + echo "Skipping strip" |
345 | |
346 | === added directory 'doc' |
347 | === added file 'loop.go' |
348 | --- loop.go 1970-01-01 00:00:00 +0000 |
349 | +++ loop.go 2015-09-02 10:42:28 +0000 |
350 | @@ -0,0 +1,76 @@ |
351 | +/* |
352 | + * Copyright 2014 Canonical Ltd. |
353 | + * |
354 | + * Authors: |
355 | + * Sergio Schvezov: sergio.schvezov@cannical.com |
356 | + * |
357 | + * This file is part of nuntium. |
358 | + * |
359 | + * nuntium is free software; you can redistribute it and/or modify |
360 | + * it under the terms of the GNU General Public License as published by |
361 | + * the Free Software Foundation; version 3. |
362 | + * |
363 | + * nuntium is distributed in the hope that it will be useful, |
364 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
365 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
366 | + * GNU General Public License for more details. |
367 | + * |
368 | + * You should have received a copy of the GNU General Public License |
369 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
370 | + */ |
371 | + |
372 | +package main |
373 | + |
374 | +import ( |
375 | + "log" |
376 | + "os" |
377 | + "os/signal" |
378 | + "syscall" |
379 | +) |
380 | + |
381 | +type Mainloop struct { |
382 | + sigchan chan os.Signal |
383 | + termchan chan int |
384 | + Bindings map[os.Signal]func() |
385 | +} |
386 | + |
387 | +/* |
388 | +Start the mainloop. |
389 | + |
390 | +This method will block its current thread. The best spot for calling this |
391 | +method is right near the bottom of your application's main() function. |
392 | +*/ |
393 | +func (m *Mainloop) Start() { |
394 | + sigs := make([]os.Signal, len(m.Bindings)) |
395 | + for s, _ := range m.Bindings { |
396 | + sigs = append(sigs, s) |
397 | + } |
398 | + signal.Notify(m.sigchan, sigs...) |
399 | +L: |
400 | + for { |
401 | + select { |
402 | + case sig := <-m.sigchan: |
403 | + log.Print("Received ", sig) |
404 | + m.Bindings[sig]() |
405 | + case _ = <-m.termchan: |
406 | + break L |
407 | + } |
408 | + } |
409 | + return |
410 | +} |
411 | + |
412 | +/* |
413 | +Stops the mainloop. |
414 | +*/ |
415 | +func (m *Mainloop) Stop() { |
416 | + go func() { m.termchan <- 1 }() |
417 | + return |
418 | +} |
419 | + |
420 | +func HupHandler() { |
421 | + syscall.Exit(1) |
422 | +} |
423 | + |
424 | +func IntHandler() { |
425 | + syscall.Exit(1) |
426 | +} |
427 | |
428 | === added file 'main.go' |
429 | --- main.go 1970-01-01 00:00:00 +0000 |
430 | +++ main.go 2015-09-02 10:42:28 +0000 |
431 | @@ -0,0 +1,84 @@ |
432 | +/* |
433 | + * Copyright 2014 Canonical Ltd. |
434 | + * |
435 | + * Authors: |
436 | + * Sergio Schvezov: sergio.schvezov@cannical.com |
437 | + * |
438 | + * This file is part of nuntium. |
439 | + * |
440 | + * nuntium is free software; you can redistribute it and/or modify |
441 | + * it under the terms of the GNU General Public License as published by |
442 | + * the Free Software Foundation; version 3. |
443 | + * |
444 | + * nuntium is distributed in the hope that it will be useful, |
445 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
446 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
447 | + * GNU General Public License for more details. |
448 | + * |
449 | + * You should have received a copy of the GNU General Public License |
450 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
451 | + */ |
452 | + |
453 | +package main |
454 | + |
455 | +import ( |
456 | + "log" |
457 | + "os" |
458 | + "syscall" |
459 | + |
460 | + "launchpad.net/go-dbus/v1" |
461 | + "launchpad.net/nuntium/ofono" |
462 | + "launchpad.net/nuntium/telepathy" |
463 | +) |
464 | + |
465 | +func main() { |
466 | + var ( |
467 | + conn *dbus.Connection |
468 | + connSession *dbus.Connection |
469 | + err error |
470 | + ) |
471 | + if connSession, err = dbus.Connect(dbus.SessionBus); err != nil { |
472 | + log.Fatal("Connection error: ", err) |
473 | + } |
474 | + log.Print("Using session bus on ", connSession.UniqueName) |
475 | + |
476 | + mmsManager, err := telepathy.NewMMSManager(connSession) |
477 | + if err != nil { |
478 | + log.Fatal(err) |
479 | + } |
480 | + |
481 | + if conn, err = dbus.Connect(dbus.SystemBus); err != nil { |
482 | + log.Fatal("Connection error: ", err) |
483 | + } |
484 | + log.Print("Using system bus on ", conn.UniqueName) |
485 | + |
486 | + modemManager := ofono.NewModemManager(conn) |
487 | + mediators := make(map[dbus.ObjectPath]*Mediator) |
488 | + go func() { |
489 | + for { |
490 | + select { |
491 | + case modem := <-modemManager.ModemAdded: |
492 | + mediators[modem.Modem] = NewMediator(modem) |
493 | + go mediators[modem.Modem].init(mmsManager) |
494 | + if err := modem.Init(); err != nil { |
495 | + log.Printf("Cannot initialize modem %s", modem.Modem) |
496 | + } |
497 | + case modem := <-modemManager.ModemRemoved: |
498 | + mediators[modem.Modem].Delete() |
499 | + } |
500 | + } |
501 | + }() |
502 | + |
503 | + if err := modemManager.Init(); err != nil { |
504 | + log.Fatal(err) |
505 | + } |
506 | + |
507 | + m := Mainloop{ |
508 | + sigchan: make(chan os.Signal, 1), |
509 | + termchan: make(chan int), |
510 | + Bindings: make(map[os.Signal]func())} |
511 | + |
512 | + m.Bindings[syscall.SIGHUP] = func() { m.Stop(); HupHandler() } |
513 | + m.Bindings[syscall.SIGINT] = func() { m.Stop(); IntHandler() } |
514 | + m.Start() |
515 | +} |
516 | |
517 | === added file 'mediator.go' |
518 | --- mediator.go 1970-01-01 00:00:00 +0000 |
519 | +++ mediator.go 2015-09-02 10:42:28 +0000 |
520 | @@ -0,0 +1,403 @@ |
521 | +/* |
522 | + * Copyright 2014 Canonical Ltd. |
523 | + * |
524 | + * Authors: |
525 | + * Sergio Schvezov: sergio.schvezov@cannical.com |
526 | + * |
527 | + * This file is part of nuntium. |
528 | + * |
529 | + * nuntium is free software; you can redistribute it and/or modify |
530 | + * it under the terms of the GNU General Public License as published by |
531 | + * the Free Software Foundation; version 3. |
532 | + * |
533 | + * nuntium is distributed in the hope that it will be useful, |
534 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
535 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
536 | + * GNU General Public License for more details. |
537 | + * |
538 | + * You should have received a copy of the GNU General Public License |
539 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
540 | + */ |
541 | + |
542 | +package main |
543 | + |
544 | +import ( |
545 | + "io/ioutil" |
546 | + "log" |
547 | + "os" |
548 | + "sync" |
549 | + |
550 | + "launchpad.net/nuntium/mms" |
551 | + "launchpad.net/nuntium/ofono" |
552 | + "launchpad.net/nuntium/storage" |
553 | + "launchpad.net/nuntium/telepathy" |
554 | +) |
555 | + |
556 | +type Mediator struct { |
557 | + modem *ofono.Modem |
558 | + telepathyService *telepathy.MMSService |
559 | + NewMNotificationInd chan *mms.MNotificationInd |
560 | + NewMNotifyRespInd chan *mms.MNotifyRespInd |
561 | + NewMRetrieveConf chan *mms.MRetrieveConf |
562 | + NewMSendReq chan *mms.MSendReq |
563 | + NewMRetrieveConfFile chan string |
564 | + NewMNotifyRespIndFile chan string |
565 | + NewMSendReqFile chan struct{ filePath, uuid string } |
566 | + outMessage chan *telepathy.OutgoingMessage |
567 | + terminate chan bool |
568 | + contextLock sync.Mutex |
569 | +} |
570 | + |
571 | +//TODO these vars need a configuration location managed by system settings or |
572 | +//some UI accessible location. |
573 | +//useDeliveryReports is set in ofono |
574 | +var ( |
575 | + deferredDownload bool |
576 | + useDeliveryReports bool |
577 | +) |
578 | + |
579 | +func NewMediator(modem *ofono.Modem) *Mediator { |
580 | + mediator := &Mediator{modem: modem} |
581 | + mediator.NewMNotificationInd = make(chan *mms.MNotificationInd) |
582 | + mediator.NewMRetrieveConf = make(chan *mms.MRetrieveConf) |
583 | + mediator.NewMRetrieveConfFile = make(chan string) |
584 | + mediator.NewMNotifyRespInd = make(chan *mms.MNotifyRespInd) |
585 | + mediator.NewMNotifyRespIndFile = make(chan string) |
586 | + mediator.NewMSendReq = make(chan *mms.MSendReq) |
587 | + mediator.NewMSendReqFile = make(chan struct{ filePath, uuid string }) |
588 | + mediator.outMessage = make(chan *telepathy.OutgoingMessage) |
589 | + mediator.terminate = make(chan bool) |
590 | + return mediator |
591 | +} |
592 | + |
593 | +func (mediator *Mediator) Delete() { |
594 | + mediator.terminate <- mediator.telepathyService == nil |
595 | +} |
596 | + |
597 | +func (mediator *Mediator) init(mmsManager *telepathy.MMSManager) { |
598 | +mediatorLoop: |
599 | + for { |
600 | + select { |
601 | + case push, ok := <-mediator.modem.PushAgent.Push: |
602 | + if !ok { |
603 | + log.Print("PushChannel is closed") |
604 | + continue |
605 | + } |
606 | + go mediator.handleMNotificationInd(push) |
607 | + case mNotificationInd := <-mediator.NewMNotificationInd: |
608 | + if deferredDownload { |
609 | + go mediator.handleDeferredDownload(mNotificationInd) |
610 | + } else { |
611 | + go mediator.getMRetrieveConf(mNotificationInd) |
612 | + } |
613 | + case mRetrieveConfFilePath := <-mediator.NewMRetrieveConfFile: |
614 | + go mediator.handleMRetrieveConf(mRetrieveConfFilePath) |
615 | + case mRetrieveConf := <-mediator.NewMRetrieveConf: |
616 | + go mediator.handleRetrieved(mRetrieveConf) |
617 | + case mNotifyRespInd := <-mediator.NewMNotifyRespInd: |
618 | + go mediator.handleMNotifyRespInd(mNotifyRespInd) |
619 | + case mNotifyRespIndFilePath := <-mediator.NewMNotifyRespIndFile: |
620 | + go mediator.sendMNotifyRespInd(mNotifyRespIndFilePath) |
621 | + case msg := <-mediator.outMessage: |
622 | + go mediator.handleOutgoingMessage(msg) |
623 | + case mSendReq := <-mediator.NewMSendReq: |
624 | + go mediator.handleMSendReq(mSendReq) |
625 | + case mSendReqFile := <-mediator.NewMSendReqFile: |
626 | + go mediator.sendMSendReq(mSendReqFile.filePath, mSendReqFile.uuid) |
627 | + case id := <-mediator.modem.IdentityAdded: |
628 | + var err error |
629 | + mediator.telepathyService, err = mmsManager.AddService(id, mediator.modem.Modem, mediator.outMessage, useDeliveryReports) |
630 | + if err != nil { |
631 | + log.Fatal(err) |
632 | + } |
633 | + case id := <-mediator.modem.IdentityRemoved: |
634 | + err := mmsManager.RemoveService(id) |
635 | + if err != nil { |
636 | + log.Fatal(err) |
637 | + } |
638 | + mediator.telepathyService = nil |
639 | + case ok := <-mediator.modem.PushInterfaceAvailable: |
640 | + if ok { |
641 | + if err := mediator.modem.PushAgent.Register(); err != nil { |
642 | + log.Fatal(err) |
643 | + } |
644 | + } else { |
645 | + if err := mediator.modem.PushAgent.Unregister(); err != nil { |
646 | + log.Fatal(err) |
647 | + } |
648 | + } |
649 | + case terminate := <-mediator.terminate: |
650 | + /* |
651 | + close(mediator.terminate) |
652 | + close(mediator.outMessage) |
653 | + close(mediator.NewMNotificationInd) |
654 | + close(mediator.NewMRetrieveConf) |
655 | + close(mediator.NewMRetrieveConfFile) |
656 | + close(mediator.NewMNotifyRespInd) |
657 | + close(mediator.NewMNotifyRespIndFile) |
658 | + close(mediator.NewMSendReq) |
659 | + close(mediator.NewMSendReqFile) |
660 | + */ |
661 | + if terminate { |
662 | + break mediatorLoop |
663 | + } |
664 | + } |
665 | + } |
666 | + log.Print("Ending mediator instance loop for modem") |
667 | +} |
668 | + |
669 | +func (mediator *Mediator) handleMNotificationInd(pushMsg *ofono.PushPDU) { |
670 | + if pushMsg == nil { |
671 | + log.Print("Received nil push") |
672 | + return |
673 | + } |
674 | + dec := mms.NewDecoder(pushMsg.Data) |
675 | + mNotificationInd := mms.NewMNotificationInd() |
676 | + if err := dec.Decode(mNotificationInd); err != nil { |
677 | + log.Println("Unable to decode m-notification.ind: ", err, "with log", dec.GetLog()) |
678 | + return |
679 | + } |
680 | + storage.Create(mNotificationInd.UUID, mNotificationInd.ContentLocation) |
681 | + mediator.NewMNotificationInd <- mNotificationInd |
682 | +} |
683 | + |
684 | +func (mediator *Mediator) handleDeferredDownload(mNotificationInd *mms.MNotificationInd) { |
685 | + //TODO send MessageAdded with status="deferred" and mNotificationInd relevant headers |
686 | +} |
687 | + |
688 | +func (mediator *Mediator) getMRetrieveConf(mNotificationInd *mms.MNotificationInd) { |
689 | + mediator.contextLock.Lock() |
690 | + defer mediator.contextLock.Unlock() |
691 | + |
692 | + preferredContext, _ := mediator.telepathyService.GetPreferredContext() |
693 | + mmsContext, err := mediator.modem.ActivateMMSContext(preferredContext) |
694 | + if err != nil { |
695 | + log.Print("Cannot activate ofono context: ", err) |
696 | + return |
697 | + } |
698 | + defer func() { |
699 | + if err := mediator.modem.DeactivateMMSContext(mmsContext); err != nil { |
700 | + log.Println("Issues while deactivating context:", err) |
701 | + } |
702 | + }() |
703 | + |
704 | + if err := mediator.telepathyService.SetPreferredContext(mmsContext.ObjectPath); err != nil { |
705 | + log.Println("Unable to store the preferred context for MMS:", err) |
706 | + } |
707 | + proxy, err := mmsContext.GetProxy() |
708 | + if err != nil { |
709 | + log.Print("Error retrieving proxy: ", err) |
710 | + return |
711 | + } |
712 | + if filePath, err := mNotificationInd.DownloadContent(proxy.Host, int32(proxy.Port)); err != nil { |
713 | + //TODO telepathy service signal the download error |
714 | + log.Print("Download issues: ", err) |
715 | + return |
716 | + } else { |
717 | + storage.UpdateDownloaded(mNotificationInd.UUID, filePath) |
718 | + } |
719 | + |
720 | + mediator.NewMRetrieveConfFile <- mNotificationInd.UUID |
721 | +} |
722 | + |
723 | +func (mediator *Mediator) handleMRetrieveConf(uuid string) { |
724 | + var filePath string |
725 | + if f, err := storage.GetMMS(uuid); err == nil { |
726 | + filePath = f |
727 | + } else { |
728 | + log.Print("Unable to retrieve MMS: ", err) |
729 | + return |
730 | + } |
731 | + mmsData, err := ioutil.ReadFile(filePath) |
732 | + if err != nil { |
733 | + log.Print("Issues while reading from downloaded file: ", err) |
734 | + return |
735 | + } |
736 | + mRetrieveConf := mms.NewMRetrieveConf(uuid) |
737 | + dec := mms.NewDecoder(mmsData) |
738 | + if err := dec.Decode(mRetrieveConf); err != nil { |
739 | + log.Println("Unable to decode m-retrieve.conf: ", err, "with log", dec.GetLog()) |
740 | + return |
741 | + } |
742 | + mediator.NewMRetrieveConf <- mRetrieveConf |
743 | + if mediator.telepathyService != nil { |
744 | + if err := mediator.telepathyService.IncomingMessageAdded(mRetrieveConf); err != nil { |
745 | + log.Println("Cannot notify telepathy-ofono about new message", err) |
746 | + } |
747 | + } else { |
748 | + log.Print("Not sending recently retrieved message") |
749 | + } |
750 | +} |
751 | + |
752 | +func (mediator *Mediator) handleRetrieved(mRetrieveConf *mms.MRetrieveConf) { |
753 | + mNotifyRespInd := mRetrieveConf.NewMNotifyRespInd(useDeliveryReports) |
754 | + if err := storage.UpdateRetrieved(mNotifyRespInd.UUID); err != nil { |
755 | + log.Print("Can't update mms status: ", err) |
756 | + return |
757 | + } |
758 | + mediator.NewMNotifyRespInd <- mNotifyRespInd |
759 | +} |
760 | + |
761 | +func (mediator *Mediator) handleMNotifyRespInd(mNotifyRespInd *mms.MNotifyRespInd) { |
762 | + f, err := storage.CreateResponseFile(mNotifyRespInd.UUID) |
763 | + if err != nil { |
764 | + log.Print("Unable to create m-notifyresp.ind file for ", mNotifyRespInd.UUID) |
765 | + return |
766 | + } |
767 | + enc := mms.NewEncoder(f) |
768 | + if err := enc.Encode(mNotifyRespInd); err != nil { |
769 | + log.Print("Unable to encode m-notifyresp.ind for ", mNotifyRespInd.UUID) |
770 | + f.Close() |
771 | + return |
772 | + } |
773 | + filePath := f.Name() |
774 | + if err := f.Sync(); err != nil { |
775 | + log.Print("Error while syncing", f.Name(), ": ", err) |
776 | + return |
777 | + } |
778 | + if err := f.Close(); err != nil { |
779 | + log.Print("Error while closing", f.Name(), ": ", err) |
780 | + return |
781 | + } |
782 | + log.Printf("Created %s to handle m-notifyresp.ind for %s", filePath, mNotifyRespInd.UUID) |
783 | + mediator.NewMNotifyRespIndFile <- filePath |
784 | +} |
785 | + |
786 | +func (mediator *Mediator) sendMNotifyRespInd(mNotifyRespIndFile string) { |
787 | + defer os.Remove(mNotifyRespIndFile) |
788 | + if _, err := mediator.uploadFile(mNotifyRespIndFile); err != nil { |
789 | + log.Printf("Cannot upload m-notifyresp.ind encoded file %s to message center: %s", mNotifyRespIndFile, err) |
790 | + } |
791 | +} |
792 | + |
793 | +func (mediator *Mediator) handleOutgoingMessage(msg *telepathy.OutgoingMessage) { |
794 | + var cts []*mms.Attachment |
795 | + for _, att := range msg.Attachments { |
796 | + ct, err := mms.NewAttachment(att.Id, att.ContentType, att.FilePath) |
797 | + if err != nil { |
798 | + log.Print(err) |
799 | + //TODO reply to telepathy ofono with an error |
800 | + return |
801 | + } |
802 | + cts = append(cts, ct) |
803 | + } |
804 | + mSendReq := mms.NewMSendReq(msg.Recipients, cts, useDeliveryReports) |
805 | + if _, err := mediator.telepathyService.ReplySendMessage(msg.Reply, mSendReq.UUID); err != nil { |
806 | + log.Print(err) |
807 | + return |
808 | + } |
809 | + mediator.NewMSendReq <- mSendReq |
810 | +} |
811 | + |
812 | +func (mediator *Mediator) handleMSendReq(mSendReq *mms.MSendReq) { |
813 | + log.Print("Encoding M-Send.Req") |
814 | + f, err := storage.CreateSendFile(mSendReq.UUID) |
815 | + if err != nil { |
816 | + log.Print("Unable to create m-send.req file for ", mSendReq.UUID) |
817 | + return |
818 | + } |
819 | + defer f.Close() |
820 | + enc := mms.NewEncoder(f) |
821 | + if err := enc.Encode(mSendReq); err != nil { |
822 | + log.Print("Unable to encode m-send.req for ", mSendReq.UUID) |
823 | + if err := mediator.telepathyService.MessageStatusChanged(mSendReq.UUID, telepathy.PERMANENT_ERROR); err != nil { |
824 | + log.Println(err) |
825 | + } |
826 | + f.Close() |
827 | + return |
828 | + } |
829 | + filePath := f.Name() |
830 | + if err := f.Sync(); err != nil { |
831 | + log.Print("Error while syncing", f.Name(), ": ", err) |
832 | + return |
833 | + } |
834 | + if err := f.Close(); err != nil { |
835 | + log.Print("Error while closing", f.Name(), ": ", err) |
836 | + return |
837 | + } |
838 | + log.Printf("Created %s to handle m-send.req for %s", filePath, mSendReq.UUID) |
839 | + mediator.sendMSendReq(filePath, mSendReq.UUID) |
840 | +} |
841 | + |
842 | +func (mediator *Mediator) sendMSendReq(mSendReqFile, uuid string) { |
843 | + defer os.Remove(mSendReqFile) |
844 | + defer mediator.telepathyService.MessageDestroy(uuid) |
845 | + mSendConfFile, err := mediator.uploadFile(mSendReqFile) |
846 | + if err != nil { |
847 | + if err := mediator.telepathyService.MessageStatusChanged(uuid, telepathy.TRANSIENT_ERROR); err != nil { |
848 | + log.Println(err) |
849 | + } |
850 | + log.Printf("Cannot upload m-send.req encoded file %s to message center: %s", mSendReqFile, err) |
851 | + return |
852 | + } |
853 | + |
854 | + defer os.Remove(mSendConfFile) |
855 | + mSendConf, err := parseMSendConfFile(mSendConfFile) |
856 | + if err != nil { |
857 | + log.Println("Error while decoding m-send.conf:", err) |
858 | + if err := mediator.telepathyService.MessageStatusChanged(uuid, telepathy.TRANSIENT_ERROR); err != nil { |
859 | + log.Println(err) |
860 | + } |
861 | + return |
862 | + } |
863 | + |
864 | + log.Println("m-send.conf ResponseStatus for", uuid, "is", mSendConf.ResponseStatus) |
865 | + var status string |
866 | + switch mSendConf.Status() { |
867 | + case nil: |
868 | + status = telepathy.SENT |
869 | + case mms.ErrPermanent: |
870 | + status = telepathy.PERMANENT_ERROR |
871 | + case mms.ErrTransient: |
872 | + status = telepathy.TRANSIENT_ERROR |
873 | + } |
874 | + if err := mediator.telepathyService.MessageStatusChanged(uuid, status); err != nil { |
875 | + log.Println(err) |
876 | + } |
877 | +} |
878 | + |
879 | +func parseMSendConfFile(mSendConfFile string) (*mms.MSendConf, error) { |
880 | + b, err := ioutil.ReadFile(mSendConfFile) |
881 | + if err != nil { |
882 | + return nil, err |
883 | + } |
884 | + |
885 | + mSendConf := mms.NewMSendConf() |
886 | + |
887 | + dec := mms.NewDecoder(b) |
888 | + if err := dec.Decode(mSendConf); err != nil { |
889 | + return nil, err |
890 | + } |
891 | + return mSendConf, nil |
892 | +} |
893 | + |
894 | +func (mediator *Mediator) uploadFile(filePath string) (string, error) { |
895 | + mediator.contextLock.Lock() |
896 | + defer mediator.contextLock.Unlock() |
897 | + |
898 | + preferredContext, _ := mediator.telepathyService.GetPreferredContext() |
899 | + mmsContext, err := mediator.modem.ActivateMMSContext(preferredContext) |
900 | + if err != nil { |
901 | + return "", err |
902 | + } |
903 | + if err := mediator.telepathyService.SetPreferredContext(mmsContext.ObjectPath); err != nil { |
904 | + log.Println("Unable to store the preferred context for MMS:", err) |
905 | + } |
906 | + defer func() { |
907 | + if err := mediator.modem.DeactivateMMSContext(mmsContext); err != nil { |
908 | + log.Println("Issues while deactivating context:", err) |
909 | + } |
910 | + }() |
911 | + |
912 | + proxy, err := mmsContext.GetProxy() |
913 | + if err != nil { |
914 | + return "", err |
915 | + } |
916 | + msc, err := mmsContext.GetMessageCenter() |
917 | + if err != nil { |
918 | + return "", err |
919 | + } |
920 | + mSendRespFile, uploadErr := mms.Upload(filePath, msc, proxy.Host, int32(proxy.Port)) |
921 | + |
922 | + return mSendRespFile, uploadErr |
923 | +} |
924 | |
925 | === added directory 'mms' |
926 | === renamed directory 'mms' => 'mms.moved' |
927 | === added file 'mms/attachments.go' |
928 | --- mms/attachments.go 1970-01-01 00:00:00 +0000 |
929 | +++ mms/attachments.go 2015-09-02 10:42:28 +0000 |
930 | @@ -0,0 +1,295 @@ |
931 | +/* |
932 | + * Copyright 2014 Canonical Ltd. |
933 | + * |
934 | + * Authors: |
935 | + * Sergio Schvezov: sergio.schvezov@cannical.com |
936 | + * |
937 | + * This file is part of mms. |
938 | + * |
939 | + * mms is free software; you can redistribute it and/or modify |
940 | + * it under the terms of the GNU General Public License as published by |
941 | + * the Free Software Foundation; version 3. |
942 | + * |
943 | + * mms is distributed in the hope that it will be useful, |
944 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
945 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
946 | + * GNU General Public License for more details. |
947 | + * |
948 | + * You should have received a copy of the GNU General Public License |
949 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
950 | + */ |
951 | + |
952 | +package mms |
953 | + |
954 | +import ( |
955 | + "errors" |
956 | + "fmt" |
957 | + "io/ioutil" |
958 | + "log" |
959 | + "reflect" |
960 | + "strings" |
961 | +) |
962 | + |
963 | +type Attachment struct { |
964 | + MediaType string |
965 | + Type string `encode:"no"` |
966 | + Name string `encode:"no"` |
967 | + FileName string `encode:"no"` |
968 | + Charset string `encode:"no"` |
969 | + Start string `encode:"no"` |
970 | + StartInfo string `encode:"no"` |
971 | + Domain string `encode:"no"` |
972 | + Path string `encode:"no"` |
973 | + Comment string `encode:"no"` |
974 | + ContentLocation string |
975 | + ContentId string |
976 | + Level byte `encode:"no"` |
977 | + Length uint64 `encode:"no"` |
978 | + Size uint64 `encode:"no"` |
979 | + CreationDate uint64 `encode:"no"` |
980 | + ModificationDate uint64 `encode:"no"` |
981 | + ReadDate uint64 `encode:"no"` |
982 | + Offset int `encode:"no"` |
983 | + Secure bool `encode:"no"` |
984 | + Q float64 `encode:"no"` |
985 | + Data []byte `encode:"no"` |
986 | +} |
987 | + |
988 | +func NewAttachment(id, contentType, filePath string) (*Attachment, error) { |
989 | + data, err := ioutil.ReadFile(filePath) |
990 | + if err != nil { |
991 | + return nil, fmt.Errorf("cannot create new ContentType for %s of content type %s on %s: %s", id, contentType, filePath, err) |
992 | + } |
993 | + |
994 | + ct := &Attachment{ |
995 | + ContentId: id, |
996 | + ContentLocation: id, |
997 | + Name: id, |
998 | + Data: data, |
999 | + } |
1000 | + |
1001 | + parts := strings.Split(contentType, ";") |
1002 | + ct.MediaType = strings.TrimSpace(parts[0]) |
1003 | + for i := 1; i < len(parts); i++ { |
1004 | + if field := strings.Split(strings.TrimSpace(parts[i]), "="); len(field) > 1 { |
1005 | + switch strings.TrimSpace(field[0]) { |
1006 | + case "charset": |
1007 | + ct.Charset = strings.TrimSpace(field[1]) |
1008 | + default: |
1009 | + log.Println("Unhandled field in attachment", field[0]) |
1010 | + } |
1011 | + } |
1012 | + } |
1013 | + |
1014 | + if contentType == "application/smil" { |
1015 | + start, err := getSmilStart(data) |
1016 | + if err != nil { |
1017 | + return nil, err |
1018 | + } |
1019 | + ct.ContentId = start |
1020 | + } |
1021 | + return ct, nil |
1022 | +} |
1023 | + |
1024 | +func getSmilStart(smilData []byte) (string, error) { |
1025 | + smilStart := string(smilData) |
1026 | + |
1027 | + i := strings.Index(smilStart, ">") |
1028 | + if i == -1 { |
1029 | + return "", errors.New("cannot find the SMIL Start tag") |
1030 | + } else if i+1 > len(smilData) { |
1031 | + return "", errors.New("buffer overrun while searching for the SMIL Start tag") |
1032 | + } |
1033 | + return smilStart[:i+1], nil |
1034 | +} |
1035 | + |
1036 | +//GetSmil returns the text corresponding to the ContentType that holds the SMIL |
1037 | +func (pdu *MRetrieveConf) GetSmil() (string, error) { |
1038 | + for i := range pdu.Attachments { |
1039 | + if strings.HasPrefix(pdu.Attachments[i].MediaType, "application/smil") { |
1040 | + return string(pdu.Attachments[i].Data), nil |
1041 | + } |
1042 | + } |
1043 | + return "", errors.New("cannot find SMIL data part") |
1044 | +} |
1045 | + |
1046 | +//GetDataParts returns the non SMIL ContentType data parts |
1047 | +func (pdu *MRetrieveConf) GetDataParts() []Attachment { |
1048 | + var dataParts []Attachment |
1049 | + for i := range pdu.Attachments { |
1050 | + if pdu.Attachments[i].MediaType == "application/smil" { |
1051 | + continue |
1052 | + } |
1053 | + dataParts = append(dataParts, pdu.Attachments[i]) |
1054 | + } |
1055 | + return dataParts |
1056 | +} |
1057 | + |
1058 | +func (dec *MMSDecoder) ReadContentTypeParts(reflectedPdu *reflect.Value) error { |
1059 | + var err error |
1060 | + var parts uint64 |
1061 | + if parts, err = dec.ReadUintVar(nil, ""); err != nil { |
1062 | + return err |
1063 | + } |
1064 | + var dataParts []Attachment |
1065 | + dec.log = dec.log + fmt.Sprintf("Number of parts: %d\n", parts) |
1066 | + for i := uint64(0); i < parts; i++ { |
1067 | + headerLen, err := dec.ReadUintVar(nil, "") |
1068 | + if err != nil { |
1069 | + return err |
1070 | + } |
1071 | + dataLen, err := dec.ReadUintVar(nil, "") |
1072 | + if err != nil { |
1073 | + return err |
1074 | + } |
1075 | + headerEnd := dec.Offset + int(headerLen) |
1076 | + dec.log = dec.log + fmt.Sprintf("Attachament len(header): %d - len(data) %d\n", headerLen, dataLen) |
1077 | + var ct Attachment |
1078 | + ct.Offset = headerEnd + 1 |
1079 | + ctReflected := reflect.ValueOf(&ct).Elem() |
1080 | + if err := dec.ReadContentType(&ctReflected); err == nil { |
1081 | + if err := dec.ReadMMSHeaders(&ctReflected, headerEnd); err != nil { |
1082 | + return err |
1083 | + } |
1084 | + } else if err != nil && err.Error() != "WAP message" { //TODO create error type |
1085 | + return err |
1086 | + } |
1087 | + dec.Offset = headerEnd + 1 |
1088 | + if _, err := dec.ReadBoundedBytes(&ctReflected, "Data", dec.Offset+int(dataLen)); err != nil { |
1089 | + return err |
1090 | + } |
1091 | + if ct.MediaType == "application/smil" || strings.HasPrefix(ct.MediaType, "text/plain") || ct.MediaType == "" { |
1092 | + dec.log = dec.log + fmt.Sprintf("%s\n", ct.Data) |
1093 | + } |
1094 | + if ct.Charset != "" { |
1095 | + ct.MediaType = ct.MediaType + ";charset=" + ct.Charset |
1096 | + } |
1097 | + dataParts = append(dataParts, ct) |
1098 | + } |
1099 | + dataPartsR := reflect.ValueOf(dataParts) |
1100 | + reflectedPdu.FieldByName("Attachments").Set(dataPartsR) |
1101 | + |
1102 | + return nil |
1103 | +} |
1104 | + |
1105 | +func (dec *MMSDecoder) ReadMMSHeaders(ctMember *reflect.Value, headerEnd int) error { |
1106 | + for dec.Offset < headerEnd { |
1107 | + var err error |
1108 | + param, _ := dec.ReadInteger(nil, "") |
1109 | + switch param { |
1110 | + case MMS_PART_CONTENT_LOCATION: |
1111 | + _, err = dec.ReadString(ctMember, "ContentLocation") |
1112 | + case MMS_PART_CONTENT_ID: |
1113 | + _, err = dec.ReadString(ctMember, "ContentId") |
1114 | + default: |
1115 | + break |
1116 | + } |
1117 | + if err != nil { |
1118 | + return err |
1119 | + } |
1120 | + } |
1121 | + return nil |
1122 | +} |
1123 | + |
1124 | +func (dec *MMSDecoder) ReadContentType(ctMember *reflect.Value) error { |
1125 | + if dec.Offset+1 >= len(dec.Data) { |
1126 | + return fmt.Errorf("message ended prematurely, offset: %d and payload length is %d", dec.Offset, len(dec.Data)) |
1127 | + } |
1128 | + // These call the same function |
1129 | + if next := dec.Data[dec.Offset+1]; next&SHORT_FILTER != 0 { |
1130 | + return dec.ReadMediaType(ctMember, "MediaType") |
1131 | + } else if next >= TEXT_MIN && next <= TEXT_MAX { |
1132 | + return dec.ReadMediaType(ctMember, "MediaType") |
1133 | + } |
1134 | + |
1135 | + var err error |
1136 | + var length uint64 |
1137 | + if length, err = dec.ReadLength(ctMember); err != nil { |
1138 | + return err |
1139 | + } |
1140 | + dec.log = dec.log + fmt.Sprintf("Content Type Length: %d\n", length) |
1141 | + endOffset := int(length) + dec.Offset |
1142 | + |
1143 | + if err := dec.ReadMediaType(ctMember, "MediaType"); err != nil { |
1144 | + return err |
1145 | + } |
1146 | + |
1147 | + for dec.Offset < len(dec.Data) && dec.Offset < endOffset { |
1148 | + param, _ := dec.ReadInteger(nil, "") |
1149 | + switch param { |
1150 | + case WSP_PARAMETER_TYPE_Q: |
1151 | + err = dec.ReadQ(ctMember) |
1152 | + case WSP_PARAMETER_TYPE_CHARSET: |
1153 | + _, err = dec.ReadCharset(ctMember, "Charset") |
1154 | + case WSP_PARAMETER_TYPE_LEVEL: |
1155 | + _, err = dec.ReadShortInteger(ctMember, "Level") |
1156 | + case WSP_PARAMETER_TYPE_TYPE: |
1157 | + _, err = dec.ReadInteger(ctMember, "Type") |
1158 | + case WSP_PARAMETER_TYPE_NAME_DEFUNCT: |
1159 | + log.Println("Using deprecated Name header") |
1160 | + _, err = dec.ReadString(ctMember, "Name") |
1161 | + case WSP_PARAMETER_TYPE_FILENAME_DEFUNCT: |
1162 | + log.Println("Using deprecated FileName header") |
1163 | + _, err = dec.ReadString(ctMember, "FileName") |
1164 | + case WSP_PARAMETER_TYPE_DIFFERENCES: |
1165 | + err = errors.New("Unhandled Differences") |
1166 | + case WSP_PARAMETER_TYPE_PADDING: |
1167 | + dec.ReadShortInteger(nil, "") |
1168 | + case WSP_PARAMETER_TYPE_CONTENT_TYPE: |
1169 | + _, err = dec.ReadString(ctMember, "Type") |
1170 | + case WSP_PARAMETER_TYPE_START_DEFUNCT: |
1171 | + log.Println("Using deprecated Start header") |
1172 | + _, err = dec.ReadString(ctMember, "Start") |
1173 | + case WSP_PARAMETER_TYPE_START_INFO_DEFUNCT: |
1174 | + log.Println("Using deprecated StartInfo header") |
1175 | + _, err = dec.ReadString(ctMember, "StartInfo") |
1176 | + case WSP_PARAMETER_TYPE_COMMENT_DEFUNCT: |
1177 | + log.Println("Using deprecated Comment header") |
1178 | + _, err = dec.ReadString(ctMember, "Comment") |
1179 | + case WSP_PARAMETER_TYPE_DOMAIN_DEFUNCT: |
1180 | + log.Println("Using deprecated Domain header") |
1181 | + _, err = dec.ReadString(ctMember, "Domain") |
1182 | + case WSP_PARAMETER_TYPE_MAX_AGE: |
1183 | + err = errors.New("Unhandled Max Age") |
1184 | + case WSP_PARAMETER_TYPE_PATH_DEFUNCT: |
1185 | + log.Println("Using deprecated Path header") |
1186 | + _, err = dec.ReadString(ctMember, "Path") |
1187 | + case WSP_PARAMETER_TYPE_SECURE: |
1188 | + log.Println("Unhandled Secure header detected") |
1189 | + case WSP_PARAMETER_TYPE_SEC: |
1190 | + v, _ := dec.ReadShortInteger(nil, "") |
1191 | + log.Println("Using deprecated and unhandled Sec header with value", v) |
1192 | + case WSP_PARAMETER_TYPE_MAC: |
1193 | + err = errors.New("Unhandled MAC") |
1194 | + case WSP_PARAMETER_TYPE_CREATION_DATE: |
1195 | + case WSP_PARAMETER_TYPE_MODIFICATION_DATE: |
1196 | + case WSP_PARAMETER_TYPE_READ_DATE: |
1197 | + err = errors.New("Unhandled Date parameters") |
1198 | + case WSP_PARAMETER_TYPE_SIZE: |
1199 | + _, err = dec.ReadInteger(ctMember, "Size") |
1200 | + case WSP_PARAMETER_TYPE_NAME: |
1201 | + _, err = dec.ReadString(ctMember, "Name") |
1202 | + case WSP_PARAMETER_TYPE_FILENAME: |
1203 | + _, err = dec.ReadString(ctMember, "FileName") |
1204 | + case WSP_PARAMETER_TYPE_START: |
1205 | + _, err = dec.ReadString(ctMember, "Start") |
1206 | + case WSP_PARAMETER_TYPE_START_INFO: |
1207 | + _, err = dec.ReadString(ctMember, "StartInfo") |
1208 | + case WSP_PARAMETER_TYPE_COMMENT: |
1209 | + _, err = dec.ReadString(ctMember, "Comment") |
1210 | + case WSP_PARAMETER_TYPE_DOMAIN: |
1211 | + _, err = dec.ReadString(ctMember, "Domain") |
1212 | + case WSP_PARAMETER_TYPE_PATH: |
1213 | + _, err = dec.ReadString(ctMember, "Path") |
1214 | + case WSP_PARAMETER_TYPE_UNTYPED: |
1215 | + v, _ := dec.ReadString(nil, "") |
1216 | + log.Println("Unhandled Secure header detected with value", v) |
1217 | + default: |
1218 | + err = fmt.Errorf("Unhandled parameter %#x == %d at offset %d", param, param, dec.Offset) |
1219 | + } |
1220 | + if err != nil { |
1221 | + return err |
1222 | + } |
1223 | + } |
1224 | + return nil |
1225 | +} |
1226 | |
1227 | === added directory 'mms/decode-cli' |
1228 | === added file 'mms/decode-cli/decode.go' |
1229 | --- mms/decode-cli/decode.go 1970-01-01 00:00:00 +0000 |
1230 | +++ mms/decode-cli/decode.go 2015-09-02 10:42:28 +0000 |
1231 | @@ -0,0 +1,70 @@ |
1232 | +package main |
1233 | + |
1234 | +import ( |
1235 | + "fmt" |
1236 | + "io/ioutil" |
1237 | + "os" |
1238 | + "path/filepath" |
1239 | + |
1240 | + "launchpad.net/nuntium/mms" |
1241 | +) |
1242 | + |
1243 | +func main() { |
1244 | + var targetPath string |
1245 | + if len(os.Args) < 2 { |
1246 | + usage() |
1247 | + } else if len(os.Args) == 3 { |
1248 | + targetPath = os.Args[2] |
1249 | + } else if len(os.Args) > 3 { |
1250 | + usage() |
1251 | + } |
1252 | + |
1253 | + mmsFile := os.Args[1] |
1254 | + if _, err := os.Stat(mmsFile); os.IsNotExist(err) { |
1255 | + fmt.Printf("File argument %s does no exist\n", mmsFile) |
1256 | + os.Exit(1) |
1257 | + } |
1258 | + |
1259 | + mmsData, err := ioutil.ReadFile(mmsFile) |
1260 | + if err != nil { |
1261 | + fmt.Println(err) |
1262 | + os.Exit(1) |
1263 | + } |
1264 | + |
1265 | + retConfHdr := mms.NewMRetrieveConf(mmsFile) |
1266 | + dec := mms.NewDecoder(mmsData) |
1267 | + if err := dec.Decode(retConfHdr); err != nil { |
1268 | + fmt.Println(err) |
1269 | + os.Exit(1) |
1270 | + } |
1271 | + |
1272 | + if targetPath != "" { |
1273 | + fmt.Println("Saving to", targetPath) |
1274 | + writeParts(targetPath, retConfHdr.Attachments) |
1275 | + } |
1276 | + |
1277 | + fmt.Println(dec.GetLog()) |
1278 | +} |
1279 | + |
1280 | +func usage() { |
1281 | + fmt.Printf("Usage: %s [mms] [decode dir]\n", os.Args[0]) |
1282 | + os.Exit(1) |
1283 | +} |
1284 | + |
1285 | +func writeParts(targetPath string, parts []mms.Attachment) { |
1286 | + if fi, err := os.Stat(targetPath); err != nil { |
1287 | + if err := os.MkdirAll(targetPath, 0755); err != nil { |
1288 | + fmt.Println(err) |
1289 | + } |
1290 | + } else if !fi.IsDir() { |
1291 | + fmt.Println(targetPath, "is not a directory") |
1292 | + os.Exit(1) |
1293 | + } |
1294 | + |
1295 | + for i, _ := range parts { |
1296 | + if parts[i].Name != "" { |
1297 | + ioutil.WriteFile(filepath.Join(targetPath, parts[i].Name), parts[i].Data, 0644) |
1298 | + } |
1299 | + fmt.Println(parts[i].MediaType, parts[i].Name) |
1300 | + } |
1301 | +} |
1302 | |
1303 | === added file 'mms/decoder.go' |
1304 | --- mms/decoder.go 1970-01-01 00:00:00 +0000 |
1305 | +++ mms/decoder.go 2015-09-02 10:42:28 +0000 |
1306 | @@ -0,0 +1,437 @@ |
1307 | +/* |
1308 | + * Copyright 2014 Canonical Ltd. |
1309 | + * |
1310 | + * Authors: |
1311 | + * Sergio Schvezov: sergio.schvezov@cannical.com |
1312 | + * |
1313 | + * This file is part of mms. |
1314 | + * |
1315 | + * mms is free software; you can redistribute it and/or modify |
1316 | + * it under the terms of the GNU General Public License as published by |
1317 | + * the Free Software Foundation; version 3. |
1318 | + * |
1319 | + * mms is distributed in the hope that it will be useful, |
1320 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1321 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1322 | + * GNU General Public License for more details. |
1323 | + * |
1324 | + * You should have received a copy of the GNU General Public License |
1325 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1326 | + */ |
1327 | + |
1328 | +package mms |
1329 | + |
1330 | +import ( |
1331 | + "fmt" |
1332 | + "reflect" |
1333 | +) |
1334 | + |
1335 | +func NewDecoder(data []byte) *MMSDecoder { |
1336 | + return &MMSDecoder{Data: data} |
1337 | +} |
1338 | + |
1339 | +type MMSDecoder struct { |
1340 | + Data []byte |
1341 | + Offset int |
1342 | + log string |
1343 | +} |
1344 | + |
1345 | +func (dec *MMSDecoder) ReadEncodedString(reflectedPdu *reflect.Value, hdr string) (string, error) { |
1346 | + var length uint64 |
1347 | + var err error |
1348 | + switch { |
1349 | + case dec.Data[dec.Offset+1] < SHORT_LENGTH_MAX: |
1350 | + var l byte |
1351 | + l, err = dec.ReadShortInteger(nil, "") |
1352 | + length = uint64(l) |
1353 | + case dec.Data[dec.Offset+1] == LENGTH_QUOTE: |
1354 | + dec.Offset++ |
1355 | + length, err = dec.ReadUintVar(nil, "") |
1356 | + } |
1357 | + if err != nil { |
1358 | + return "", err |
1359 | + } |
1360 | + if length != 0 { |
1361 | + charset, err := dec.ReadCharset(nil, "") |
1362 | + if err != nil { |
1363 | + return "", err |
1364 | + } |
1365 | + dec.log = dec.log + fmt.Sprintf("Next string encoded with: %s\n", charset) |
1366 | + } |
1367 | + var str string |
1368 | + if str, err = dec.ReadString(reflectedPdu, hdr); err != nil { |
1369 | + return "", err |
1370 | + } |
1371 | + return str, nil |
1372 | +} |
1373 | + |
1374 | +func (dec *MMSDecoder) ReadQ(reflectedPdu *reflect.Value) error { |
1375 | + v, err := dec.ReadUintVar(nil, "") |
1376 | + if err != nil { |
1377 | + return err |
1378 | + } |
1379 | + q := float64(v) |
1380 | + if q > 100 { |
1381 | + q = (q - 100) / 1000 |
1382 | + } else { |
1383 | + q = (q - 1) / 100 |
1384 | + } |
1385 | + reflectedPdu.FieldByName("Q").SetFloat(q) |
1386 | + return nil |
1387 | +} |
1388 | + |
1389 | +// ReadLength reads the length from the next position according to section |
1390 | +// 8.4.2.2 of WAP-230-WSP-20010705-a. |
1391 | +// |
1392 | +// Value-length = Short-length | (Length-quote Length) |
1393 | +// ; Value length is used to indicate the length of the value to follow |
1394 | +// Short-length = <Any octet 0-30> (0x7f to check for short) |
1395 | +// Length-quote = <Octet 31> |
1396 | +// Length = Uintvar-integer |
1397 | +func (dec *MMSDecoder) ReadLength(reflectedPdu *reflect.Value) (length uint64, err error) { |
1398 | + switch { |
1399 | + case dec.Data[dec.Offset+1]&0x7f <= SHORT_LENGTH_MAX: |
1400 | + l, err := dec.ReadShortInteger(nil, "") |
1401 | + v := uint64(l) |
1402 | + if reflectedPdu != nil { |
1403 | + reflectedPdu.FieldByName("Length").SetUint(v) |
1404 | + } |
1405 | + return v, err |
1406 | + case dec.Data[dec.Offset+1] == LENGTH_QUOTE: |
1407 | + dec.Offset++ |
1408 | + var hdr string |
1409 | + if reflectedPdu != nil { |
1410 | + hdr = "Length" |
1411 | + } |
1412 | + return dec.ReadUintVar(reflectedPdu, hdr) |
1413 | + } |
1414 | + return 0, fmt.Errorf("Unhandled length %#x @%d", dec.Data[dec.Offset+1], dec.Offset) |
1415 | +} |
1416 | + |
1417 | +func (dec *MMSDecoder) ReadCharset(reflectedPdu *reflect.Value, hdr string) (string, error) { |
1418 | + var charset string |
1419 | + |
1420 | + if dec.Data[dec.Offset] == ANY_CHARSET { |
1421 | + dec.Offset++ |
1422 | + charset = "*" |
1423 | + } else { |
1424 | + charCode, err := dec.ReadInteger(nil, "") |
1425 | + if err != nil { |
1426 | + return "", err |
1427 | + } |
1428 | + var ok bool |
1429 | + if charset, ok = CHARSETS[charCode]; !ok { |
1430 | + return "", fmt.Errorf("Cannot find matching charset for %#x == %d", charCode, charCode) |
1431 | + } |
1432 | + } |
1433 | + if hdr != "" { |
1434 | + reflectedPdu.FieldByName("Charset").SetString(charset) |
1435 | + } |
1436 | + return charset, nil |
1437 | +} |
1438 | + |
1439 | +func (dec *MMSDecoder) ReadMediaType(reflectedPdu *reflect.Value, hdr string) (err error) { |
1440 | + var mediaType string |
1441 | + origOffset := dec.Offset |
1442 | + if dec.Data[dec.Offset+1] >= TEXT_MIN && dec.Data[dec.Offset+1] <= TEXT_MAX { |
1443 | + if mediaType, err = dec.ReadString(nil, ""); err != nil { |
1444 | + return err |
1445 | + } |
1446 | + } else if mt, err := dec.ReadInteger(nil, ""); err == nil && len(CONTENT_TYPES) > int(mt) { |
1447 | + mediaType = CONTENT_TYPES[mt] |
1448 | + } else { |
1449 | + return fmt.Errorf("cannot decode media type for field beginning with %#x@%d", dec.Data[origOffset], origOffset) |
1450 | + } |
1451 | + |
1452 | + reflectedPdu.FieldByName(hdr).SetString(mediaType) |
1453 | + dec.log = dec.log + fmt.Sprintf("%s: %s\n", hdr, mediaType) |
1454 | + return nil |
1455 | +} |
1456 | + |
1457 | +func (dec *MMSDecoder) ReadTo(reflectedPdu *reflect.Value) error { |
1458 | + toField, err := dec.ReadEncodedString(reflectedPdu, "") |
1459 | + if err != nil { |
1460 | + return err |
1461 | + } |
1462 | + to := reflectedPdu.FieldByName("To").String() |
1463 | + if to != "" { |
1464 | + toField = toField + "," + to |
1465 | + } |
1466 | + reflectedPdu.FieldByName("To").SetString(toField) |
1467 | + return err |
1468 | +} |
1469 | + |
1470 | +func (dec *MMSDecoder) ReadString(reflectedPdu *reflect.Value, hdr string) (string, error) { |
1471 | + dec.Offset++ |
1472 | + if dec.Data[dec.Offset] == 34 { // Skip the quote char(34) == " |
1473 | + dec.Offset++ |
1474 | + } |
1475 | + begin := dec.Offset |
1476 | + for ; len(dec.Data) > dec.Offset; dec.Offset++ { |
1477 | + if dec.Data[dec.Offset] == 0 { |
1478 | + break |
1479 | + } |
1480 | + } |
1481 | + if len(dec.Data) == dec.Offset { |
1482 | + return "", fmt.Errorf("reached end of data while trying to read string: %s", dec.Data[begin:]) |
1483 | + } |
1484 | + v := string(dec.Data[begin:dec.Offset]) |
1485 | + if hdr != "" { |
1486 | + reflectedPdu.FieldByName(hdr).SetString(v) |
1487 | + dec.log = dec.log + fmt.Sprintf("Setting %s to %s\n", hdr, v) |
1488 | + } |
1489 | + return v, nil |
1490 | +} |
1491 | + |
1492 | +func (dec *MMSDecoder) ReadShortInteger(reflectedPdu *reflect.Value, hdr string) (byte, error) { |
1493 | + dec.Offset++ |
1494 | + /* |
1495 | + TODO fix use of short when not short |
1496 | + if dec.Data[dec.Offset] & 0x80 == 0 { |
1497 | + return 0, fmt.Errorf("Data on offset %d with value %#x is not a short integer", dec.Offset, dec.Data[dec.Offset]) |
1498 | + } |
1499 | + */ |
1500 | + v := dec.Data[dec.Offset] & 0x7F |
1501 | + if hdr != "" { |
1502 | + reflectedPdu.FieldByName(hdr).SetUint(uint64(v)) |
1503 | + dec.log = dec.log + fmt.Sprintf("Setting %s to %#x == %d\n", hdr, v, v) |
1504 | + } |
1505 | + return v, nil |
1506 | +} |
1507 | + |
1508 | +func (dec *MMSDecoder) ReadByte(reflectedPdu *reflect.Value, hdr string) (byte, error) { |
1509 | + dec.Offset++ |
1510 | + v := dec.Data[dec.Offset] |
1511 | + if hdr != "" { |
1512 | + reflectedPdu.FieldByName(hdr).SetUint(uint64(v)) |
1513 | + dec.log = dec.log + fmt.Sprintf("Setting %s to %#x == %d\n", hdr, v, v) |
1514 | + } |
1515 | + return v, nil |
1516 | +} |
1517 | + |
1518 | +func (dec *MMSDecoder) ReadBytes(reflectedPdu *reflect.Value, hdr string) ([]byte, error) { |
1519 | + dec.Offset++ |
1520 | + v := []byte(dec.Data[dec.Offset:]) |
1521 | + if hdr != "" { |
1522 | + reflectedPdu.FieldByName(hdr).SetBytes(v) |
1523 | + dec.log = dec.log + fmt.Sprintf("Setting %s to %#x == %d\n", hdr, v, v) |
1524 | + } |
1525 | + return v, nil |
1526 | +} |
1527 | + |
1528 | +func (dec *MMSDecoder) ReadBoundedBytes(reflectedPdu *reflect.Value, hdr string, end int) ([]byte, error) { |
1529 | + v := []byte(dec.Data[dec.Offset:end]) |
1530 | + if hdr != "" { |
1531 | + reflectedPdu.FieldByName(hdr).SetBytes(v) |
1532 | + } |
1533 | + dec.Offset = end - 1 |
1534 | + return v, nil |
1535 | +} |
1536 | + |
1537 | +// A UintVar is a variable lenght uint of up to 5 octects long where |
1538 | +// more octects available are indicated with the most significant bit |
1539 | +// set to 1 |
1540 | +func (dec *MMSDecoder) ReadUintVar(reflectedPdu *reflect.Value, hdr string) (value uint64, err error) { |
1541 | + dec.Offset++ |
1542 | + for dec.Data[dec.Offset]>>7 == 0x01 { |
1543 | + value = value << 7 |
1544 | + value |= uint64(dec.Data[dec.Offset] & 0x7F) |
1545 | + dec.Offset++ |
1546 | + } |
1547 | + |
1548 | + value = value << 7 |
1549 | + value |= uint64(dec.Data[dec.Offset] & 0x7F) |
1550 | + if hdr != "" { |
1551 | + reflectedPdu.FieldByName(hdr).SetUint(value) |
1552 | + dec.log = dec.log + fmt.Sprintf("Setting %s to %d\n", hdr, value) |
1553 | + } |
1554 | + return value, nil |
1555 | +} |
1556 | + |
1557 | +func (dec *MMSDecoder) ReadInteger(reflectedPdu *reflect.Value, hdr string) (uint64, error) { |
1558 | + param := dec.Data[dec.Offset+1] |
1559 | + var v uint64 |
1560 | + var err error |
1561 | + switch { |
1562 | + case param&0x80 != 0: |
1563 | + var vv byte |
1564 | + vv, err = dec.ReadShortInteger(nil, "") |
1565 | + v = uint64(vv) |
1566 | + default: |
1567 | + v, err = dec.ReadLongInteger(nil, "") |
1568 | + } |
1569 | + if hdr != "" { |
1570 | + reflectedPdu.FieldByName(hdr).SetUint(v) |
1571 | + dec.log = dec.log + fmt.Sprintf("Setting %s to %d\n", hdr, v) |
1572 | + } |
1573 | + return v, err |
1574 | +} |
1575 | + |
1576 | +func (dec *MMSDecoder) ReadLongInteger(reflectedPdu *reflect.Value, hdr string) (uint64, error) { |
1577 | + dec.Offset++ |
1578 | + size := int(dec.Data[dec.Offset]) |
1579 | + if size > SHORT_LENGTH_MAX { |
1580 | + return 0, fmt.Errorf("cannot encode long integer, lenght was %d but expected %d", size, SHORT_LENGTH_MAX) |
1581 | + } |
1582 | + dec.Offset++ |
1583 | + end := dec.Offset + size |
1584 | + var v uint64 |
1585 | + for ; dec.Offset < end; dec.Offset++ { |
1586 | + v = v << 8 |
1587 | + v |= uint64(dec.Data[dec.Offset]) |
1588 | + } |
1589 | + dec.Offset-- |
1590 | + if hdr != "" { |
1591 | + reflectedPdu.FieldByName(hdr).SetUint(uint64(v)) |
1592 | + dec.log = dec.log + fmt.Sprintf("Setting %s to %d\n", hdr, v) |
1593 | + } |
1594 | + return v, nil |
1595 | +} |
1596 | + |
1597 | +//getParam reads the next parameter to decode and returns it if it's well known |
1598 | +//or just decodes and discards if it's application specific, if the latter is |
1599 | +//the case it also returns false |
1600 | +func (dec *MMSDecoder) getParam() (byte, bool, error) { |
1601 | + if dec.Data[dec.Offset]&0x80 != 0 { |
1602 | + return dec.Data[dec.Offset] & 0x7f, true, nil |
1603 | + } else { |
1604 | + var param, value string |
1605 | + var err error |
1606 | + dec.Offset-- |
1607 | + //Read the parameter name |
1608 | + if param, err = dec.ReadString(nil, ""); err != nil { |
1609 | + return 0, false, err |
1610 | + } |
1611 | + //Read the parameter value |
1612 | + if value, err = dec.ReadString(nil, ""); err != nil { |
1613 | + return 0, false, err |
1614 | + } |
1615 | + dec.log = dec.log + fmt.Sprintf("Ignoring application header: %#x: %s", param, value) |
1616 | + return 0, false, nil |
1617 | + } |
1618 | +} |
1619 | + |
1620 | +func (dec *MMSDecoder) Decode(pdu MMSReader) (err error) { |
1621 | + reflectedPdu := reflect.ValueOf(pdu).Elem() |
1622 | + moreHdrToRead := true |
1623 | + //fmt.Printf("len data: %d, data: %x\n", len(dec.Data), dec.Data) |
1624 | + for ; (dec.Offset < len(dec.Data)) && moreHdrToRead; dec.Offset++ { |
1625 | + //fmt.Printf("offset %d, value: %x\n", dec.Offset, dec.Data[dec.Offset]) |
1626 | + err = nil |
1627 | + param, needsDecoding, err := dec.getParam() |
1628 | + if err != nil { |
1629 | + return err |
1630 | + } else if !needsDecoding { |
1631 | + continue |
1632 | + } |
1633 | + switch param { |
1634 | + case X_MMS_MESSAGE_TYPE: |
1635 | + dec.Offset++ |
1636 | + expectedType := byte(reflectedPdu.FieldByName("Type").Uint()) |
1637 | + parsedType := dec.Data[dec.Offset] |
1638 | + //Unknown message types will be discarded. OMA-WAP-MMS-ENC-v1.1 section 7.2.16 |
1639 | + if parsedType != expectedType { |
1640 | + err = fmt.Errorf("Expected message type %x got %x", expectedType, parsedType) |
1641 | + } |
1642 | + case FROM: |
1643 | + dec.Offset++ |
1644 | + size := int(dec.Data[dec.Offset]) |
1645 | + dec.Offset++ |
1646 | + token := dec.Data[dec.Offset] |
1647 | + switch token { |
1648 | + case TOKEN_INSERT_ADDRESS: |
1649 | + break |
1650 | + case TOKEN_ADDRESS_PRESENT: |
1651 | + // TODO add check for /TYPE=PLMN |
1652 | + var from string |
1653 | + from, err = dec.ReadString(&reflectedPdu, "From") |
1654 | + // size - 2 == size - token - '0' |
1655 | + if len(from) != size-2 { |
1656 | + err = fmt.Errorf("From field is %d but expected size is %d", len(from), size-2) |
1657 | + } |
1658 | + default: |
1659 | + err = fmt.Errorf("Unhandled token address in from field %x", token) |
1660 | + } |
1661 | + case X_MMS_EXPIRY: |
1662 | + dec.Offset++ |
1663 | + size := int(dec.Data[dec.Offset]) |
1664 | + dec.Offset++ |
1665 | + token := dec.Data[dec.Offset] |
1666 | + dec.Offset++ |
1667 | + var val uint |
1668 | + endOffset := dec.Offset + size - 2 |
1669 | + for ; dec.Offset < endOffset; dec.Offset++ { |
1670 | + val = (val << 8) | uint(dec.Data[dec.Offset]) |
1671 | + } |
1672 | + // TODO add switch case for token |
1673 | + dec.log = dec.log + fmt.Sprintf("Expiry token: %x\n", token) |
1674 | + reflectedPdu.FieldByName("Expiry").SetUint(uint64(val)) |
1675 | + dec.log = dec.log + fmt.Sprintf("Message Expiry %d, %x\n", val, dec.Data[dec.Offset]) |
1676 | + case X_MMS_TRANSACTION_ID: |
1677 | + _, err = dec.ReadString(&reflectedPdu, "TransactionId") |
1678 | + case CONTENT_TYPE: |
1679 | + ctMember := reflectedPdu.FieldByName("Content") |
1680 | + if err = dec.ReadContentType(&ctMember); err != nil { |
1681 | + return err |
1682 | + } |
1683 | + //application/vnd.wap.multipart.related and others |
1684 | + if ctMember.FieldByName("MediaType").String() != "text/plain" { |
1685 | + err = dec.ReadContentTypeParts(&reflectedPdu) |
1686 | + } else { |
1687 | + dec.Offset++ |
1688 | + _, err = dec.ReadBoundedBytes(&reflectedPdu, "Data", len(dec.Data)) |
1689 | + } |
1690 | + moreHdrToRead = false |
1691 | + case X_MMS_CONTENT_LOCATION: |
1692 | + _, err = dec.ReadString(&reflectedPdu, "ContentLocation") |
1693 | + moreHdrToRead = false |
1694 | + case MESSAGE_ID: |
1695 | + _, err = dec.ReadString(&reflectedPdu, "MessageId") |
1696 | + case SUBJECT: |
1697 | + _, err = dec.ReadEncodedString(&reflectedPdu, "Subject") |
1698 | + case TO: |
1699 | + err = dec.ReadTo(&reflectedPdu) |
1700 | + case CC: |
1701 | + _, err = dec.ReadEncodedString(&reflectedPdu, "Cc") |
1702 | + case X_MMS_REPLY_CHARGING_ID: |
1703 | + _, err = dec.ReadString(&reflectedPdu, "ReplyChargingId") |
1704 | + case X_MMS_RETRIEVE_TEXT: |
1705 | + _, err = dec.ReadString(&reflectedPdu, "RetrieveText") |
1706 | + case X_MMS_MMS_VERSION: |
1707 | + _, err = dec.ReadShortInteger(&reflectedPdu, "Version") |
1708 | + case X_MMS_MESSAGE_CLASS: |
1709 | + //TODO implement Token text form |
1710 | + _, err = dec.ReadByte(&reflectedPdu, "Class") |
1711 | + case X_MMS_REPLY_CHARGING: |
1712 | + _, err = dec.ReadByte(&reflectedPdu, "ReplyCharging") |
1713 | + case X_MMS_REPLY_CHARGING_DEADLINE: |
1714 | + _, err = dec.ReadByte(&reflectedPdu, "ReplyChargingDeadLine") |
1715 | + case X_MMS_PRIORITY: |
1716 | + _, err = dec.ReadByte(&reflectedPdu, "Priority") |
1717 | + case X_MMS_RETRIEVE_STATUS: |
1718 | + _, err = dec.ReadByte(&reflectedPdu, "RetrieveStatus") |
1719 | + case X_MMS_RESPONSE_STATUS: |
1720 | + _, err = dec.ReadByte(&reflectedPdu, "ResponseStatus") |
1721 | + case X_MMS_RESPONSE_TEXT: |
1722 | + _, err = dec.ReadString(&reflectedPdu, "ResponseText") |
1723 | + case X_MMS_DELIVERY_REPORT: |
1724 | + _, err = dec.ReadByte(&reflectedPdu, "DeliveryReport") |
1725 | + case X_MMS_READ_REPORT: |
1726 | + _, err = dec.ReadByte(&reflectedPdu, "ReadReport") |
1727 | + case X_MMS_MESSAGE_SIZE: |
1728 | + _, err = dec.ReadLongInteger(&reflectedPdu, "Size") |
1729 | + case DATE: |
1730 | + _, err = dec.ReadLongInteger(&reflectedPdu, "Date") |
1731 | + default: |
1732 | + return fmt.Errorf("Unhandled byte: %#0x\tdec: %d\tdec.Offset: %d ... decoded so far: %s", param, param, dec.Offset) |
1733 | + } |
1734 | + if err != nil { |
1735 | + return err |
1736 | + } |
1737 | + } |
1738 | + return nil |
1739 | +} |
1740 | + |
1741 | +func (dec *MMSDecoder) GetLog() string { |
1742 | + return dec.log |
1743 | +} |
1744 | |
1745 | === added file 'mms/decoder_payload_test.go' |
1746 | --- mms/decoder_payload_test.go 1970-01-01 00:00:00 +0000 |
1747 | +++ mms/decoder_payload_test.go 2015-09-02 10:42:28 +0000 |
1748 | @@ -0,0 +1,56 @@ |
1749 | +/* |
1750 | + * Copyright 2014 Canonical Ltd. |
1751 | + * |
1752 | + * Authors: |
1753 | + * Sergio Schvezov: sergio.schvezov@cannical.com |
1754 | + * |
1755 | + * This file is part of mms. |
1756 | + * |
1757 | + * mms is free software; you can redistribute it and/or modify |
1758 | + * it under the terms of the GNU General Public License as published by |
1759 | + * the Free Software Foundation; version 3. |
1760 | + * |
1761 | + * mms is distributed in the hope that it will be useful, |
1762 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1763 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1764 | + * GNU General Public License for more details. |
1765 | + * |
1766 | + * You should have received a copy of the GNU General Public License |
1767 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1768 | + */ |
1769 | + |
1770 | +package mms |
1771 | + |
1772 | +import ( |
1773 | + "io/ioutil" |
1774 | + |
1775 | + . "launchpad.net/gocheck" |
1776 | +) |
1777 | + |
1778 | +type PayloadDecoderTestSuite struct{} |
1779 | + |
1780 | +var _ = Suite(&PayloadDecoderTestSuite{}) |
1781 | + |
1782 | +func (s *PayloadDecoderTestSuite) TestDecodeSuccessfulMSendConf(c *C) { |
1783 | + inputBytes, err := ioutil.ReadFile("test_payloads/m-send.conf_success") |
1784 | + c.Assert(err, IsNil) |
1785 | + |
1786 | + mSendConf := NewMSendConf() |
1787 | + dec := NewDecoder(inputBytes) |
1788 | + err = dec.Decode(mSendConf) |
1789 | + c.Assert(err, IsNil) |
1790 | + c.Check(mSendConf.ResponseStatus, Equals, ResponseStatusOk) |
1791 | + c.Check(mSendConf.TransactionId, Equals, "ad6babe2628710c443cdeb3ff39679ac") |
1792 | +} |
1793 | + |
1794 | +func (s *PayloadDecoderTestSuite) TestDecodeInvalidMSendConf(c *C) { |
1795 | + inputBytes := []byte(`<html><head><title>719</title><meta http-equiv="Cache-Control" content="max-age=0" /><meta http-equiv="Cache-control" content="no-cache" /></head><body><h3 align="center">Disculpe,ha ocurrido un error: Failure to Query from Radius Server</h3><br/><p>Por favor, regrese al menu anterior o acceda al siguiente link.<br/></p><ul><li><a href="http://wap.personal.com.ar/"><strong>Home Personal</strong></a></li></ul></body></html>^M`) |
1796 | + |
1797 | + mSendConf := NewMSendConf() |
1798 | + dec := NewDecoder(inputBytes) |
1799 | + err := dec.Decode(mSendConf) |
1800 | + c.Check(err, NotNil) |
1801 | + c.Check(mSendConf.ResponseStatus, Equals, byte(0x0)) |
1802 | + c.Check(mSendConf.TransactionId, Equals, "") |
1803 | + mSendConf.Status() |
1804 | +} |
1805 | |
1806 | === added file 'mms/decoder_test.go' |
1807 | --- mms/decoder_test.go 1970-01-01 00:00:00 +0000 |
1808 | +++ mms/decoder_test.go 2015-09-02 10:42:28 +0000 |
1809 | @@ -0,0 +1,59 @@ |
1810 | +/* |
1811 | + * Copyright 2014 Canonical Ltd. |
1812 | + * |
1813 | + * Authors: |
1814 | + * Sergio Schvezov: sergio.schvezov@cannical.com |
1815 | + * |
1816 | + * This file is part of mms. |
1817 | + * |
1818 | + * mms is free software; you can redistribute it and/or modify |
1819 | + * it under the terms of the GNU General Public License as published by |
1820 | + * the Free Software Foundation; version 3. |
1821 | + * |
1822 | + * mms is distributed in the hope that it will be useful, |
1823 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1824 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1825 | + * GNU General Public License for more details. |
1826 | + * |
1827 | + * You should have received a copy of the GNU General Public License |
1828 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1829 | + */ |
1830 | + |
1831 | +package mms |
1832 | + |
1833 | +import ( |
1834 | + "errors" |
1835 | + |
1836 | + . "launchpad.net/gocheck" |
1837 | +) |
1838 | + |
1839 | +type DecoderTestSuite struct{} |
1840 | + |
1841 | +var _ = Suite(&DecoderTestSuite{}) |
1842 | + |
1843 | +func (s *DecoderTestSuite) TestDecodeStringNoNullByteTerminator(c *C) { |
1844 | + inputBytes := []byte{ |
1845 | + //stub byte |
1846 | + 0x80, |
1847 | + //<html> |
1848 | + 0x3c, 0x68, 0x74, 0x6d, 0x6c, 0x3e, |
1849 | + } |
1850 | + expectedErr := errors.New("reached end of data while trying to read string: <html>") |
1851 | + dec := NewDecoder(inputBytes) |
1852 | + str, err := dec.ReadString(nil, "") |
1853 | + c.Check(str, Equals, "") |
1854 | + c.Check(err, DeepEquals, expectedErr) |
1855 | +} |
1856 | + |
1857 | +func (s *DecoderTestSuite) TestDecodeStringWithNullByteTerminator(c *C) { |
1858 | + inputBytes := []byte{ |
1859 | + //stub byte |
1860 | + 0x80, |
1861 | + //<smil> |
1862 | + 0x3c, 0x73, 0x6d, 0x69, 0x6c, 0x3e, 0x00, |
1863 | + } |
1864 | + dec := NewDecoder(inputBytes) |
1865 | + str, err := dec.ReadString(nil, "") |
1866 | + c.Check(str, Equals, "<smil>") |
1867 | + c.Check(err, IsNil) |
1868 | +} |
1869 | |
1870 | === added file 'mms/download.go' |
1871 | --- mms/download.go 1970-01-01 00:00:00 +0000 |
1872 | +++ mms/download.go 2015-09-02 10:42:28 +0000 |
1873 | @@ -0,0 +1,92 @@ |
1874 | +/* |
1875 | + * Copyright 2014 Canonical Ltd. |
1876 | + * |
1877 | + * Authors: |
1878 | + * Sergio Schvezov: sergio.schvezov@cannical.com |
1879 | + * |
1880 | + * This file is part of mms. |
1881 | + * |
1882 | + * mms is free software; you can redistribute it and/or modify |
1883 | + * it under the terms of the GNU General Public License as published by |
1884 | + * the Free Software Foundation; version 3. |
1885 | + * |
1886 | + * mms is distributed in the hope that it will be useful, |
1887 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1888 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1889 | + * GNU General Public License for more details. |
1890 | + * |
1891 | + * You should have received a copy of the GNU General Public License |
1892 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1893 | + */ |
1894 | + |
1895 | +package mms |
1896 | + |
1897 | +import ( |
1898 | + "errors" |
1899 | + "fmt" |
1900 | + "log" |
1901 | + "time" |
1902 | + |
1903 | + "launchpad.net/udm" |
1904 | +) |
1905 | + |
1906 | +func (pdu *MNotificationInd) DownloadContent(proxyHost string, proxyPort int32) (string, error) { |
1907 | + downloadManager, err := udm.NewDownloadManager() |
1908 | + if err != nil { |
1909 | + return "", err |
1910 | + } |
1911 | + download, err := downloadManager.CreateMmsDownload(pdu.ContentLocation, proxyHost, proxyPort) |
1912 | + if err != nil { |
1913 | + return "", err |
1914 | + } |
1915 | + f := download.Finished() |
1916 | + p := download.DownloadProgress() |
1917 | + e := download.Error() |
1918 | + log.Print("Starting download of ", pdu.ContentLocation, " with proxy ", proxyHost, ":", proxyPort) |
1919 | + download.Start() |
1920 | + for { |
1921 | + select { |
1922 | + case progress := <-p: |
1923 | + log.Print("Progress:", progress.Total, progress.Received) |
1924 | + case downloadFilePath := <-f: |
1925 | + log.Print("File downloaded to ", downloadFilePath) |
1926 | + return downloadFilePath, nil |
1927 | + case <-time.After(3 * time.Minute): |
1928 | + return "", fmt.Errorf("Download timeout exceeded while fetching %s", pdu.ContentLocation) |
1929 | + case err := <-e: |
1930 | + return "", err |
1931 | + } |
1932 | + } |
1933 | +} |
1934 | + |
1935 | +func Upload(file, msc, proxyHost string, proxyPort int32) (string, error) { |
1936 | + udm, err := udm.NewUploadManager() |
1937 | + if err != nil { |
1938 | + return "", err |
1939 | + } |
1940 | + upload, err := udm.CreateMmsUpload(msc, file, proxyHost, proxyPort) |
1941 | + if err != nil { |
1942 | + return "", err |
1943 | + } |
1944 | + f := upload.Finished() |
1945 | + p := upload.UploadProgress() |
1946 | + e := upload.Error() |
1947 | + log.Print("Starting upload of ", file, " to ", msc, " with proxy ", proxyHost, ":", proxyPort) |
1948 | + if err := upload.Start(); err != nil { |
1949 | + return "", err |
1950 | + } |
1951 | + |
1952 | + for { |
1953 | + select { |
1954 | + case progress := <-p: |
1955 | + log.Print("Progress:", progress.Total, progress.Received) |
1956 | + case responseFile := <-f: |
1957 | + log.Print("File ", responseFile, " returned in upload") |
1958 | + return responseFile, nil |
1959 | + case <-time.After(10 * time.Minute): |
1960 | + return "", errors.New("upload timeout") |
1961 | + case err := <-e: |
1962 | + return "", err |
1963 | + } |
1964 | + } |
1965 | +} |
1966 | |
1967 | === added file 'mms/encode_decode_test.go' |
1968 | --- mms/encode_decode_test.go 1970-01-01 00:00:00 +0000 |
1969 | +++ mms/encode_decode_test.go 2015-09-02 10:42:28 +0000 |
1970 | @@ -0,0 +1,110 @@ |
1971 | +/* |
1972 | + * Copyright 2014 Canonical Ltd. |
1973 | + * |
1974 | + * Authors: |
1975 | + * Sergio Schvezov: sergio.schvezov@cannical.com |
1976 | + * |
1977 | + * This file is part of mms. |
1978 | + * |
1979 | + * mms is free software; you can redistribute it and/or modify |
1980 | + * it under the terms of the GNU General Public License as published by |
1981 | + * the Free Software Foundation; version 3. |
1982 | + * |
1983 | + * mms is distributed in the hope that it will be useful, |
1984 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1985 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1986 | + * GNU General Public License for more details. |
1987 | + * |
1988 | + * You should have received a copy of the GNU General Public License |
1989 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1990 | + */ |
1991 | + |
1992 | +package mms |
1993 | + |
1994 | +import ( |
1995 | + "bytes" |
1996 | + |
1997 | + . "launchpad.net/gocheck" |
1998 | +) |
1999 | + |
2000 | +type EncodeDecodeTestSuite struct { |
2001 | + bytes *bytes.Buffer |
2002 | + enc *MMSEncoder |
2003 | + dec *MMSDecoder |
2004 | +} |
2005 | + |
2006 | +var _ = Suite(&EncodeDecodeTestSuite{}) |
2007 | + |
2008 | +func (s *EncodeDecodeTestSuite) SetUpTest(c *C) { |
2009 | + s.bytes = new(bytes.Buffer) |
2010 | + s.enc = NewEncoder(s.bytes) |
2011 | + c.Assert(s.enc.writeByte(0), IsNil) |
2012 | +} |
2013 | + |
2014 | +func (s *EncodeDecodeTestSuite) TestString(c *C) { |
2015 | + testStr := "'Hello World!" |
2016 | + c.Assert(s.enc.writeString(testStr), IsNil) |
2017 | + s.dec = NewDecoder(s.bytes.Bytes()) |
2018 | + |
2019 | + str, err := s.dec.ReadString(nil, "") |
2020 | + c.Assert(err, IsNil) |
2021 | + c.Assert(str, Equals, testStr) |
2022 | +} |
2023 | + |
2024 | +func (s *EncodeDecodeTestSuite) TestByte(c *C) { |
2025 | + testBytes := []byte{0, 0x79, 0x80, 0x81} |
2026 | + for i := range testBytes { |
2027 | + c.Assert(s.enc.writeByte(testBytes[i]), IsNil) |
2028 | + } |
2029 | + bytes := s.bytes.Bytes() |
2030 | + s.dec = NewDecoder(bytes) |
2031 | + for i := range testBytes { |
2032 | + b, err := s.dec.ReadByte(nil, "") |
2033 | + c.Assert(err, IsNil) |
2034 | + c.Assert(b, Equals, testBytes[i], Commentf("From testBytes[%d] and encoded bytes: %#x", i, bytes)) |
2035 | + } |
2036 | +} |
2037 | + |
2038 | +func (s *EncodeDecodeTestSuite) TestInteger(c *C) { |
2039 | + // 128 bounds short and long integers |
2040 | + testInts := []uint64{512, 100, 127, 128, 129, 255, 256, 511, 3000} |
2041 | + for i := range testInts { |
2042 | + c.Assert(s.enc.writeInteger(testInts[i]), IsNil) |
2043 | + } |
2044 | + bytes := s.bytes.Bytes() |
2045 | + s.dec = NewDecoder(bytes) |
2046 | + for i := range testInts { |
2047 | + integer, err := s.dec.ReadInteger(nil, "") |
2048 | + c.Assert(err, IsNil) |
2049 | + c.Check(integer, Equals, testInts[i], Commentf("%d != %d with encoded bytes starting at %d: %d", integer, testInts[i], i, bytes)) |
2050 | + } |
2051 | +} |
2052 | + |
2053 | +func (s *EncodeDecodeTestSuite) TestUintVar(c *C) { |
2054 | + testInts := []uint64{127, 512, 255, 256, 3000} |
2055 | + for i := range testInts { |
2056 | + c.Assert(s.enc.writeUintVar(testInts[i]), IsNil) |
2057 | + } |
2058 | + bytes := s.bytes.Bytes() |
2059 | + s.dec = NewDecoder(bytes) |
2060 | + for i := range testInts { |
2061 | + integer, err := s.dec.ReadUintVar(nil, "") |
2062 | + c.Assert(err, IsNil) |
2063 | + c.Check(integer, Equals, testInts[i], Commentf("%d != %d with encoded bytes starting at %d: %d", integer, testInts[i], i, bytes)) |
2064 | + } |
2065 | +} |
2066 | + |
2067 | +func (s *EncodeDecodeTestSuite) TestLength(c *C) { |
2068 | + // > 30 requires encoding with length quote |
2069 | + testLengths := []uint64{10, 1, 29, 30, 31, 500} |
2070 | + for i := range testLengths { |
2071 | + c.Assert(s.enc.writeLength(testLengths[i]), IsNil) |
2072 | + } |
2073 | + bytes := s.bytes.Bytes() |
2074 | + s.dec = NewDecoder(bytes) |
2075 | + for i := range testLengths { |
2076 | + integer, err := s.dec.ReadLength(nil) |
2077 | + c.Assert(err, IsNil, Commentf("%d != %d with encoded bytes starting at %d: %d", integer, testLengths[i], s.dec.Offset, bytes)) |
2078 | + c.Check(integer, Equals, testLengths[i], Commentf("%d != %d with encoded bytes starting at %d: %d", integer, testLengths[i], s.dec.Offset, bytes)) |
2079 | + } |
2080 | +} |
2081 | |
2082 | === added file 'mms/encoder.go' |
2083 | --- mms/encoder.go 1970-01-01 00:00:00 +0000 |
2084 | +++ mms/encoder.go 2015-09-02 10:42:28 +0000 |
2085 | @@ -0,0 +1,448 @@ |
2086 | +/* |
2087 | + * Copyright 2014 Canonical Ltd. |
2088 | + * |
2089 | + * Authors: |
2090 | + * Sergio Schvezov: sergio.schvezov@cannical.com |
2091 | + * |
2092 | + * This file is part of mms. |
2093 | + * |
2094 | + * mms is free software; you can redistribute it and/or modify |
2095 | + * it under the terms of the GNU General Public License as published by |
2096 | + * the Free Software Foundation; version 3. |
2097 | + * |
2098 | + * mms is distributed in the hope that it will be useful, |
2099 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2100 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2101 | + * GNU General Public License for more details. |
2102 | + * |
2103 | + * You should have received a copy of the GNU General Public License |
2104 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2105 | + */ |
2106 | + |
2107 | +package mms |
2108 | + |
2109 | +import ( |
2110 | + "bytes" |
2111 | + "errors" |
2112 | + "fmt" |
2113 | + "io" |
2114 | + "log" |
2115 | + "reflect" |
2116 | +) |
2117 | + |
2118 | +type MMSEncoder struct { |
2119 | + w io.Writer |
2120 | + log string |
2121 | +} |
2122 | + |
2123 | +func NewEncoder(w io.Writer) *MMSEncoder { |
2124 | + return &MMSEncoder{w: w} |
2125 | +} |
2126 | + |
2127 | +func (enc *MMSEncoder) Encode(pdu MMSWriter) error { |
2128 | + rPdu := reflect.ValueOf(pdu).Elem() |
2129 | + |
2130 | + //The order of the following fields doens't matter much |
2131 | + typeOfPdu := rPdu.Type() |
2132 | + var err error |
2133 | + for i := 0; i < rPdu.NumField(); i++ { |
2134 | + fieldName := typeOfPdu.Field(i).Name |
2135 | + encodeTag := typeOfPdu.Field(i).Tag.Get("encode") |
2136 | + f := rPdu.Field(i) |
2137 | + |
2138 | + if encodeTag == "no" { |
2139 | + continue |
2140 | + } |
2141 | + switch f.Kind() { |
2142 | + case reflect.Uint: |
2143 | + case reflect.Uint8: |
2144 | + enc.log = enc.log + fmt.Sprintf("%s: %d %#x\n", fieldName, f.Uint(), f.Uint()) |
2145 | + case reflect.Bool: |
2146 | + enc.log = enc.log + fmt.Sprintf(fieldName, f.Bool()) |
2147 | + default: |
2148 | + enc.log = enc.log + fmt.Sprintf(fieldName, f) |
2149 | + } |
2150 | + |
2151 | + switch fieldName { |
2152 | + case "Type": |
2153 | + err = enc.writeByteParam(X_MMS_MESSAGE_TYPE, byte(f.Uint())) |
2154 | + case "Version": |
2155 | + err = enc.writeByteParam(X_MMS_MMS_VERSION, byte(f.Uint())) |
2156 | + case "TransactionId": |
2157 | + err = enc.writeStringParam(X_MMS_TRANSACTION_ID, f.String()) |
2158 | + case "Status": |
2159 | + err = enc.writeByteParam(X_MMS_STATUS, byte(f.Uint())) |
2160 | + case "From": |
2161 | + err = enc.writeFrom() |
2162 | + case "Name": |
2163 | + err = enc.writeStringParam(WSP_PARAMETER_TYPE_NAME_DEFUNCT, f.String()) |
2164 | + case "Start": |
2165 | + err = enc.writeStringParam(WSP_PARAMETER_TYPE_START_DEFUNCT, f.String()) |
2166 | + case "To": |
2167 | + err = enc.writeStringParam(TO, f.String()) |
2168 | + case "ContentType": |
2169 | + // if there is a ContentType there has to be content |
2170 | + if mSendReq, ok := pdu.(*MSendReq); ok { |
2171 | + if err := enc.setParam(CONTENT_TYPE); err != nil { |
2172 | + return err |
2173 | + } |
2174 | + if err = enc.writeContentType(mSendReq.ContentType, mSendReq.ContentTypeStart, mSendReq.ContentTypeType, ""); err != nil { |
2175 | + return err |
2176 | + } |
2177 | + err = enc.writeAttachments(mSendReq.Attachments) |
2178 | + } else { |
2179 | + err = errors.New("unhandled content type") |
2180 | + } |
2181 | + case "MediaType": |
2182 | + if a, ok := pdu.(*Attachment); ok { |
2183 | + if err = enc.writeContentType(a.MediaType, "", "", a.Name); err != nil { |
2184 | + return err |
2185 | + } |
2186 | + } else { |
2187 | + if err = enc.writeMediaType(f.String()); err != nil { |
2188 | + return err |
2189 | + } |
2190 | + } |
2191 | + case "Charset": |
2192 | + //TODO |
2193 | + err = enc.writeCharset(f.String()) |
2194 | + case "ContentLocation": |
2195 | + err = enc.writeStringParam(MMS_PART_CONTENT_LOCATION, f.String()) |
2196 | + case "ContentId": |
2197 | + err = enc.writeQuotedStringParam(MMS_PART_CONTENT_ID, f.String()) |
2198 | + case "Date": |
2199 | + date := f.Uint() |
2200 | + if date > 0 { |
2201 | + err = enc.writeLongIntegerParam(DATE, date) |
2202 | + } |
2203 | + case "Class": |
2204 | + err = enc.writeByteParam(X_MMS_MESSAGE_CLASS, byte(f.Uint())) |
2205 | + case "ReportAllowed": |
2206 | + err = enc.writeByteParam(X_MMS_REPORT_ALLOWED, byte(f.Uint())) |
2207 | + case "DeliveryReport": |
2208 | + err = enc.writeByteParam(X_MMS_DELIVERY_REPORT, byte(f.Uint())) |
2209 | + case "ReadReport": |
2210 | + err = enc.writeByteParam(X_MMS_READ_REPORT, byte(f.Uint())) |
2211 | + case "Expiry": |
2212 | + expiry := f.Uint() |
2213 | + if expiry > 0 { |
2214 | + err = enc.writeRelativeExpiry(expiry) |
2215 | + } |
2216 | + default: |
2217 | + if encodeTag == "optional" { |
2218 | + log.Printf("Unhandled optional field %s", fieldName) |
2219 | + } else { |
2220 | + panic(fmt.Sprintf("missing encoding for mandatory field %s", fieldName)) |
2221 | + } |
2222 | + } |
2223 | + if err != nil { |
2224 | + return fmt.Errorf("cannot encode field %s with value %s: %s ... encoded so far: %s", fieldName, f, err, enc.log) |
2225 | + } |
2226 | + } |
2227 | + return nil |
2228 | +} |
2229 | + |
2230 | +func (enc *MMSEncoder) setParam(param byte) error { |
2231 | + return enc.writeByte(param | 0x80) |
2232 | +} |
2233 | + |
2234 | +func encodeAttachment(attachment *Attachment) ([]byte, error) { |
2235 | + var outBytes bytes.Buffer |
2236 | + enc := NewEncoder(&outBytes) |
2237 | + if err := enc.Encode(attachment); err != nil { |
2238 | + return []byte{}, err |
2239 | + } |
2240 | + return outBytes.Bytes(), nil |
2241 | +} |
2242 | + |
2243 | +func (enc *MMSEncoder) writeAttachments(attachments []*Attachment) error { |
2244 | + // Write the number of parts |
2245 | + if err := enc.writeUintVar(uint64(len(attachments))); err != nil { |
2246 | + return err |
2247 | + } |
2248 | + |
2249 | + for i := range attachments { |
2250 | + var attachmentHeader []byte |
2251 | + if b, err := encodeAttachment(attachments[i]); err != nil { |
2252 | + return err |
2253 | + } else { |
2254 | + attachmentHeader = b |
2255 | + } |
2256 | + |
2257 | + // headers length |
2258 | + headerLength := uint64(len(attachmentHeader)) |
2259 | + if err := enc.writeUintVar(headerLength); err != nil { |
2260 | + return err |
2261 | + } |
2262 | + // data length |
2263 | + dataLength := uint64(len(attachments[i].Data)) |
2264 | + if err := enc.writeUintVar(dataLength); err != nil { |
2265 | + return err |
2266 | + } |
2267 | + if err := enc.writeBytes(attachmentHeader, int(headerLength)); err != nil { |
2268 | + return err |
2269 | + } |
2270 | + if err := enc.writeBytes(attachments[i].Data, int(dataLength)); err != nil { |
2271 | + return err |
2272 | + } |
2273 | + } |
2274 | + return nil |
2275 | +} |
2276 | + |
2277 | +func (enc *MMSEncoder) writeCharset(charset string) error { |
2278 | + if charset == "" { |
2279 | + return nil |
2280 | + } |
2281 | + charsetCode := uint64(ANY_CHARSET) |
2282 | + for k, v := range CHARSETS { |
2283 | + if v == charset { |
2284 | + charsetCode = k |
2285 | + } |
2286 | + } |
2287 | + return enc.writeIntegerParam(WSP_PARAMETER_TYPE_CHARSET, charsetCode) |
2288 | +} |
2289 | + |
2290 | +func (enc *MMSEncoder) writeLength(length uint64) error { |
2291 | + if length <= SHORT_LENGTH_MAX { |
2292 | + return enc.writeByte(byte(length)) |
2293 | + } else { |
2294 | + if err := enc.writeByte(LENGTH_QUOTE); err != nil { |
2295 | + return err |
2296 | + } |
2297 | + return enc.writeUintVar(length) |
2298 | + } |
2299 | +} |
2300 | + |
2301 | +func encodeContentType(media string) (uint64, error) { |
2302 | + var mt int |
2303 | + for mt = range CONTENT_TYPES { |
2304 | + if CONTENT_TYPES[mt] == media { |
2305 | + return uint64(mt), nil |
2306 | + } |
2307 | + } |
2308 | + return 0, errors.New("cannot binary encode media") |
2309 | +} |
2310 | + |
2311 | +func (enc *MMSEncoder) writeContentType(media, start, ctype, name string) error { |
2312 | + if start == "" && ctype == "" && name == "" { |
2313 | + return enc.writeMediaType(media) |
2314 | + } |
2315 | + |
2316 | + var contentType []byte |
2317 | + if start != "" { |
2318 | + contentType = append(contentType, WSP_PARAMETER_TYPE_START_DEFUNCT|SHORT_FILTER) |
2319 | + contentType = append(contentType, []byte(start)...) |
2320 | + contentType = append(contentType, 0) |
2321 | + } |
2322 | + if ctype != "" { |
2323 | + contentType = append(contentType, WSP_PARAMETER_TYPE_CONTENT_TYPE|SHORT_FILTER) |
2324 | + contentType = append(contentType, []byte(ctype)...) |
2325 | + contentType = append(contentType, 0) |
2326 | + } |
2327 | + if name != "" { |
2328 | + contentType = append(contentType, WSP_PARAMETER_TYPE_NAME_DEFUNCT|SHORT_FILTER) |
2329 | + contentType = append(contentType, []byte(name)...) |
2330 | + contentType = append(contentType, 0) |
2331 | + } |
2332 | + |
2333 | + if mt, err := encodeContentType(media); err == nil { |
2334 | + // +1 for mt |
2335 | + length := uint64(len(contentType) + 1) |
2336 | + if err := enc.writeLength(length); err != nil { |
2337 | + return err |
2338 | + } |
2339 | + if err := enc.writeInteger(mt); err != nil { |
2340 | + return err |
2341 | + } |
2342 | + } else { |
2343 | + mediaB := []byte(media) |
2344 | + mediaB = append(mediaB, 0) |
2345 | + contentType = append(mediaB, contentType...) |
2346 | + length := uint64(len(contentType)) |
2347 | + if err := enc.writeLength(length); err != nil { |
2348 | + return err |
2349 | + } |
2350 | + } |
2351 | + return enc.writeBytes(contentType, len(contentType)) |
2352 | +} |
2353 | + |
2354 | +func (enc *MMSEncoder) writeMediaType(media string) error { |
2355 | + if mt, err := encodeContentType(media); err == nil { |
2356 | + return enc.writeInteger(mt) |
2357 | + } |
2358 | + |
2359 | + // +1 is the byte{0} |
2360 | + if err := enc.writeByte(byte(len(media) + 1)); err != nil { |
2361 | + return err |
2362 | + } |
2363 | + return enc.writeString(media) |
2364 | +} |
2365 | + |
2366 | +func (enc *MMSEncoder) writeRelativeExpiry(expiry uint64) error { |
2367 | + if err := enc.setParam(X_MMS_EXPIRY); err != nil { |
2368 | + return err |
2369 | + } |
2370 | + encodedLong := encodeLong(expiry) |
2371 | + |
2372 | + var b []byte |
2373 | + // +1 for the token, +1 for the len of long |
2374 | + b = append(b, byte(len(encodedLong)+2)) |
2375 | + b = append(b, ExpiryTokenRelative) |
2376 | + b = append(b, byte(len(encodedLong))) |
2377 | + b = append(b, encodedLong...) |
2378 | + |
2379 | + return enc.writeBytes(b, len(b)) |
2380 | +} |
2381 | + |
2382 | +func (enc *MMSEncoder) writeLongIntegerParam(param byte, i uint64) error { |
2383 | + if err := enc.setParam(param); err != nil { |
2384 | + return err |
2385 | + } |
2386 | + return enc.writeLongInteger(i) |
2387 | +} |
2388 | + |
2389 | +func (enc *MMSEncoder) writeIntegerParam(param byte, i uint64) error { |
2390 | + if err := enc.setParam(param); err != nil { |
2391 | + return err |
2392 | + } |
2393 | + return enc.writeInteger(i) |
2394 | +} |
2395 | + |
2396 | +func (enc *MMSEncoder) writeQuotedStringParam(param byte, s string) error { |
2397 | + if s == "" { |
2398 | + enc.log = enc.log + "Skipping empty string\n" |
2399 | + } |
2400 | + if err := enc.setParam(param); err != nil { |
2401 | + return err |
2402 | + } |
2403 | + if err := enc.writeByte(STRING_QUOTE); err != nil { |
2404 | + return err |
2405 | + } |
2406 | + return enc.writeString(s) |
2407 | +} |
2408 | + |
2409 | +func (enc *MMSEncoder) writeStringParam(param byte, s string) error { |
2410 | + if s == "" { |
2411 | + enc.log = enc.log + "Skipping empty string\n" |
2412 | + return nil |
2413 | + } |
2414 | + if err := enc.setParam(param); err != nil { |
2415 | + return err |
2416 | + } |
2417 | + return enc.writeString(s) |
2418 | +} |
2419 | + |
2420 | +func (enc *MMSEncoder) writeByteParam(param byte, b byte) error { |
2421 | + if err := enc.setParam(param); err != nil { |
2422 | + return err |
2423 | + } |
2424 | + return enc.writeByte(b) |
2425 | +} |
2426 | + |
2427 | +func (enc *MMSEncoder) writeFrom() error { |
2428 | + if err := enc.setParam(FROM); err != nil { |
2429 | + return err |
2430 | + } |
2431 | + if err := enc.writeByte(1); err != nil { |
2432 | + return err |
2433 | + } |
2434 | + return enc.writeByte(TOKEN_INSERT_ADDRESS) |
2435 | +} |
2436 | + |
2437 | +func (enc *MMSEncoder) writeString(s string) error { |
2438 | + bytes := []byte(s) |
2439 | + bytes = append(bytes, 0) |
2440 | + _, err := enc.w.Write(bytes) |
2441 | + return err |
2442 | +} |
2443 | + |
2444 | +func (enc *MMSEncoder) writeBytes(b []byte, count int) error { |
2445 | + if n, err := enc.w.Write(b); n != count { |
2446 | + return fmt.Errorf("expected to write %d byte[s] but wrote %d", count, n) |
2447 | + } else if err != nil { |
2448 | + return err |
2449 | + } |
2450 | + return nil |
2451 | +} |
2452 | + |
2453 | +func (enc *MMSEncoder) writeByte(b byte) error { |
2454 | + return enc.writeBytes([]byte{b}, 1) |
2455 | +} |
2456 | + |
2457 | +// writeShort encodes i according to the Basic Rules described in section |
2458 | +// 8.4.2.2 of WAP-230-WSP-20010705-a. |
2459 | +// |
2460 | +// Integers in range 0-127 (< 0x80) shall be encoded as a one octet value |
2461 | +// with the most significant bit set to one (1xxx xxxx == |0x80) and with |
2462 | +// the value in the remaining least significant bits. |
2463 | +func (enc *MMSEncoder) writeShortInteger(i uint64) error { |
2464 | + return enc.writeByte(byte(i | 0x80)) |
2465 | +} |
2466 | + |
2467 | +// writeLongInteger encodes i according to the Basic Rules described in section |
2468 | +// 8.4.2.2 of WAP-230-WSP-20010705-a. |
2469 | +// |
2470 | +// Long-integer = Short-length Multi-octet-integer |
2471 | +// The Short-length indicates the length of the Multi-octet-integer |
2472 | +// |
2473 | +// Multi-octet-integer = 1*30 OCTET |
2474 | +// The content octets shall be an unsigned integer value |
2475 | +// with the most significant octet encoded first (big-endian representation). |
2476 | +// The minimum number of octets must be used to encode the value. |
2477 | +func (enc *MMSEncoder) writeLongInteger(i uint64) error { |
2478 | + encodedLong := encodeLong(i) |
2479 | + encLength := uint64(len(encodedLong)) |
2480 | + if encLength > SHORT_LENGTH_MAX { |
2481 | + return fmt.Errorf("cannot encode long integer, lenght was %d but expected %d", encLength, SHORT_LENGTH_MAX) |
2482 | + } |
2483 | + if err := enc.writeByte(byte(encLength)); err != nil { |
2484 | + return err |
2485 | + } |
2486 | + |
2487 | + return enc.writeBytes(encodedLong, len(encodedLong)) |
2488 | +} |
2489 | + |
2490 | +func encodeLong(i uint64) (encodedLong []byte) { |
2491 | + for i > 0 { |
2492 | + b := byte(0xff & i) |
2493 | + encodedLong = append([]byte{b}, encodedLong...) |
2494 | + i = i >> 8 |
2495 | + } |
2496 | + return encodedLong |
2497 | +} |
2498 | + |
2499 | +// writeInteger encodes i according to the Basic Rules described in section |
2500 | +// 8.4.2.2 of WAP-230-WSP-20010705-a. |
2501 | +// |
2502 | +// It encodes as a Short-integer when i < 128 (=0x80) or as a Long-Integer |
2503 | +// otherwise |
2504 | +func (enc *MMSEncoder) writeInteger(i uint64) error { |
2505 | + if i < 0x80 { |
2506 | + return enc.writeShortInteger(i) |
2507 | + } else { |
2508 | + return enc.writeLongInteger(i) |
2509 | + } |
2510 | + return nil |
2511 | +} |
2512 | + |
2513 | +// writeUintVar encodes v according to section 8.1.2 and the Basic Rules |
2514 | +// described in section 8.4.2.2 of WAP-230-WSP-20010705-a. |
2515 | +// |
2516 | +// To encode a large unsigned integer, split it into 7-bit (0x7f) fragments |
2517 | +// and place them in the payloads of multiple octets. The most significant |
2518 | +// bits are placed in the first octets with the least significant bits ending |
2519 | +// up in the last octet. All octets MUST set the Continue bit to 1 (|0x80) |
2520 | +// except the last octet, which MUST set the Continue bit to 0. |
2521 | +// |
2522 | +// The unsigned integer MUST be encoded in the smallest encoding possible. |
2523 | +// In other words, the encoded value MUST NOT start with an octet with the |
2524 | +// value 0x80. |
2525 | +func (enc *MMSEncoder) writeUintVar(v uint64) error { |
2526 | + uintVar := []byte{byte(v & 0x7f)} |
2527 | + v = v >> 7 |
2528 | + for v > 0 { |
2529 | + uintVar = append([]byte{byte(0x80 | (v & 0x7f))}, uintVar...) |
2530 | + v = v >> 7 |
2531 | + } |
2532 | + return enc.writeBytes(uintVar, len(uintVar)) |
2533 | +} |
2534 | |
2535 | === added file 'mms/encoder_test.go' |
2536 | --- mms/encoder_test.go 1970-01-01 00:00:00 +0000 |
2537 | +++ mms/encoder_test.go 2015-09-02 10:42:28 +0000 |
2538 | @@ -0,0 +1,141 @@ |
2539 | +/* |
2540 | + * Copyright 2014 Canonical Ltd. |
2541 | + * |
2542 | + * Authors: |
2543 | + * Sergio Schvezov: sergio.schvezov@cannical.com |
2544 | + * |
2545 | + * This file is part of mms. |
2546 | + * |
2547 | + * mms is free software; you can redistribute it and/or modify |
2548 | + * it under the terms of the GNU General Public License as published by |
2549 | + * the Free Software Foundation; version 3. |
2550 | + * |
2551 | + * mms is distributed in the hope that it will be useful, |
2552 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2553 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2554 | + * GNU General Public License for more details. |
2555 | + * |
2556 | + * You should have received a copy of the GNU General Public License |
2557 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2558 | + */ |
2559 | + |
2560 | +package mms |
2561 | + |
2562 | +import ( |
2563 | + "bytes" |
2564 | + "io/ioutil" |
2565 | + "os" |
2566 | + "testing" |
2567 | + |
2568 | + . "launchpad.net/gocheck" |
2569 | +) |
2570 | + |
2571 | +type EncoderTestSuite struct{} |
2572 | + |
2573 | +// Hook up gocheck into the "go test" runner. |
2574 | +func Test(t *testing.T) { TestingT(t) } |
2575 | + |
2576 | +var _ = Suite(&EncoderTestSuite{}) |
2577 | + |
2578 | +func (s *EncoderTestSuite) TestEncodeMNotifyRespIndRetrievedWithReports(c *C) { |
2579 | + expectedBytes := []byte{ |
2580 | + //Message Type m-notifyresp.ind |
2581 | + 0x8C, 0x83, |
2582 | + // Transaction Id |
2583 | + 0x98, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x00, |
2584 | + // MMS Version 1.3 |
2585 | + 0x8D, 0x93, |
2586 | + // Status retrieved |
2587 | + 0x95, 0x81, |
2588 | + // Report Allowed No |
2589 | + 0x91, 0x81, |
2590 | + } |
2591 | + mNotifyRespInd := &MNotifyRespInd{ |
2592 | + UUID: "1", |
2593 | + Type: TYPE_NOTIFYRESP_IND, |
2594 | + TransactionId: "0123456", |
2595 | + Version: MMS_MESSAGE_VERSION_1_3, |
2596 | + Status: STATUS_RETRIEVED, |
2597 | + ReportAllowed: ReportAllowedNo, |
2598 | + } |
2599 | + var outBytes bytes.Buffer |
2600 | + enc := NewEncoder(&outBytes) |
2601 | + c.Assert(enc.Encode(mNotifyRespInd), IsNil) |
2602 | + c.Assert(outBytes.Bytes(), DeepEquals, expectedBytes) |
2603 | +} |
2604 | + |
2605 | +func (s *EncoderTestSuite) TestEncodeMNotifyRespIndDeffered(c *C) { |
2606 | + expectedBytes := []byte{ |
2607 | + //Message Type m-notifyresp.ind |
2608 | + 0x8C, 0x83, |
2609 | + // Transaction Id |
2610 | + 0x98, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x00, |
2611 | + // MMS Version 1.3 |
2612 | + 0x8D, 0x93, |
2613 | + // Status deffered |
2614 | + 0x95, 0x83, |
2615 | + // Report Allowed No |
2616 | + 0x91, 0x81, |
2617 | + } |
2618 | + mNotifyRespInd := &MNotifyRespInd{ |
2619 | + UUID: "1", |
2620 | + Type: TYPE_NOTIFYRESP_IND, |
2621 | + TransactionId: "0123456", |
2622 | + Version: MMS_MESSAGE_VERSION_1_3, |
2623 | + Status: STATUS_DEFERRED, |
2624 | + ReportAllowed: ReportAllowedNo, |
2625 | + } |
2626 | + var outBytes bytes.Buffer |
2627 | + enc := NewEncoder(&outBytes) |
2628 | + c.Assert(enc.Encode(mNotifyRespInd), IsNil) |
2629 | + c.Assert(outBytes.Bytes(), DeepEquals, expectedBytes) |
2630 | +} |
2631 | + |
2632 | +func (s *EncoderTestSuite) TestEncodeMNotifyRespIndRetrievedWithoutReports(c *C) { |
2633 | + expectedBytes := []byte{ |
2634 | + //Message Type m-notifyresp.ind |
2635 | + 0x8C, 0x83, |
2636 | + // Transaction Id |
2637 | + 0x98, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x00, |
2638 | + // MMS Version 1.3 |
2639 | + 0x8D, 0x93, |
2640 | + // Status retrieved |
2641 | + 0x95, 0x81, |
2642 | + // Report Allowed Yes |
2643 | + 0x91, 0x80, |
2644 | + } |
2645 | + mNotifyRespInd := &MNotifyRespInd{ |
2646 | + UUID: "1", |
2647 | + Type: TYPE_NOTIFYRESP_IND, |
2648 | + TransactionId: "0123456", |
2649 | + Version: MMS_MESSAGE_VERSION_1_3, |
2650 | + Status: STATUS_RETRIEVED, |
2651 | + ReportAllowed: ReportAllowedYes, |
2652 | + } |
2653 | + var outBytes bytes.Buffer |
2654 | + enc := NewEncoder(&outBytes) |
2655 | + c.Assert(enc.Encode(mNotifyRespInd), IsNil) |
2656 | + c.Assert(outBytes.Bytes(), DeepEquals, expectedBytes) |
2657 | +} |
2658 | + |
2659 | +func (s *EncoderTestSuite) TestEncodeMSendReq(c *C) { |
2660 | + tmp, err := ioutil.TempFile("", "") |
2661 | + c.Assert(err, IsNil) |
2662 | + tmp.Close() |
2663 | + defer os.Remove(tmp.Name()) |
2664 | + err = ioutil.WriteFile(tmp.Name(), []byte{1, 2, 3, 4, 5, 6}, 0644) |
2665 | + c.Assert(err, IsNil) |
2666 | + |
2667 | + att, err := NewAttachment("text0", "text0.txt", tmp.Name()) |
2668 | + c.Assert(err, IsNil) |
2669 | + |
2670 | + attachments := []*Attachment{att} |
2671 | + |
2672 | + recipients := []string{"+12345"} |
2673 | + mSendReq := NewMSendReq(recipients, attachments, false) |
2674 | + |
2675 | + var outBytes bytes.Buffer |
2676 | + enc := NewEncoder(&outBytes) |
2677 | + err = enc.Encode(mSendReq) |
2678 | + c.Assert(err, IsNil) |
2679 | +} |
2680 | |
2681 | === added file 'mms/mms.go' |
2682 | --- mms/mms.go 1970-01-01 00:00:00 +0000 |
2683 | +++ mms/mms.go 2015-09-02 10:42:28 +0000 |
2684 | @@ -0,0 +1,438 @@ |
2685 | +/* |
2686 | + * Copyright 2014 Canonical Ltd. |
2687 | + * |
2688 | + * Authors: |
2689 | + * Sergio Schvezov: sergio.schvezov@cannical.com |
2690 | + * |
2691 | + * This file is part of mms. |
2692 | + * |
2693 | + * mms is free software; you can redistribute it and/or modify |
2694 | + * it under the terms of the GNU General Public License as published by |
2695 | + * the Free Software Foundation; version 3. |
2696 | + * |
2697 | + * mms is distributed in the hope that it will be useful, |
2698 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2699 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2700 | + * GNU General Public License for more details. |
2701 | + * |
2702 | + * You should have received a copy of the GNU General Public License |
2703 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2704 | + */ |
2705 | + |
2706 | +package mms |
2707 | + |
2708 | +import ( |
2709 | + "errors" |
2710 | + "fmt" |
2711 | + "log" |
2712 | + "os" |
2713 | + "strings" |
2714 | + "time" |
2715 | +) |
2716 | + |
2717 | +// MMS Field names from OMA-WAP-MMS section 7.3 Table 12 |
2718 | +const ( |
2719 | + BCC = 0x01 |
2720 | + CC = 0x02 |
2721 | + X_MMS_CONTENT_LOCATION = 0x03 |
2722 | + CONTENT_TYPE = 0x04 |
2723 | + DATE = 0x05 |
2724 | + X_MMS_DELIVERY_REPORT = 0x06 |
2725 | + X_MMS_DELIVERY_TIME = 0x07 |
2726 | + X_MMS_EXPIRY = 0x08 |
2727 | + FROM = 0x09 |
2728 | + X_MMS_MESSAGE_CLASS = 0x0A |
2729 | + MESSAGE_ID = 0x0B |
2730 | + X_MMS_MESSAGE_TYPE = 0x0C |
2731 | + X_MMS_MMS_VERSION = 0x0D |
2732 | + X_MMS_MESSAGE_SIZE = 0x0E |
2733 | + X_MMS_PRIORITY = 0x0F |
2734 | + X_MMS_READ_REPORT = 0x10 |
2735 | + X_MMS_REPORT_ALLOWED = 0x11 |
2736 | + X_MMS_RESPONSE_STATUS = 0x12 |
2737 | + X_MMS_RESPONSE_TEXT = 0x13 |
2738 | + X_MMS_SENDER_VISIBILITY = 0x14 |
2739 | + X_MMS_STATUS = 0x15 |
2740 | + SUBJECT = 0x16 |
2741 | + TO = 0x17 |
2742 | + X_MMS_TRANSACTION_ID = 0x18 |
2743 | + X_MMS_RETRIEVE_STATUS = 0x19 |
2744 | + X_MMS_RETRIEVE_TEXT = 0x1A |
2745 | + X_MMS_READ_STATUS = 0x1B |
2746 | + X_MMS_REPLY_CHARGING = 0x1C |
2747 | + X_MMS_REPLY_CHARGING_DEADLINE = 0x1D |
2748 | + X_MMS_REPLY_CHARGING_ID = 0x1E |
2749 | + X_MMS_REPLY_CHARGING_SIZE = 0x1F |
2750 | + X_MMS_PREVIOUSLY_SENT_BY = 0x20 |
2751 | + X_MMS_PREVIOUSLY_SENT_DATE = 0x21 |
2752 | +) |
2753 | + |
2754 | +// MMS Content Type Assignments OMA-WAP-MMS section 7.3 Table 13 |
2755 | +const ( |
2756 | + PUSH_APPLICATION_ID = 4 |
2757 | + VND_WAP_MMS_MESSAGE = "application/vnd.wap.mms-message" |
2758 | +) |
2759 | + |
2760 | +const ( |
2761 | + TYPE_SEND_REQ = 0x80 |
2762 | + TYPE_SEND_CONF = 0x81 |
2763 | + TYPE_NOTIFICATION_IND = 0x82 |
2764 | + TYPE_NOTIFYRESP_IND = 0x83 |
2765 | + TYPE_RETRIEVE_CONF = 0x84 |
2766 | + TYPE_ACKNOWLEDGE_IND = 0x85 |
2767 | + TYPE_DELIVERY_IND = 0x86 |
2768 | +) |
2769 | + |
2770 | +const ( |
2771 | + MMS_MESSAGE_VERSION_1_0 = 0x90 |
2772 | + MMS_MESSAGE_VERSION_1_1 = 0x91 |
2773 | + MMS_MESSAGE_VERSION_1_2 = 0x92 |
2774 | + MMS_MESSAGE_VERSION_1_3 = 0x93 |
2775 | +) |
2776 | + |
2777 | +// Delivery Report defined in OMA-WAP-MMS section 7.2.6 |
2778 | +const ( |
2779 | + DeliveryReportYes byte = 128 |
2780 | + DeliveryReportNo byte = 129 |
2781 | +) |
2782 | + |
2783 | +// Expiry tokens defined in OMA-WAP-MMS section 7.2.10 |
2784 | +const ( |
2785 | + ExpiryTokenAbsolute byte = 128 |
2786 | + ExpiryTokenRelative byte = 129 |
2787 | +) |
2788 | + |
2789 | +// From tokens defined in OMA-WAP-MMS section 7.2.11 |
2790 | +const ( |
2791 | + TOKEN_ADDRESS_PRESENT = 0x80 |
2792 | + TOKEN_INSERT_ADDRESS = 0x81 |
2793 | +) |
2794 | + |
2795 | +// Message classes defined in OMA-WAP-MMS section 7.2.14 |
2796 | +const ( |
2797 | + ClassPersonal byte = 128 |
2798 | + ClassAdvertisement byte = 129 |
2799 | + ClassInformational byte = 130 |
2800 | + ClassAuto byte = 131 |
2801 | +) |
2802 | + |
2803 | +// Report Report defined in OMA-WAP-MMS 7.2.20 |
2804 | +const ( |
2805 | + ReadReportYes byte = 128 |
2806 | + ReadReportNo byte = 129 |
2807 | +) |
2808 | + |
2809 | +// Report Allowed defined in OMA-WAP-MMS section 7.2.26 |
2810 | +const ( |
2811 | + ReportAllowedYes byte = 128 |
2812 | + ReportAllowedNo byte = 129 |
2813 | +) |
2814 | + |
2815 | +// Response Status defined in OMA-WAP-MMS section 7.2.27 |
2816 | +// |
2817 | +// An MMS Client MUST react the same to a value in range 196 to 223 as it |
2818 | +// does to the value 192 (Error-transient-failure). |
2819 | +// |
2820 | +// An MMS Client MUST react the same to a value in range 234 to 255 as it |
2821 | +// does to the value 224 (Error-permanent-failure). |
2822 | +// |
2823 | +// Any other values SHALL NOT be used. They are reserved for future use. |
2824 | +// An MMS Client that receives such a reserved value MUST react the same |
2825 | +// as it does to the value 224 (Error-permanent-failure). |
2826 | +const ( |
2827 | + ResponseStatusOk byte = 128 |
2828 | + ResponseStatusErrorUnspecified byte = 129 // Obsolete |
2829 | + ResponseStatusErrorServiceDenied byte = 130 // Obsolete |
2830 | + ResponseStatusErrorMessageFormatCorrupt byte = 131 // Obsolete |
2831 | + ResponseStatusErrorSendingAddressUnresolved byte = 132 // Obsolete |
2832 | + ResponseStatusErrorMessageNotFound byte = 133 // Obsolete |
2833 | + ResponseStatusErrorNetworkProblem byte = 134 // Obsolete |
2834 | + ResponseStatusErrorContentNotAccepted byte = 135 // Obsolete |
2835 | + ResponseStatusErrorUnsupportedMessage byte = 136 |
2836 | + |
2837 | + ResponseStatusErrorTransientFailure byte = 192 |
2838 | + ResponseStatusErrorTransientAddressUnresolved byte = 193 |
2839 | + ResponseStatusErrorTransientMessageNotFound byte = 194 |
2840 | + ResponseStatusErrorTransientNetworkProblem byte = 195 |
2841 | + |
2842 | + ResponseStatusErrorTransientMaxReserved byte = 223 |
2843 | + |
2844 | + ResponseStatusErrorPermanentFailure byte = 224 |
2845 | + ResponseStatusErrorPermanentServiceDenied byte = 225 |
2846 | + ResponseStatusErrorPermanentMessageFormatCorrupt byte = 226 |
2847 | + ResponseStatusErrorPermanentAddressUnresolved byte = 227 |
2848 | + ResponseStatusErrorPermanentMessageNotFound byte = 228 |
2849 | + ResponseStatusErrorPermanentContentNotAccepted byte = 229 |
2850 | + ResponseStatusErrorPermanentReplyChargingLimitationsNotMet byte = 230 |
2851 | + ResponseStatusErrorPermanentReplyChargingRequestNotAccepted byte = 231 |
2852 | + ResponseStatusErrorPermanentReplyChargingForwardingDenied byte = 232 |
2853 | + ResponseStatusErrorPermanentReplyChargingNotSupported byte = 233 |
2854 | + |
2855 | + ResponseStatusErrorPermamentMaxReserved byte = 255 |
2856 | +) |
2857 | + |
2858 | +// Status defined in OMA-WAP-MMS section 7.2.23 |
2859 | +const ( |
2860 | + STATUS_EXPIRED = 128 |
2861 | + STATUS_RETRIEVED = 129 |
2862 | + STATUS_REJECTED = 130 |
2863 | + STATUS_DEFERRED = 131 |
2864 | + STATUS_UNRECOGNIZED = 132 |
2865 | +) |
2866 | + |
2867 | +// MSendReq holds a m-send.req message defined in |
2868 | +// OMA-WAP-MMS-ENC-v1.1 section 6.1.1 |
2869 | +type MSendReq struct { |
2870 | + UUID string `encode:"no"` |
2871 | + Type byte |
2872 | + TransactionId string |
2873 | + Version byte |
2874 | + Date uint64 `encode:"optional"` |
2875 | + From string |
2876 | + To string |
2877 | + Cc string `encode:"no"` |
2878 | + Bcc string `encode:"no"` |
2879 | + Subject string `encode:"optional"` |
2880 | + Class byte `encode:"optional"` |
2881 | + Expiry uint64 `encode:"optional"` |
2882 | + DeliveryTime uint64 `encode:"optional"` |
2883 | + Priority byte `encode:"optional"` |
2884 | + SenderVisibility byte `encode:"optional"` |
2885 | + DeliveryReport byte `encode:"optional"` |
2886 | + ReadReport byte `encode:"optional"` |
2887 | + ContentTypeStart string `encode:"no"` |
2888 | + ContentTypeType string `encode:"no"` |
2889 | + ContentType string |
2890 | + Attachments []*Attachment `encode:"no"` |
2891 | +} |
2892 | + |
2893 | +// MSendReq holds a m-send.conf message defined in |
2894 | +// OMA-WAP-MMS-ENC section 6.1.2 |
2895 | +type MSendConf struct { |
2896 | + Type byte |
2897 | + TransactionId string |
2898 | + Version byte |
2899 | + ResponseStatus byte |
2900 | + ResponseText string |
2901 | + MessageId string |
2902 | +} |
2903 | + |
2904 | +// MNotificationInd holds a m-notification.ind message defined in |
2905 | +// OMA-WAP-MMS-ENC section 6.2 |
2906 | +type MNotificationInd struct { |
2907 | + MMSReader |
2908 | + UUID string |
2909 | + Type, Version, Class, DeliveryReport byte |
2910 | + ReplyCharging, ReplyChargingDeadline byte |
2911 | + ReplyChargingId string |
2912 | + TransactionId, ContentLocation string |
2913 | + From, Subject string |
2914 | + Expiry, Size uint64 |
2915 | +} |
2916 | + |
2917 | +// MNotificationInd holds a m-notifyresp.ind message defined in |
2918 | +// OMA-WAP-MMS-ENC-v1.1 section 6.2 |
2919 | +type MNotifyRespInd struct { |
2920 | + UUID string `encode:"no"` |
2921 | + Type byte |
2922 | + TransactionId string |
2923 | + Version byte |
2924 | + Status byte |
2925 | + ReportAllowed byte `encode:"optional"` |
2926 | +} |
2927 | + |
2928 | +// MRetrieveConf holds a m-retrieve.conf message defined in |
2929 | +// OMA-WAP-MMS-ENC-v1.1 section 6.3 |
2930 | +type MRetrieveConf struct { |
2931 | + MMSReader |
2932 | + UUID string |
2933 | + Type, Version, Status, Class, Priority byte |
2934 | + ReplyCharging, ReplyChargingDeadline byte |
2935 | + ReplyChargingId string |
2936 | + ReadReport, RetrieveStatus, DeliveryReport byte |
2937 | + TransactionId, MessageId, RetrieveText string |
2938 | + From, Cc, Subject string |
2939 | + To string |
2940 | + ReportAllowed byte |
2941 | + Date uint64 |
2942 | + Content Attachment |
2943 | + Attachments []Attachment |
2944 | + Data []byte |
2945 | +} |
2946 | + |
2947 | +type MMSReader interface{} |
2948 | +type MMSWriter interface{} |
2949 | + |
2950 | +// NewMSendReq creates a personal message with a normal priority and no read report |
2951 | +func NewMSendReq(recipients []string, attachments []*Attachment, deliveryReport bool) *MSendReq { |
2952 | + for i := range recipients { |
2953 | + recipients[i] += "/TYPE=PLMN" |
2954 | + } |
2955 | + uuid := genUUID() |
2956 | + |
2957 | + orderedAttachments, smilStart, smilType := processAttachments(attachments) |
2958 | + |
2959 | + return &MSendReq{ |
2960 | + Type: TYPE_SEND_REQ, |
2961 | + To: strings.Join(recipients, ","), |
2962 | + TransactionId: uuid, |
2963 | + Version: MMS_MESSAGE_VERSION_1_1, |
2964 | + UUID: uuid, |
2965 | + Date: getDate(), |
2966 | + // this will expire the message in 7 days |
2967 | + Expiry: uint64(time.Duration(time.Hour * 24 * 7).Seconds()), |
2968 | + DeliveryReport: getDeliveryReport(deliveryReport), |
2969 | + ReadReport: ReadReportNo, |
2970 | + Class: ClassPersonal, |
2971 | + ContentType: "application/vnd.wap.multipart.related", |
2972 | + ContentTypeStart: smilStart, |
2973 | + ContentTypeType: smilType, |
2974 | + Attachments: orderedAttachments, |
2975 | + } |
2976 | +} |
2977 | + |
2978 | +func NewMSendConf() *MSendConf { |
2979 | + return &MSendConf{ |
2980 | + Type: TYPE_SEND_CONF, |
2981 | + } |
2982 | +} |
2983 | + |
2984 | +func NewMNotificationInd() *MNotificationInd { |
2985 | + return &MNotificationInd{Type: TYPE_NOTIFICATION_IND, UUID: genUUID()} |
2986 | +} |
2987 | + |
2988 | +func (mNotificationInd *MNotificationInd) NewMNotifyRespInd(status byte, deliveryReport bool) *MNotifyRespInd { |
2989 | + return &MNotifyRespInd{ |
2990 | + Type: TYPE_NOTIFYRESP_IND, |
2991 | + UUID: mNotificationInd.UUID, |
2992 | + TransactionId: mNotificationInd.TransactionId, |
2993 | + Version: mNotificationInd.Version, |
2994 | + Status: status, |
2995 | + ReportAllowed: getReportAllowed(deliveryReport), |
2996 | + } |
2997 | +} |
2998 | + |
2999 | +func (mRetrieveConf *MRetrieveConf) NewMNotifyRespInd(deliveryReport bool) *MNotifyRespInd { |
3000 | + return &MNotifyRespInd{ |
3001 | + Type: TYPE_NOTIFYRESP_IND, |
3002 | + UUID: mRetrieveConf.UUID, |
3003 | + TransactionId: mRetrieveConf.TransactionId, |
3004 | + Version: mRetrieveConf.Version, |
3005 | + Status: STATUS_RETRIEVED, |
3006 | + ReportAllowed: getReportAllowed(deliveryReport), |
3007 | + } |
3008 | +} |
3009 | + |
3010 | +func NewMNotifyRespInd() *MNotifyRespInd { |
3011 | + return &MNotifyRespInd{Type: TYPE_NOTIFYRESP_IND} |
3012 | +} |
3013 | + |
3014 | +func NewMRetrieveConf(uuid string) *MRetrieveConf { |
3015 | + return &MRetrieveConf{Type: TYPE_RETRIEVE_CONF, UUID: uuid} |
3016 | +} |
3017 | + |
3018 | +func genUUID() string { |
3019 | + var id string |
3020 | + random, err := os.Open("/dev/urandom") |
3021 | + if err != nil { |
3022 | + id = "1234567890ABCDEF" |
3023 | + } else { |
3024 | + defer random.Close() |
3025 | + b := make([]byte, 16) |
3026 | + random.Read(b) |
3027 | + id = fmt.Sprintf("%x", b) |
3028 | + } |
3029 | + return id |
3030 | +} |
3031 | + |
3032 | +var ErrTransient = errors.New("Error-transient-failure") |
3033 | +var ErrPermanent = errors.New("Error-permament-failure") |
3034 | + |
3035 | +func (mSendConf *MSendConf) Status() error { |
3036 | + s := mSendConf.ResponseStatus |
3037 | + // these are case by case Response Status and we need to determine each one |
3038 | + switch s { |
3039 | + case ResponseStatusOk: |
3040 | + return nil |
3041 | + case ResponseStatusErrorUnspecified: |
3042 | + return ErrTransient |
3043 | + case ResponseStatusErrorServiceDenied: |
3044 | + return ErrTransient |
3045 | + case ResponseStatusErrorMessageFormatCorrupt: |
3046 | + return ErrPermanent |
3047 | + case ResponseStatusErrorSendingAddressUnresolved: |
3048 | + return ErrPermanent |
3049 | + case ResponseStatusErrorMessageNotFound: |
3050 | + // this could be ErrTransient or ErrPermanent |
3051 | + return ErrPermanent |
3052 | + case ResponseStatusErrorNetworkProblem: |
3053 | + return ErrTransient |
3054 | + case ResponseStatusErrorContentNotAccepted: |
3055 | + return ErrPermanent |
3056 | + case ResponseStatusErrorUnsupportedMessage: |
3057 | + return ErrPermanent |
3058 | + } |
3059 | + |
3060 | + // these are the Response Status we can group |
3061 | + if s >= ResponseStatusErrorTransientFailure && s <= ResponseStatusErrorTransientMaxReserved { |
3062 | + return ErrTransient |
3063 | + } else if s >= ResponseStatusErrorPermanentFailure && s <= ResponseStatusErrorPermamentMaxReserved { |
3064 | + return ErrPermanent |
3065 | + } |
3066 | + |
3067 | + // any case not handled is a permanent error |
3068 | + return ErrPermanent |
3069 | +} |
3070 | + |
3071 | +func getReadReport(v bool) (read byte) { |
3072 | + if v { |
3073 | + read = ReadReportYes |
3074 | + } else { |
3075 | + read = ReadReportNo |
3076 | + } |
3077 | + return read |
3078 | +} |
3079 | + |
3080 | +func getDeliveryReport(v bool) (delivery byte) { |
3081 | + if v { |
3082 | + delivery = DeliveryReportYes |
3083 | + } else { |
3084 | + delivery = DeliveryReportNo |
3085 | + } |
3086 | + return delivery |
3087 | +} |
3088 | + |
3089 | +func getReportAllowed(v bool) (allowed byte) { |
3090 | + if v { |
3091 | + allowed = ReportAllowedYes |
3092 | + } else { |
3093 | + allowed = ReportAllowedNo |
3094 | + } |
3095 | + return allowed |
3096 | +} |
3097 | + |
3098 | +func getDate() (date uint64) { |
3099 | + d := time.Now().Unix() |
3100 | + if d > 0 { |
3101 | + date = uint64(d) |
3102 | + } |
3103 | + return date |
3104 | +} |
3105 | + |
3106 | +func processAttachments(a []*Attachment) (oa []*Attachment, smilStart, smilType string) { |
3107 | + oa = make([]*Attachment, 0, len(a)) |
3108 | + for i := range a { |
3109 | + if strings.HasPrefix(a[i].MediaType, "application/smil") { |
3110 | + oa = append([]*Attachment{a[i]}, oa...) |
3111 | + var err error |
3112 | + smilStart, err = getSmilStart(a[i].Data) |
3113 | + if err != nil { |
3114 | + log.Println("Cannot set content type start:", err) |
3115 | + } |
3116 | + smilType = "application/smil" |
3117 | + } else { |
3118 | + oa = append(oa, a[i]) |
3119 | + } |
3120 | + } |
3121 | + return oa, smilStart, smilType |
3122 | +} |
3123 | |
3124 | === added file 'mms/mms_test.go' |
3125 | --- mms/mms_test.go 1970-01-01 00:00:00 +0000 |
3126 | +++ mms/mms_test.go 2015-09-02 10:42:28 +0000 |
3127 | @@ -0,0 +1,37 @@ |
3128 | +/* |
3129 | + * Copyright 2014 Canonical Ltd. |
3130 | + * |
3131 | + * Authors: |
3132 | + * Sergio Schvezov: sergio.schvezov@cannical.com |
3133 | + * |
3134 | + * This file is part of mms. |
3135 | + * |
3136 | + * mms is free software; you can redistribute it and/or modify |
3137 | + * it under the terms of the GNU General Public License as published by |
3138 | + * the Free Software Foundation; version 3. |
3139 | + * |
3140 | + * mms is distributed in the hope that it will be useful, |
3141 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3142 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3143 | + * GNU General Public License for more details. |
3144 | + * |
3145 | + * You should have received a copy of the GNU General Public License |
3146 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3147 | + */ |
3148 | + |
3149 | +package mms |
3150 | + |
3151 | +import . "launchpad.net/gocheck" |
3152 | + |
3153 | +type MMSTestSuite struct{} |
3154 | + |
3155 | +var _ = Suite(&MMSTestSuite{}) |
3156 | + |
3157 | +func (s *MMSTestSuite) TestNewMSendReq(c *C) { |
3158 | + recipients := []string{"+11111", "+22222", "+33333"} |
3159 | + recipientsStr := "+11111/TYPE=PLMN,+22222/TYPE=PLMN,+33333/TYPE=PLMN" |
3160 | + mSendReq := NewMSendReq(recipients, []*Attachment{}, false) |
3161 | + c.Check(mSendReq.To, Equals, recipientsStr) |
3162 | + c.Check(mSendReq.ContentType, Equals, "application/vnd.wap.multipart.related") |
3163 | + c.Check(mSendReq.Type, Equals, byte(TYPE_SEND_REQ)) |
3164 | +} |
3165 | |
3166 | === added file 'mms/parameters.go' |
3167 | --- mms/parameters.go 1970-01-01 00:00:00 +0000 |
3168 | +++ mms/parameters.go 2015-09-02 10:42:28 +0000 |
3169 | @@ -0,0 +1,153 @@ |
3170 | +/* |
3171 | + * Copyright 2014 Canonical Ltd. |
3172 | + * |
3173 | + * Authors: |
3174 | + * Sergio Schvezov: sergio.schvezov@cannical.com |
3175 | + * |
3176 | + * This file is part of mms. |
3177 | + * |
3178 | + * mms is free software; you can redistribute it and/or modify |
3179 | + * it under the terms of the GNU General Public License as published by |
3180 | + * the Free Software Foundation; version 3. |
3181 | + * |
3182 | + * mms is distributed in the hope that it will be useful, |
3183 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3184 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3185 | + * GNU General Public License for more details. |
3186 | + * |
3187 | + * You should have received a copy of the GNU General Public License |
3188 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3189 | + */ |
3190 | + |
3191 | +package mms |
3192 | + |
3193 | +//Table 38 of Well-Known Parameter Assignments from OMA-WAP-MMS section 7.3 |
3194 | +const ( |
3195 | + WSP_PARAMETER_TYPE_Q = 0x00 // Version 1.1 Q-value |
3196 | + WSP_PARAMETER_TYPE_CHARSET = 0x01 // Version 1.1 Well-known-charset |
3197 | + WSP_PARAMETER_TYPE_LEVEL = 0x02 // Version 1.1 Version-value |
3198 | + WSP_PARAMETER_TYPE_TYPE = 0x03 // Version 1.1 Integer-value |
3199 | + WSP_PARAMETER_TYPE_NAME_DEFUNCT = 0x05 // Version 1.1 Text-string |
3200 | + WSP_PARAMETER_TYPE_FILENAME_DEFUNCT = 0x06 // Version 1.1 Text-string |
3201 | + WSP_PARAMETER_TYPE_DIFFERENCES = 0x07 // Version 1.1 Field-name |
3202 | + WSP_PARAMETER_TYPE_PADDING = 0x08 // Version 1.1 Short-integer |
3203 | + WSP_PARAMETER_TYPE_CONTENT_TYPE = 0x09 // Version 1.2 Constrained-encoding |
3204 | + WSP_PARAMETER_TYPE_START_DEFUNCT = 0x0A // Version 1.2 Text-string |
3205 | + WSP_PARAMETER_TYPE_START_INFO_DEFUNCT = 0x0B // Version 1.2 Text-string |
3206 | + WSP_PARAMETER_TYPE_COMMENT_DEFUNCT = 0x0C // Version 1.3 Text-string |
3207 | + WSP_PARAMETER_TYPE_DOMAIN_DEFUNCT = 0x0D // Version 1.3 Text-string |
3208 | + WSP_PARAMETER_TYPE_MAX_AGE = 0x0E // Version 1.3 Delta-seconds-value |
3209 | + WSP_PARAMETER_TYPE_PATH_DEFUNCT = 0x0F // Version 1.3 Text-string |
3210 | + WSP_PARAMETER_TYPE_SECURE = 0x10 // Version 1.3 No-value |
3211 | + WSP_PARAMETER_TYPE_SEC = 0x11 // Version 1.4 Short-integer |
3212 | + WSP_PARAMETER_TYPE_MAC = 0x12 // Version 1.4 Text-value |
3213 | + WSP_PARAMETER_TYPE_CREATION_DATE = 0x13 // Version 1.4 Date-value |
3214 | + WSP_PARAMETER_TYPE_MODIFICATION_DATE = 0x14 // Version 1.4 Date-value |
3215 | + WSP_PARAMETER_TYPE_READ_DATE = 0x15 // Version 1.4 Date-value |
3216 | + WSP_PARAMETER_TYPE_SIZE = 0x16 // Version 1.4 Integer-value |
3217 | + WSP_PARAMETER_TYPE_NAME = 0x17 // Version 1.4 Text-value |
3218 | + WSP_PARAMETER_TYPE_FILENAME = 0x18 // Version 1.4 Text-value |
3219 | + WSP_PARAMETER_TYPE_START = 0x19 // Version 1.4 Text-value |
3220 | + WSP_PARAMETER_TYPE_START_INFO = 0x1A // Version 1.4 Text-value |
3221 | + WSP_PARAMETER_TYPE_COMMENT = 0x1B // Version 1.4 Text-value |
3222 | + WSP_PARAMETER_TYPE_DOMAIN = 0x1C // Version 1.4 Text-value |
3223 | + WSP_PARAMETER_TYPE_PATH = 0x1D // Version 1.4 Text-value |
3224 | + WSP_PARAMETER_TYPE_UNTYPED = 0xFF // Version 1.4 Text-value |
3225 | +) |
3226 | + |
3227 | +const ( |
3228 | + MMS_PART_CONTENT_LOCATION = 0x0E |
3229 | + MMS_PART_CONTENT_ID = 0x40 |
3230 | +) |
3231 | + |
3232 | +const ( |
3233 | + TEXT_MAX = 127 |
3234 | + TEXT_MIN = 32 |
3235 | + SHORT_LENGTH_MAX = 30 |
3236 | + LENGTH_QUOTE = 31 |
3237 | + STRING_QUOTE = 34 |
3238 | + SHORT_FILTER = 0x80 |
3239 | +) |
3240 | + |
3241 | +const ( |
3242 | + ANY_CHARSET = 128 |
3243 | +) |
3244 | + |
3245 | +var CONTENT_TYPES []string = []string{ |
3246 | + "*/*", "text/*", "text/html", "text/plain", |
3247 | + "text/x-hdml", "text/x-ttml", "text/x-vCalendar", |
3248 | + "text/x-vCard", "text/vnd.wap.wml", |
3249 | + "text/vnd.wap.wmlscript", "text/vnd.wap.wta-event", |
3250 | + "multipart/*", "multipart/mixed", "multipart/form-data", |
3251 | + "multipart/byterantes", "multipart/alternative", |
3252 | + "application/*", "application/java-vm", |
3253 | + "application/x-www-form-urlencoded", |
3254 | + "application/x-hdmlc", "application/vnd.wap.wmlc", |
3255 | + "application/vnd.wap.wmlscriptc", |
3256 | + "application/vnd.wap.wta-eventc", |
3257 | + "application/vnd.wap.uaprof", |
3258 | + "application/vnd.wap.wtls-ca-certificate", |
3259 | + "application/vnd.wap.wtls-user-certificate", |
3260 | + "application/x-x509-ca-cert", |
3261 | + "application/x-x509-user-cert", |
3262 | + "image/*", "image/gif", "image/jpeg", "image/tiff", |
3263 | + "image/png", "image/vnd.wap.wbmp", |
3264 | + "application/vnd.wap.multipart.*", |
3265 | + "application/vnd.wap.multipart.mixed", |
3266 | + "application/vnd.wap.multipart.form-data", |
3267 | + "application/vnd.wap.multipart.byteranges", |
3268 | + "application/vnd.wap.multipart.alternative", |
3269 | + "application/xml", "text/xml", |
3270 | + "application/vnd.wap.wbxml", |
3271 | + "application/x-x968-cross-cert", |
3272 | + "application/x-x968-ca-cert", |
3273 | + "application/x-x968-user-cert", |
3274 | + "text/vnd.wap.si", |
3275 | + "application/vnd.wap.sic", |
3276 | + "text/vnd.wap.sl", |
3277 | + "application/vnd.wap.slc", |
3278 | + "text/vnd.wap.co", |
3279 | + "application/vnd.wap.coc", |
3280 | + "application/vnd.wap.multipart.related", |
3281 | + "application/vnd.wap.sia", |
3282 | + "text/vnd.wap.connectivity-xml", |
3283 | + "application/vnd.wap.connectivity-wbxml", |
3284 | + "application/pkcs7-mime", |
3285 | + "application/vnd.wap.hashed-certificate", |
3286 | + "application/vnd.wap.signed-certificate", |
3287 | + "application/vnd.wap.cert-response", |
3288 | + "application/xhtml+xml", |
3289 | + "application/wml+xml", |
3290 | + "text/css", |
3291 | + "application/vnd.wap.mms-message", |
3292 | + "application/vnd.wap.rollover-certificate", |
3293 | + "application/vnd.wap.locc+wbxml", |
3294 | + "application/vnd.wap.loc+xml", |
3295 | + "application/vnd.syncml.dm+wbxml", |
3296 | + "application/vnd.syncml.dm+xml", |
3297 | + "application/vnd.syncml.notification", |
3298 | + "application/vnd.wap.xhtml+xml", |
3299 | + "application/vnd.wv.csp.cir", |
3300 | + "application/vnd.oma.dd+xml", |
3301 | + "application/vnd.oma.drm.message", |
3302 | + "application/vnd.oma.drm.content", |
3303 | + "application/vnd.oma.drm.rights+xml", |
3304 | + "application/vnd.oma.drm.rights+wbxml", |
3305 | +} |
3306 | + |
3307 | +var CHARSETS map[uint64]string = map[uint64]string{ |
3308 | + 0x07EA: "big5", |
3309 | + 0x03E8: "iso-10646-ucs-2", |
3310 | + 0x04: "iso-8859-1", |
3311 | + 0x05: "iso-8859-2", |
3312 | + 0x06: "iso-8859-3", |
3313 | + 0x07: "iso-8859-4", |
3314 | + 0x08: "iso-8859-5", |
3315 | + 0x09: "iso-8859-6", |
3316 | + 0x0A: "iso-8859-7", |
3317 | + 0x0B: "iso-8859-8", |
3318 | + 0x0C: "iso-8859-9", |
3319 | + 0x11: "shift_JIS", |
3320 | + 0x03: "us-ascii", |
3321 | + 0x6A: "utf-8", |
3322 | +} |
3323 | |
3324 | === added directory 'mms/test_payloads' |
3325 | === added file 'mms/test_payloads/m-send.conf_success' |
3326 | Binary files mms/test_payloads/m-send.conf_success 1970-01-01 00:00:00 +0000 and mms/test_payloads/m-send.conf_success 2015-09-02 10:42:28 +0000 differ |
3327 | === added directory 'ofono' |
3328 | === renamed directory 'ofono' => 'ofono.moved' |
3329 | === added file 'ofono/common.go' |
3330 | --- ofono/common.go 1970-01-01 00:00:00 +0000 |
3331 | +++ ofono/common.go 2015-09-02 10:42:28 +0000 |
3332 | @@ -0,0 +1,69 @@ |
3333 | +/* |
3334 | + * Copyright 2014 Canonical Ltd. |
3335 | + * |
3336 | + * Authors: |
3337 | + * Sergio Schvezov: sergio.schvezov@cannical.com |
3338 | + * |
3339 | + * This file is part of nuntium. |
3340 | + * |
3341 | + * nuntium is free software; you can redistribute it and/or modify |
3342 | + * it under the terms of the GNU General Public License as published by |
3343 | + * the Free Software Foundation; version 3. |
3344 | + * |
3345 | + * nuntium is distributed in the hope that it will be useful, |
3346 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3347 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3348 | + * GNU General Public License for more details. |
3349 | + * |
3350 | + * You should have received a copy of the GNU General Public License |
3351 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3352 | + */ |
3353 | + |
3354 | +package ofono |
3355 | + |
3356 | +import "launchpad.net/go-dbus/v1" |
3357 | + |
3358 | +const ( |
3359 | + AGENT_TAG = dbus.ObjectPath("/nuntium") |
3360 | + PUSH_NOTIFICATION_INTERFACE = "org.ofono.PushNotification" |
3361 | + PUSH_NOTIFICATION_AGENT_INTERFACE = "org.ofono.PushNotificationAgent" |
3362 | + CONNECTION_MANAGER_INTERFACE = "org.ofono.ConnectionManager" |
3363 | + CONNECTION_CONTEXT_INTERFACE = "org.ofono.ConnectionContext" |
3364 | + SIM_MANAGER_INTERFACE = "org.ofono.SimManager" |
3365 | + OFONO_MANAGER_INTERFACE = "org.ofono.Manager" |
3366 | + OFONO_SENDER = "org.ofono" |
3367 | + MODEM_INTERFACE = "org.ofono.Modem" |
3368 | +) |
3369 | + |
3370 | +type PropertiesType map[string]dbus.Variant |
3371 | + |
3372 | +func getModems(conn *dbus.Connection) (modemPaths []dbus.ObjectPath, err error) { |
3373 | + modemsReply, err := getOfonoProps(conn, "/", OFONO_SENDER, "org.ofono.Manager", "GetModems") |
3374 | + if err != nil { |
3375 | + return nil, err |
3376 | + } |
3377 | + for _, modemReply := range modemsReply { |
3378 | + modemPaths = append(modemPaths, modemReply.ObjectPath) |
3379 | + } |
3380 | + return modemPaths, nil |
3381 | +} |
3382 | + |
3383 | +func connectToPropertySignal(conn *dbus.Connection, path dbus.ObjectPath, inter string) (*dbus.SignalWatch, error) { |
3384 | + w, err := conn.WatchSignal(&dbus.MatchRule{ |
3385 | + Type: dbus.TypeSignal, |
3386 | + Sender: OFONO_SENDER, |
3387 | + Interface: inter, |
3388 | + Member: "PropertyChanged", |
3389 | + Path: path}) |
3390 | + return w, err |
3391 | +} |
3392 | + |
3393 | +func connectToSignal(conn *dbus.Connection, path dbus.ObjectPath, inter, member string) (*dbus.SignalWatch, error) { |
3394 | + w, err := conn.WatchSignal(&dbus.MatchRule{ |
3395 | + Type: dbus.TypeSignal, |
3396 | + Sender: OFONO_SENDER, |
3397 | + Interface: inter, |
3398 | + Member: member, |
3399 | + Path: path}) |
3400 | + return w, err |
3401 | +} |
3402 | |
3403 | === added file 'ofono/context_test.go' |
3404 | --- ofono/context_test.go 1970-01-01 00:00:00 +0000 |
3405 | +++ ofono/context_test.go 2015-09-02 10:42:28 +0000 |
3406 | @@ -0,0 +1,342 @@ |
3407 | +/* |
3408 | + * Copyright 2014 Canonical Ltd. |
3409 | + * |
3410 | + * Authors: |
3411 | + * Sergio Schvezov: sergio.schvezov@canonical.com |
3412 | + * |
3413 | + * This file is part of mms. |
3414 | + * |
3415 | + * mms is free software; you can redistribute it and/or modify |
3416 | + * it under the terms of the GNU General Public License as published by |
3417 | + * the Free Software Foundation; version 3. |
3418 | + * |
3419 | + * mms is distributed in the hope that it will be useful, |
3420 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3421 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3422 | + * GNU General Public License for more details. |
3423 | + * |
3424 | + * You should have received a copy of the GNU General Public License |
3425 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3426 | + */ |
3427 | + |
3428 | +package ofono |
3429 | + |
3430 | +import ( |
3431 | + "errors" |
3432 | + "fmt" |
3433 | + |
3434 | + "launchpad.net/go-dbus/v1" |
3435 | + . "launchpad.net/gocheck" |
3436 | +) |
3437 | + |
3438 | +type ContextTestSuite struct { |
3439 | + modem Modem |
3440 | + contexts []OfonoContext |
3441 | +} |
3442 | + |
3443 | +var _ = Suite(&ContextTestSuite{}) |
3444 | + |
3445 | +var proxy ProxyInfo |
3446 | + |
3447 | +func makeGenericContextProperty(name, cType string, active, messageCenter, messageProxy bool) PropertiesType { |
3448 | + p := make(PropertiesType) |
3449 | + p["Name"] = dbus.Variant{name} |
3450 | + p["Type"] = dbus.Variant{cType} |
3451 | + p["Active"] = dbus.Variant{active} |
3452 | + if messageCenter { |
3453 | + p["MessageCenter"] = dbus.Variant{"http://messagecenter.com"} |
3454 | + } else { |
3455 | + p["MessageCenter"] = dbus.Variant{""} |
3456 | + } |
3457 | + if messageProxy { |
3458 | + p["MessageProxy"] = dbus.Variant{proxy.String()} |
3459 | + } else { |
3460 | + p["MessageProxy"] = dbus.Variant{""} |
3461 | + } |
3462 | + return p |
3463 | +} |
3464 | + |
3465 | +func (s *ContextTestSuite) SetUpSuite(c *C) { |
3466 | +} |
3467 | + |
3468 | +func (s *ContextTestSuite) SetUpTest(c *C) { |
3469 | + s.modem = Modem{} |
3470 | + s.contexts = []OfonoContext{} |
3471 | + proxy = ProxyInfo{ |
3472 | + Host: "4.4.4.4", |
3473 | + Port: 9999, |
3474 | + } |
3475 | + getOfonoProps = func(conn *dbus.Connection, objectPath dbus.ObjectPath, destination, iface, method string) (oProps []OfonoContext, err error) { |
3476 | + return s.contexts, nil |
3477 | + } |
3478 | +} |
3479 | + |
3480 | +func (s *ContextTestSuite) TestNoContext(c *C) { |
3481 | + context, err := s.modem.GetMMSContexts("") |
3482 | + c.Check(context, IsNil) |
3483 | + c.Assert(err, DeepEquals, errors.New("No mms contexts found")) |
3484 | +} |
3485 | + |
3486 | +func (s *ContextTestSuite) TestMMSOverInternet(c *C) { |
3487 | + context1 := OfonoContext{ |
3488 | + ObjectPath: "/ril_0/context1", |
3489 | + Properties: makeGenericContextProperty("Context1", contextTypeInternet, true, true, true), |
3490 | + } |
3491 | + s.contexts = append(s.contexts, context1) |
3492 | + |
3493 | + contexts, err := s.modem.GetMMSContexts("") |
3494 | + c.Assert(err, IsNil) |
3495 | + c.Assert(len(contexts), Equals, 1) |
3496 | + c.Check(contexts[0], DeepEquals, context1) |
3497 | +} |
3498 | + |
3499 | +func (s *ContextTestSuite) TestMMSOverInactiveInternet(c *C) { |
3500 | + context1 := OfonoContext{ |
3501 | + ObjectPath: "/ril_0/context1", |
3502 | + Properties: makeGenericContextProperty("Context1", contextTypeInternet, false, true, true), |
3503 | + } |
3504 | + s.contexts = append(s.contexts, context1) |
3505 | + |
3506 | + context, err := s.modem.GetMMSContexts("") |
3507 | + c.Check(context, IsNil) |
3508 | + c.Assert(err, DeepEquals, errors.New("No mms contexts found")) |
3509 | +} |
3510 | + |
3511 | +func (s *ContextTestSuite) TestMMSOverInternetNoProxy(c *C) { |
3512 | + context1 := OfonoContext{ |
3513 | + ObjectPath: "/ril_0/context1", |
3514 | + Properties: makeGenericContextProperty("Context1", contextTypeInternet, true, true, false), |
3515 | + } |
3516 | + s.contexts = append(s.contexts, context1) |
3517 | + |
3518 | + contexts, err := s.modem.GetMMSContexts("") |
3519 | + c.Assert(err, IsNil) |
3520 | + c.Assert(len(contexts), Equals, 1) |
3521 | + c.Check(contexts[0], DeepEquals, context1) |
3522 | +} |
3523 | + |
3524 | +func (s *ContextTestSuite) TestMMSOverMMS(c *C) { |
3525 | + context1 := OfonoContext{ |
3526 | + ObjectPath: "/ril_0/context1", |
3527 | + Properties: makeGenericContextProperty("Context1", contextTypeInternet, true, false, false), |
3528 | + } |
3529 | + s.contexts = append(s.contexts, context1) |
3530 | + |
3531 | + context2 := OfonoContext{ |
3532 | + ObjectPath: "/ril_0/context2", |
3533 | + Properties: makeGenericContextProperty("Context2", contextTypeMMS, false, true, true), |
3534 | + } |
3535 | + s.contexts = append(s.contexts, context2) |
3536 | + |
3537 | + contexts, err := s.modem.GetMMSContexts("") |
3538 | + c.Assert(err, IsNil) |
3539 | + c.Assert(len(contexts), Equals, 1) |
3540 | + c.Check(contexts[0], DeepEquals, context2) |
3541 | +} |
3542 | + |
3543 | +func (s *ContextTestSuite) TestMMSOverMMSNoProxy(c *C) { |
3544 | + context1 := OfonoContext{ |
3545 | + ObjectPath: "/ril_0/context1", |
3546 | + Properties: makeGenericContextProperty("Context1", contextTypeInternet, true, false, false), |
3547 | + } |
3548 | + s.contexts = append(s.contexts, context1) |
3549 | + |
3550 | + context2 := OfonoContext{ |
3551 | + ObjectPath: "/ril_0/context2", |
3552 | + Properties: makeGenericContextProperty("Context2", contextTypeMMS, false, true, false), |
3553 | + } |
3554 | + s.contexts = append(s.contexts, context2) |
3555 | + |
3556 | + contexts, err := s.modem.GetMMSContexts("") |
3557 | + c.Assert(err, IsNil) |
3558 | + c.Assert(len(contexts), Equals, 1) |
3559 | + c.Check(contexts[0], DeepEquals, context2) |
3560 | +} |
3561 | + |
3562 | +func (s *ContextTestSuite) TestMMSMoreThanOneValid(c *C) { |
3563 | + context1 := OfonoContext{ |
3564 | + ObjectPath: "/ril_0/context1", |
3565 | + Properties: makeGenericContextProperty("Context1", contextTypeInternet, true, true, false), |
3566 | + } |
3567 | + s.contexts = append(s.contexts, context1) |
3568 | + |
3569 | + context2 := OfonoContext{ |
3570 | + ObjectPath: "/ril_0/context2", |
3571 | + Properties: makeGenericContextProperty("Context2", contextTypeMMS, false, true, false), |
3572 | + } |
3573 | + s.contexts = append(s.contexts, context2) |
3574 | + |
3575 | + contexts, err := s.modem.GetMMSContexts("") |
3576 | + c.Assert(err, IsNil) |
3577 | + c.Assert(len(contexts), Equals, 2) |
3578 | + c.Check(contexts[0], DeepEquals, context1) |
3579 | + c.Check(contexts[1], DeepEquals, context2) |
3580 | +} |
3581 | + |
3582 | +func (s *ContextTestSuite) TestMMSMoreThanOneValidContextSelectPreferred(c *C) { |
3583 | + context1 := OfonoContext{ |
3584 | + ObjectPath: "/ril_0/context1", |
3585 | + Properties: makeGenericContextProperty("Context1", contextTypeInternet, true, true, false), |
3586 | + } |
3587 | + s.contexts = append(s.contexts, context1) |
3588 | + |
3589 | + context2 := OfonoContext{ |
3590 | + ObjectPath: "/ril_0/context2", |
3591 | + Properties: makeGenericContextProperty("Context2", contextTypeMMS, false, true, false), |
3592 | + } |
3593 | + s.contexts = append(s.contexts, context2) |
3594 | + |
3595 | + context3 := OfonoContext{ |
3596 | + ObjectPath: "/ril_0/context3", |
3597 | + Properties: makeGenericContextProperty("Context3", contextTypeMMS, false, true, false), |
3598 | + } |
3599 | + s.contexts = append(s.contexts, context3) |
3600 | + |
3601 | + contexts, err := s.modem.GetMMSContexts("/ril_0/context2") |
3602 | + c.Assert(err, IsNil) |
3603 | + c.Assert(len(contexts), Equals, 3) |
3604 | + c.Check(contexts[0], DeepEquals, context2) |
3605 | + c.Check(contexts[1], DeepEquals, context1) |
3606 | + c.Check(contexts[2], DeepEquals, context3) |
3607 | +} |
3608 | + |
3609 | +func (s *ContextTestSuite) TestMMSMoreThanOneValidContextPreferredNoMatch(c *C) { |
3610 | + context1 := OfonoContext{ |
3611 | + ObjectPath: "/ril_0/context1", |
3612 | + Properties: makeGenericContextProperty("Context1", contextTypeInternet, true, true, false), |
3613 | + } |
3614 | + s.contexts = append(s.contexts, context1) |
3615 | + |
3616 | + context2 := OfonoContext{ |
3617 | + ObjectPath: "/ril_0/context2", |
3618 | + Properties: makeGenericContextProperty("Context2", contextTypeMMS, false, true, false), |
3619 | + } |
3620 | + s.contexts = append(s.contexts, context2) |
3621 | + |
3622 | + context3 := OfonoContext{ |
3623 | + ObjectPath: "/ril_0/context3", |
3624 | + Properties: makeGenericContextProperty("Context3", contextTypeMMS, false, true, false), |
3625 | + } |
3626 | + s.contexts = append(s.contexts, context3) |
3627 | + |
3628 | + contexts, err := s.modem.GetMMSContexts("/ril_0/context25") |
3629 | + c.Assert(err, IsNil) |
3630 | + c.Assert(len(contexts), Equals, 3) |
3631 | + c.Check(contexts[0], DeepEquals, context1) |
3632 | + c.Check(contexts[1], DeepEquals, context2) |
3633 | + c.Check(contexts[2], DeepEquals, context3) |
3634 | +} |
3635 | + |
3636 | +func (s *ContextTestSuite) TestMMSMoreThanOneValidContext2Active(c *C) { |
3637 | + context0 := OfonoContext{ |
3638 | + ObjectPath: "/ril_0/context0", |
3639 | + Properties: makeGenericContextProperty("Context0", contextTypeInternet, false, true, false), |
3640 | + } |
3641 | + s.contexts = append(s.contexts, context0) |
3642 | + |
3643 | + context1 := OfonoContext{ |
3644 | + ObjectPath: "/ril_0/context1", |
3645 | + Properties: makeGenericContextProperty("Context1", contextTypeMMS, false, true, false), |
3646 | + } |
3647 | + s.contexts = append(s.contexts, context1) |
3648 | + |
3649 | + context2 := OfonoContext{ |
3650 | + ObjectPath: "/ril_0/context2", |
3651 | + Properties: makeGenericContextProperty("Context2", contextTypeMMS, false, true, false), |
3652 | + } |
3653 | + s.contexts = append(s.contexts, context2) |
3654 | + |
3655 | + context3 := OfonoContext{ |
3656 | + ObjectPath: "/ril_0/context3", |
3657 | + Properties: makeGenericContextProperty("Context3", contextTypeMMS, true, true, false), |
3658 | + } |
3659 | + s.contexts = append(s.contexts, context3) |
3660 | + |
3661 | + contexts, err := s.modem.GetMMSContexts("") |
3662 | + c.Assert(err, IsNil) |
3663 | + c.Assert(len(contexts), Equals, 3) |
3664 | + c.Check(contexts[0], DeepEquals, context3) |
3665 | + c.Check(contexts[1], DeepEquals, context1) |
3666 | + c.Check(contexts[2], DeepEquals, context2) |
3667 | +} |
3668 | + |
3669 | +func (s *ContextTestSuite) TestMMSMoreThanOneValidContextPreferredNotActive(c *C) { |
3670 | + context0 := OfonoContext{ |
3671 | + ObjectPath: "/ril_0/context0", |
3672 | + Properties: makeGenericContextProperty("Context1", contextTypeInternet, true, true, false), |
3673 | + } |
3674 | + s.contexts = append(s.contexts, context0) |
3675 | + |
3676 | + context1 := OfonoContext{ |
3677 | + ObjectPath: "/ril_0/context1", |
3678 | + Properties: makeGenericContextProperty("Context1", contextTypeMMS, false, true, false), |
3679 | + } |
3680 | + s.contexts = append(s.contexts, context1) |
3681 | + |
3682 | + context2 := OfonoContext{ |
3683 | + ObjectPath: "/ril_0/context2", |
3684 | + Properties: makeGenericContextProperty("Context2", contextTypeMMS, false, true, false), |
3685 | + } |
3686 | + s.contexts = append(s.contexts, context2) |
3687 | + |
3688 | + context3 := OfonoContext{ |
3689 | + ObjectPath: "/ril_0/context3", |
3690 | + Properties: makeGenericContextProperty("Context3", contextTypeMMS, false, true, false), |
3691 | + } |
3692 | + |
3693 | + s.contexts = append(s.contexts, context3) |
3694 | + |
3695 | + contexts, err := s.modem.GetMMSContexts("/ril_0/context3") |
3696 | + c.Assert(err, IsNil) |
3697 | + c.Assert(len(contexts), Equals, 4) |
3698 | + c.Check(contexts[0], DeepEquals, context3) |
3699 | + c.Check(contexts[1], DeepEquals, context0) |
3700 | + c.Check(contexts[2], DeepEquals, context1) |
3701 | + c.Check(contexts[3], DeepEquals, context2) |
3702 | +} |
3703 | + |
3704 | +func (s *ContextTestSuite) TestGetProxy(c *C) { |
3705 | + context := OfonoContext{ |
3706 | + ObjectPath: "/ril_0/context1", |
3707 | + Properties: makeGenericContextProperty("Context1", contextTypeInternet, true, true, true), |
3708 | + } |
3709 | + |
3710 | + p, err := context.GetProxy() |
3711 | + c.Assert(err, IsNil) |
3712 | + c.Check(p, DeepEquals, proxy) |
3713 | +} |
3714 | + |
3715 | +func (s *ContextTestSuite) TestGetProxyNoProxy(c *C) { |
3716 | + context := OfonoContext{ |
3717 | + ObjectPath: "/ril_0/context1", |
3718 | + Properties: makeGenericContextProperty("Context1", contextTypeInternet, true, true, false), |
3719 | + } |
3720 | + |
3721 | + p, err := context.GetProxy() |
3722 | + c.Assert(err, IsNil) |
3723 | + c.Check(p, DeepEquals, ProxyInfo{}) |
3724 | +} |
3725 | + |
3726 | +func (s *ContextTestSuite) TestGetProxyWithHTTP(c *C) { |
3727 | + context := OfonoContext{ |
3728 | + ObjectPath: "/ril_0/context1", |
3729 | + Properties: makeGenericContextProperty("Context1", contextTypeInternet, true, true, true), |
3730 | + } |
3731 | + context.Properties["MessageProxy"] = dbus.Variant{fmt.Sprintf("http://%s:%d", proxy.Host, proxy.Port)} |
3732 | + |
3733 | + p, err := context.GetProxy() |
3734 | + c.Assert(err, IsNil) |
3735 | + c.Check(p, DeepEquals, proxy) |
3736 | +} |
3737 | + |
3738 | +func (s *ContextTestSuite) TestGetProxyNoPort(c *C) { |
3739 | + context := OfonoContext{ |
3740 | + ObjectPath: "/ril_0/context1", |
3741 | + Properties: makeGenericContextProperty("Context1", contextTypeInternet, true, true, true), |
3742 | + } |
3743 | + context.Properties["MessageProxy"] = dbus.Variant{fmt.Sprintf("http://%s", proxy.Host)} |
3744 | + |
3745 | + p, err := context.GetProxy() |
3746 | + c.Assert(err, IsNil) |
3747 | + c.Check(p, DeepEquals, ProxyInfo{Host: proxy.Host, Port: 80}) |
3748 | +} |
3749 | |
3750 | === added file 'ofono/manager.go' |
3751 | --- ofono/manager.go 1970-01-01 00:00:00 +0000 |
3752 | +++ ofono/manager.go 2015-09-02 10:42:28 +0000 |
3753 | @@ -0,0 +1,111 @@ |
3754 | +/* |
3755 | + * Copyright 2014 Canonical Ltd. |
3756 | + * |
3757 | + * Authors: |
3758 | + * Sergio Schvezov: sergio.schvezov@cannical.com |
3759 | + * |
3760 | + * This file is part of nuntium. |
3761 | + * |
3762 | + * nuntium is free software; you can redistribute it and/or modify |
3763 | + * it under the terms of the GNU General Public License as published by |
3764 | + * the Free Software Foundation; version 3. |
3765 | + * |
3766 | + * nuntium is distributed in the hope that it will be useful, |
3767 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3768 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3769 | + * GNU General Public License for more details. |
3770 | + * |
3771 | + * You should have received a copy of the GNU General Public License |
3772 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3773 | + */ |
3774 | + |
3775 | +package ofono |
3776 | + |
3777 | +import ( |
3778 | + "log" |
3779 | + |
3780 | + "launchpad.net/go-dbus/v1" |
3781 | +) |
3782 | + |
3783 | +type Modems map[dbus.ObjectPath]*Modem |
3784 | + |
3785 | +type ModemManager struct { |
3786 | + ModemAdded chan (*Modem) |
3787 | + ModemRemoved chan (*Modem) |
3788 | + modems Modems |
3789 | + conn *dbus.Connection |
3790 | +} |
3791 | + |
3792 | +func NewModemManager(conn *dbus.Connection) *ModemManager { |
3793 | + return &ModemManager{ |
3794 | + conn: conn, |
3795 | + ModemAdded: make(chan *Modem), |
3796 | + ModemRemoved: make(chan *Modem), |
3797 | + modems: make(Modems), |
3798 | + } |
3799 | +} |
3800 | + |
3801 | +func (mm *ModemManager) Init() error { |
3802 | + modemAddedSignal, err := connectToSignal(mm.conn, "/", OFONO_MANAGER_INTERFACE, "ModemAdded") |
3803 | + if err != nil { |
3804 | + return err |
3805 | + } |
3806 | + modemRemovedSignal, err := connectToSignal(mm.conn, "/", OFONO_MANAGER_INTERFACE, "ModemRemoved") |
3807 | + if err != nil { |
3808 | + return err |
3809 | + } |
3810 | + go mm.watchModems(modemAddedSignal, modemRemovedSignal) |
3811 | + |
3812 | + //Check for existing modems |
3813 | + modemPaths, err := getModems(mm.conn) |
3814 | + if err != nil { |
3815 | + log.Print("Cannot preemptively add modems: ", err) |
3816 | + } else { |
3817 | + for _, objectPath := range modemPaths { |
3818 | + mm.addModem(objectPath) |
3819 | + } |
3820 | + } |
3821 | + return nil |
3822 | +} |
3823 | + |
3824 | +func (mm *ModemManager) watchModems(modemAdded, modemRemoved *dbus.SignalWatch) { |
3825 | + for { |
3826 | + var objectPath dbus.ObjectPath |
3827 | + select { |
3828 | + case m := <-modemAdded.C: |
3829 | + var signalProps PropertiesType |
3830 | + if err := m.Args(&objectPath, &signalProps); err != nil { |
3831 | + log.Print(err) |
3832 | + continue |
3833 | + } |
3834 | + mm.addModem(objectPath) |
3835 | + case m := <-modemRemoved.C: |
3836 | + if err := m.Args(&objectPath); err != nil { |
3837 | + log.Print(err) |
3838 | + continue |
3839 | + } |
3840 | + mm.removeModem(objectPath) |
3841 | + } |
3842 | + } |
3843 | +} |
3844 | + |
3845 | +func (mm *ModemManager) addModem(objectPath dbus.ObjectPath) { |
3846 | + if modem, ok := mm.modems[objectPath]; ok { |
3847 | + log.Print("Need to delete stale modem instance %s", modem.Modem) |
3848 | + modem.Delete() |
3849 | + delete(mm.modems, objectPath) |
3850 | + } |
3851 | + mm.modems[objectPath] = NewModem(mm.conn, objectPath) |
3852 | + mm.ModemAdded <- mm.modems[objectPath] |
3853 | +} |
3854 | + |
3855 | +func (mm *ModemManager) removeModem(objectPath dbus.ObjectPath) { |
3856 | + if modem, ok := mm.modems[objectPath]; ok { |
3857 | + mm.ModemRemoved <- mm.modems[objectPath] |
3858 | + log.Printf("Deleting modem instance %s", modem.Modem) |
3859 | + modem.Delete() |
3860 | + delete(mm.modems, objectPath) |
3861 | + } else { |
3862 | + log.Printf("Cannot satisfy request to remove modem %s as it does not exist", objectPath) |
3863 | + } |
3864 | +} |
3865 | |
3866 | === added file 'ofono/modem.go' |
3867 | --- ofono/modem.go 1970-01-01 00:00:00 +0000 |
3868 | +++ ofono/modem.go 2015-09-02 10:42:28 +0000 |
3869 | @@ -0,0 +1,428 @@ |
3870 | +/* |
3871 | + * Copyright 2014 Canonical Ltd. |
3872 | + * |
3873 | + * Authors: |
3874 | + * Sergio Schvezov: sergio.schvezov@cannical.com |
3875 | + * |
3876 | + * This file is part of nuntium. |
3877 | + * |
3878 | + * nuntium is free software; you can redistribute it and/or modify |
3879 | + * it under the terms of the GNU General Public License as published by |
3880 | + * the Free Software Foundation; version 3. |
3881 | + * |
3882 | + * nuntium is distributed in the hope that it will be useful, |
3883 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3884 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3885 | + * GNU General Public License for more details. |
3886 | + * |
3887 | + * You should have received a copy of the GNU General Public License |
3888 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3889 | + */ |
3890 | + |
3891 | +package ofono |
3892 | + |
3893 | +import ( |
3894 | + "errors" |
3895 | + "fmt" |
3896 | + "log" |
3897 | + "net" |
3898 | + "reflect" |
3899 | + "strconv" |
3900 | + "strings" |
3901 | + "time" |
3902 | + |
3903 | + "launchpad.net/go-dbus/v1" |
3904 | +) |
3905 | + |
3906 | +const ( |
3907 | + contextTypeInternet = "internet" |
3908 | + contextTypeMMS = "mms" |
3909 | +) |
3910 | + |
3911 | +const ( |
3912 | + ofonoAttachInProgressError = "org.ofono.AttachInProgress" |
3913 | + ofonoFailed = "org.ofono.Error.Failed" |
3914 | + ofonoInProgressError = "org.ofono.InProgress" |
3915 | + ofonoNotAttachedError = "org.ofono.Error.NotAttached" |
3916 | +) |
3917 | + |
3918 | +type OfonoContext struct { |
3919 | + ObjectPath dbus.ObjectPath |
3920 | + Properties PropertiesType |
3921 | +} |
3922 | + |
3923 | +type Modem struct { |
3924 | + conn *dbus.Connection |
3925 | + Modem dbus.ObjectPath |
3926 | + PushAgent *PushAgent |
3927 | + identity string |
3928 | + IdentityAdded chan string |
3929 | + IdentityRemoved chan string |
3930 | + endWatch chan bool |
3931 | + PushInterfaceAvailable chan bool |
3932 | + pushInterfaceAvailable bool |
3933 | + online bool |
3934 | + modemSignal, simSignal *dbus.SignalWatch |
3935 | +} |
3936 | + |
3937 | +type ProxyInfo struct { |
3938 | + Host string |
3939 | + Port uint64 |
3940 | +} |
3941 | + |
3942 | +func (p ProxyInfo) String() string { |
3943 | + return fmt.Sprintf("%s:%d", p.Host, p.Port) |
3944 | +} |
3945 | + |
3946 | +func (oProp OfonoContext) String() string { |
3947 | + var s string |
3948 | + s += fmt.Sprintf("ObjectPath: %s\n", oProp.ObjectPath) |
3949 | + for k, v := range oProp.Properties { |
3950 | + s += fmt.Sprint("\t", k, ": ", v.Value, "\n") |
3951 | + } |
3952 | + return s |
3953 | +} |
3954 | + |
3955 | +func NewModem(conn *dbus.Connection, objectPath dbus.ObjectPath) *Modem { |
3956 | + return &Modem{ |
3957 | + conn: conn, |
3958 | + Modem: objectPath, |
3959 | + IdentityAdded: make(chan string), |
3960 | + IdentityRemoved: make(chan string), |
3961 | + PushInterfaceAvailable: make(chan bool), |
3962 | + endWatch: make(chan bool), |
3963 | + PushAgent: NewPushAgent(objectPath), |
3964 | + } |
3965 | +} |
3966 | + |
3967 | +func (modem *Modem) Init() (err error) { |
3968 | + log.Printf("Initializing modem %s", modem.Modem) |
3969 | + modem.modemSignal, err = connectToPropertySignal(modem.conn, modem.Modem, MODEM_INTERFACE) |
3970 | + if err != nil { |
3971 | + return err |
3972 | + } |
3973 | + |
3974 | + modem.simSignal, err = connectToPropertySignal(modem.conn, modem.Modem, SIM_MANAGER_INTERFACE) |
3975 | + if err != nil { |
3976 | + return err |
3977 | + } |
3978 | + |
3979 | + // the calling order here avoids race conditions |
3980 | + go modem.watchStatus() |
3981 | + modem.fetchExistingStatus() |
3982 | + |
3983 | + return nil |
3984 | +} |
3985 | + |
3986 | +// fetchExistingStatus fetches key required for the modem to be considered operational |
3987 | +// from a push notification point of view |
3988 | +// |
3989 | +// status updates are fetched through dbus method calls |
3990 | +func (modem *Modem) fetchExistingStatus() { |
3991 | + if v, err := modem.getProperty(MODEM_INTERFACE, "Interfaces"); err == nil { |
3992 | + modem.updatePushInterfaceState(*v) |
3993 | + } else { |
3994 | + log.Print("Initial value couldn't be retrieved: ", err) |
3995 | + } |
3996 | + if v, err := modem.getProperty(MODEM_INTERFACE, "Online"); err == nil { |
3997 | + modem.handleOnlineState(*v) |
3998 | + } else { |
3999 | + log.Print("Initial value couldn't be retrieved: ", err) |
4000 | + } |
4001 | + if v, err := modem.getProperty(SIM_MANAGER_INTERFACE, "SubscriberIdentity"); err == nil { |
4002 | + modem.handleIdentity(*v) |
4003 | + } |
4004 | +} |
4005 | + |
4006 | +// watchStatus monitors key states required for the modem to be considered operational |
4007 | +// from a push notification point of view |
4008 | +// |
4009 | +// status updates are monitered by hooking up to dbus signals |
4010 | +func (modem *Modem) watchStatus() { |
4011 | + var propName string |
4012 | + var propValue dbus.Variant |
4013 | +watchloop: |
4014 | + for { |
4015 | + select { |
4016 | + case <-modem.endWatch: |
4017 | + log.Printf("Ending modem watch for %s", modem.Modem) |
4018 | + break watchloop |
4019 | + case msg, ok := <-modem.modemSignal.C: |
4020 | + if !ok { |
4021 | + modem.modemSignal.C = nil |
4022 | + continue watchloop |
4023 | + } |
4024 | + if err := msg.Args(&propName, &propValue); err != nil { |
4025 | + log.Printf("Cannot interpret Modem Property change: %s", err) |
4026 | + continue watchloop |
4027 | + } |
4028 | + switch propName { |
4029 | + case "Interfaces": |
4030 | + modem.updatePushInterfaceState(propValue) |
4031 | + case "Online": |
4032 | + modem.handleOnlineState(propValue) |
4033 | + default: |
4034 | + continue watchloop |
4035 | + } |
4036 | + case msg, ok := <-modem.simSignal.C: |
4037 | + if !ok { |
4038 | + modem.simSignal.C = nil |
4039 | + continue watchloop |
4040 | + } |
4041 | + if err := msg.Args(&propName, &propValue); err != nil { |
4042 | + log.Printf("Cannot interpret Sim Property change: %s", err) |
4043 | + continue watchloop |
4044 | + } |
4045 | + if propName != "SubscriberIdentity" { |
4046 | + continue watchloop |
4047 | + } |
4048 | + modem.handleIdentity(propValue) |
4049 | + } |
4050 | + } |
4051 | +} |
4052 | + |
4053 | +func (modem *Modem) handleOnlineState(propValue dbus.Variant) { |
4054 | + origState := modem.online |
4055 | + modem.online = reflect.ValueOf(propValue.Value).Bool() |
4056 | + if modem.online != origState { |
4057 | + log.Printf("Modem online: %t", modem.online) |
4058 | + } |
4059 | +} |
4060 | + |
4061 | +func (modem *Modem) handleIdentity(propValue dbus.Variant) { |
4062 | + identity := reflect.ValueOf(propValue.Value).String() |
4063 | + if identity == "" && modem.identity != "" { |
4064 | + log.Printf("Identity before remove %s", modem.identity) |
4065 | + |
4066 | + modem.IdentityRemoved <- identity |
4067 | + modem.identity = identity |
4068 | + } |
4069 | + log.Printf("Identity added %s", identity) |
4070 | + if identity != "" && modem.identity == "" { |
4071 | + modem.identity = identity |
4072 | + modem.IdentityAdded <- identity |
4073 | + } |
4074 | +} |
4075 | + |
4076 | +func (modem *Modem) updatePushInterfaceState(interfaces dbus.Variant) { |
4077 | + origState := modem.pushInterfaceAvailable |
4078 | + availableInterfaces := reflect.ValueOf(interfaces.Value) |
4079 | + for i := 0; i < availableInterfaces.Len(); i++ { |
4080 | + interfaceName := reflect.ValueOf(availableInterfaces.Index(i).Interface().(string)).String() |
4081 | + if interfaceName == PUSH_NOTIFICATION_INTERFACE { |
4082 | + modem.pushInterfaceAvailable = true |
4083 | + break |
4084 | + } |
4085 | + } |
4086 | + if modem.pushInterfaceAvailable != origState { |
4087 | + log.Printf("Push interface state: %t", modem.pushInterfaceAvailable) |
4088 | + if modem.pushInterfaceAvailable { |
4089 | + modem.PushInterfaceAvailable <- true |
4090 | + } else if modem.PushAgent.Registered { |
4091 | + modem.PushInterfaceAvailable <- false |
4092 | + } |
4093 | + } |
4094 | +} |
4095 | + |
4096 | +var getOfonoProps = func(conn *dbus.Connection, objectPath dbus.ObjectPath, destination, iface, method string) (oProps []OfonoContext, err error) { |
4097 | + obj := conn.Object(destination, objectPath) |
4098 | + reply, err := obj.Call(iface, method) |
4099 | + if err != nil || reply.Type == dbus.TypeError { |
4100 | + return oProps, err |
4101 | + } |
4102 | + if err := reply.Args(&oProps); err != nil { |
4103 | + return oProps, err |
4104 | + } |
4105 | + return oProps, err |
4106 | +} |
4107 | + |
4108 | +//ActivateMMSContext activates a context if necessary and returns the context |
4109 | +//to operate with MMS. |
4110 | +// |
4111 | +//If the context is already active it's a nop. |
4112 | +//Returns either the type=internet context or the type=mms, if none is found |
4113 | +//an error is returned. |
4114 | +func (modem *Modem) ActivateMMSContext(preferredContext dbus.ObjectPath) (OfonoContext, error) { |
4115 | + contexts, err := modem.GetMMSContexts(preferredContext) |
4116 | + if err != nil { |
4117 | + return OfonoContext{}, err |
4118 | + } |
4119 | + for _, context := range contexts { |
4120 | + if context.isActive() { |
4121 | + return context, nil |
4122 | + } |
4123 | + if err := context.toggleActive(true, modem.conn); err == nil { |
4124 | + return context, nil |
4125 | + } else { |
4126 | + log.Println("Failed to activate for", context.ObjectPath, ":", err) |
4127 | + } |
4128 | + } |
4129 | + return OfonoContext{}, errors.New("no context available to activate") |
4130 | +} |
4131 | + |
4132 | +//DeactivateMMSContext deactivates the context if it is of type mms |
4133 | +func (modem *Modem) DeactivateMMSContext(context OfonoContext) error { |
4134 | + if context.isTypeInternet() { |
4135 | + return nil |
4136 | + } |
4137 | + |
4138 | + return context.toggleActive(false, modem.conn) |
4139 | +} |
4140 | + |
4141 | +func activationErrorNeedsWait(err error) bool { |
4142 | + if dbusErr, ok := err.(*dbus.Error); ok { |
4143 | + return dbusErr.Name == ofonoInProgressError || dbusErr.Name == ofonoAttachInProgressError || dbusErr.Name == ofonoNotAttachedError || dbusErr.Name == ofonoFailed |
4144 | + } |
4145 | + return false |
4146 | +} |
4147 | + |
4148 | +func (context OfonoContext) toggleActive(state bool, conn *dbus.Connection) error { |
4149 | + log.Println("Trying to set Active property to", state, "for context on", state, context.ObjectPath) |
4150 | + obj := conn.Object("org.ofono", context.ObjectPath) |
4151 | + for i := 0; i < 3; i++ { |
4152 | + _, err := obj.Call(CONNECTION_CONTEXT_INTERFACE, "SetProperty", "Active", dbus.Variant{state}) |
4153 | + if err != nil { |
4154 | + log.Printf("Cannot set Activate to %t (try %d/3) interface on %s: %s", state, i+1, context.ObjectPath, err) |
4155 | + if activationErrorNeedsWait(err) { |
4156 | + time.Sleep(5 * time.Second) |
4157 | + } |
4158 | + } else { |
4159 | + return nil |
4160 | + } |
4161 | + } |
4162 | + return errors.New("failed to activate context") |
4163 | +} |
4164 | + |
4165 | +func (oContext OfonoContext) isTypeInternet() bool { |
4166 | + if v, ok := oContext.Properties["Type"]; ok { |
4167 | + return reflect.ValueOf(v.Value).String() == contextTypeInternet |
4168 | + } |
4169 | + return false |
4170 | +} |
4171 | + |
4172 | +func (oContext OfonoContext) isTypeMMS() bool { |
4173 | + if v, ok := oContext.Properties["Type"]; ok { |
4174 | + return reflect.ValueOf(v.Value).String() == contextTypeMMS |
4175 | + } |
4176 | + return false |
4177 | +} |
4178 | + |
4179 | +func (oContext OfonoContext) isActive() bool { |
4180 | + return reflect.ValueOf(oContext.Properties["Active"].Value).Bool() |
4181 | +} |
4182 | + |
4183 | +func (oContext OfonoContext) hasMessageCenter() bool { |
4184 | + return oContext.messageCenter() != "" |
4185 | +} |
4186 | + |
4187 | +func (oContext OfonoContext) messageCenter() string { |
4188 | + if v, ok := oContext.Properties["MessageCenter"]; ok { |
4189 | + return reflect.ValueOf(v.Value).String() |
4190 | + } |
4191 | + return "" |
4192 | +} |
4193 | + |
4194 | +func (oContext OfonoContext) messageProxy() string { |
4195 | + if v, ok := oContext.Properties["MessageProxy"]; ok { |
4196 | + return reflect.ValueOf(v.Value).String() |
4197 | + } |
4198 | + return "" |
4199 | +} |
4200 | + |
4201 | +func (oContext OfonoContext) name() string { |
4202 | + if v, ok := oContext.Properties["Name"]; ok { |
4203 | + return reflect.ValueOf(v.Value).String() |
4204 | + } |
4205 | + return "" |
4206 | +} |
4207 | + |
4208 | +func (oContext OfonoContext) GetMessageCenter() (string, error) { |
4209 | + if oContext.hasMessageCenter() { |
4210 | + return oContext.messageCenter(), nil |
4211 | + } else { |
4212 | + return "", errors.New("context setting for the Message Center value is empty") |
4213 | + } |
4214 | +} |
4215 | + |
4216 | +func (oContext OfonoContext) GetProxy() (proxyInfo ProxyInfo, err error) { |
4217 | + proxy := oContext.messageProxy() |
4218 | + // we need to support empty proxies |
4219 | + if proxy == "" { |
4220 | + return proxyInfo, nil |
4221 | + } |
4222 | + if strings.HasPrefix(proxy, "http://") { |
4223 | + proxy = proxy[len("http://"):] |
4224 | + } |
4225 | + var portString string |
4226 | + proxyInfo.Host, portString, err = net.SplitHostPort(proxy) |
4227 | + if err != nil { |
4228 | + proxyInfo.Host = proxy |
4229 | + proxyInfo.Port = 80 |
4230 | + return proxyInfo, nil |
4231 | + } |
4232 | + proxyInfo.Port, err = strconv.ParseUint(portString, 0, 64) |
4233 | + if err != nil { |
4234 | + return proxyInfo, err |
4235 | + } |
4236 | + return proxyInfo, nil |
4237 | +} |
4238 | + |
4239 | +//GetMMSContexts returns the contexts that are MMS capable; by convention it has |
4240 | +//been defined that for it to be MMS capable it either has to define a MessageProxy |
4241 | +//and a MessageCenter within the context. |
4242 | +// |
4243 | +//The following rules take place: |
4244 | +//- check current type=internet context for MessageProxy & MessageCenter; |
4245 | +// if they exist and aren't empty AND the context is active, select it as the |
4246 | +// context to use for MMS. |
4247 | +//- otherwise search for type=mms, if found, use it and activate |
4248 | +// |
4249 | +//Returns either the type=internet context or the type=mms, if none is found |
4250 | +//an error is returned. |
4251 | +func (modem *Modem) GetMMSContexts(preferredContext dbus.ObjectPath) (mmsContexts []OfonoContext, err error) { |
4252 | + contexts, err := getOfonoProps(modem.conn, modem.Modem, OFONO_SENDER, CONNECTION_MANAGER_INTERFACE, "GetContexts") |
4253 | + if err != nil { |
4254 | + return mmsContexts, err |
4255 | + } |
4256 | + |
4257 | + for _, context := range contexts { |
4258 | + if (context.isTypeInternet() && context.isActive() && context.hasMessageCenter()) || context.isTypeMMS() { |
4259 | + if context.ObjectPath == preferredContext || context.isActive() { |
4260 | + mmsContexts = append([]OfonoContext{context}, mmsContexts...) |
4261 | + } else { |
4262 | + mmsContexts = append(mmsContexts, context) |
4263 | + } |
4264 | + } |
4265 | + } |
4266 | + if len(mmsContexts) == 0 { |
4267 | + log.Printf("non matching contexts:\n %+v", contexts) |
4268 | + return mmsContexts, errors.New("No mms contexts found") |
4269 | + } |
4270 | + return mmsContexts, nil |
4271 | +} |
4272 | + |
4273 | +func (modem *Modem) getProperty(interfaceName, propertyName string) (*dbus.Variant, error) { |
4274 | + errorString := "Cannot retrieve %s from %s for %s: %s" |
4275 | + rilObj := modem.conn.Object(OFONO_SENDER, modem.Modem) |
4276 | + if reply, err := rilObj.Call(interfaceName, "GetProperties"); err == nil { |
4277 | + var property PropertiesType |
4278 | + if err := reply.Args(&property); err != nil { |
4279 | + return nil, fmt.Errorf(errorString, propertyName, interfaceName, modem.Modem, err) |
4280 | + } |
4281 | + if v, ok := property[propertyName]; ok { |
4282 | + return &v, nil |
4283 | + } |
4284 | + return nil, fmt.Errorf(errorString, propertyName, interfaceName, modem.Modem, "property not found") |
4285 | + } else { |
4286 | + return nil, fmt.Errorf(errorString, propertyName, interfaceName, modem.Modem, err) |
4287 | + } |
4288 | +} |
4289 | + |
4290 | +func (modem *Modem) Delete() { |
4291 | + modem.IdentityRemoved <- modem.identity |
4292 | + modem.modemSignal.Cancel() |
4293 | + modem.modemSignal.C = nil |
4294 | + modem.simSignal.Cancel() |
4295 | + modem.simSignal.C = nil |
4296 | + modem.endWatch <- true |
4297 | +} |
4298 | |
4299 | === added file 'ofono/push.go' |
4300 | --- ofono/push.go 1970-01-01 00:00:00 +0000 |
4301 | +++ ofono/push.go 2015-09-02 10:42:28 +0000 |
4302 | @@ -0,0 +1,112 @@ |
4303 | +/* |
4304 | + * Copyright 2014 Canonical Ltd. |
4305 | + * |
4306 | + * Authors: |
4307 | + * Sergio Schvezov: sergio.schvezov@cannical.com |
4308 | + * |
4309 | + * This file is part of nuntium. |
4310 | + * |
4311 | + * nuntium is free software; you can redistribute it and/or modify |
4312 | + * it under the terms of the GNU General Public License as published by |
4313 | + * the Free Software Foundation; version 3. |
4314 | + * |
4315 | + * nuntium is distributed in the hope that it will be useful, |
4316 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
4317 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4318 | + * GNU General Public License for more details. |
4319 | + * |
4320 | + * You should have received a copy of the GNU General Public License |
4321 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
4322 | + */ |
4323 | + |
4324 | +package ofono |
4325 | + |
4326 | +import ( |
4327 | + "errors" |
4328 | + "fmt" |
4329 | + "reflect" |
4330 | + |
4331 | + "launchpad.net/nuntium/mms" |
4332 | +) |
4333 | + |
4334 | +type PDU byte |
4335 | + |
4336 | +type PushPDU struct { |
4337 | + HeaderLength uint64 |
4338 | + ContentLength uint64 |
4339 | + ApplicationId, EncodingVersion, PushFlag byte |
4340 | + ContentType string |
4341 | + Data []byte |
4342 | +} |
4343 | + |
4344 | +type PushPDUDecoder struct { |
4345 | + mms.MMSDecoder |
4346 | +} |
4347 | + |
4348 | +func NewDecoder(data []byte) *PushPDUDecoder { |
4349 | + decoder := new(PushPDUDecoder) |
4350 | + decoder.MMSDecoder.Data = data |
4351 | + return decoder |
4352 | +} |
4353 | + |
4354 | +// The HeadersLen field specifies the length of the ContentType and Headers fields combined. |
4355 | +// The ContentType field contains the content type of the data. It conforms to the Content-Type value encoding specified |
4356 | +// in section 8.4.2.24, “Content type field”. |
4357 | +// The Headers field contains the push headers. |
4358 | +// The Data field contains the data pushed from the server. The length of the Data field is determined by the SDU size as |
4359 | +// provided to and reported from the underlying transport. The Data field starts immediately after the Headers field and |
4360 | +// ends at the end of the SDU. |
4361 | +func (dec *PushPDUDecoder) Decode(pdu *PushPDU) (err error) { |
4362 | + if PDU(dec.Data[1]) != PUSH { |
4363 | + return errors.New(fmt.Sprintf("%x != %x is not a push PDU", PDU(dec.Data[1]), PUSH)) |
4364 | + } |
4365 | + // Move offset +tid +type = +2 |
4366 | + dec.Offset = 1 |
4367 | + rValue := reflect.ValueOf(pdu).Elem() |
4368 | + if _, err = dec.ReadUintVar(&rValue, "HeaderLength"); err != nil { |
4369 | + return err |
4370 | + } |
4371 | + if err = dec.ReadMediaType(&rValue, "ContentType"); err != nil { |
4372 | + return err |
4373 | + } |
4374 | + dec.Offset++ |
4375 | + remainHeaders := int(pdu.HeaderLength) - dec.Offset + 3 |
4376 | + if err = dec.decodeHeaders(pdu, remainHeaders); err != nil { |
4377 | + return err |
4378 | + } |
4379 | + pdu.Data = dec.Data[(pdu.HeaderLength + 3):] |
4380 | + return nil |
4381 | +} |
4382 | + |
4383 | +func (dec *PushPDUDecoder) decodeHeaders(pdu *PushPDU, hdrLengthRemain int) error { |
4384 | + rValue := reflect.ValueOf(pdu).Elem() |
4385 | + var err error |
4386 | + for ; dec.Offset < (hdrLengthRemain + dec.Offset); dec.Offset++ { |
4387 | + param := dec.Data[dec.Offset] & 0x7F |
4388 | + switch param { |
4389 | + case X_WAP_APPLICATION_ID: |
4390 | + _, err = dec.ReadInteger(&rValue, "ApplicationId") |
4391 | + case PUSH_FLAG: |
4392 | + _, err = dec.ReadShortInteger(&rValue, "PushFlag") |
4393 | + case ENCODING_VERSION: |
4394 | + dec.Offset++ |
4395 | + pdu.EncodingVersion = dec.Data[dec.Offset] & 0x7F |
4396 | + dec.Offset++ |
4397 | + case CONTENT_LENGTH: |
4398 | + _, err = dec.ReadInteger(&rValue, "ContentLength") |
4399 | + case X_WAP_INITIATOR_URI: |
4400 | + var v string |
4401 | + v, err = dec.ReadString(nil, "") |
4402 | + fmt.Println("Unsaved value decoded:", v) |
4403 | + default: |
4404 | + err = fmt.Errorf("Unhandled header data %#x @%d", dec.Data[dec.Offset], dec.Offset) |
4405 | + } |
4406 | + if err != nil { |
4407 | + return fmt.Errorf("error while decoding %#x @%d: ", param, dec.Offset, err) |
4408 | + } else if pdu.ApplicationId != 0 { |
4409 | + return nil |
4410 | + } |
4411 | + |
4412 | + } |
4413 | + return nil |
4414 | +} |
4415 | |
4416 | === added file 'ofono/push_decode_test.go' |
4417 | --- ofono/push_decode_test.go 1970-01-01 00:00:00 +0000 |
4418 | +++ ofono/push_decode_test.go 2015-09-02 10:42:28 +0000 |
4419 | @@ -0,0 +1,170 @@ |
4420 | +/* |
4421 | + * Copyright 2014 Canonical Ltd. |
4422 | + * |
4423 | + * Authors: |
4424 | + * Sergio Schvezov: sergio.schvezov@canonical.com |
4425 | + * |
4426 | + * This file is part of mms. |
4427 | + * |
4428 | + * mms is free software; you can redistribute it and/or modify |
4429 | + * it under the terms of the GNU General Public License as published by |
4430 | + * the Free Software Foundation; version 3. |
4431 | + * |
4432 | + * mms is distributed in the hope that it will be useful, |
4433 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
4434 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4435 | + * GNU General Public License for more details. |
4436 | + * |
4437 | + * You should have received a copy of the GNU General Public License |
4438 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
4439 | + */ |
4440 | + |
4441 | +package ofono |
4442 | + |
4443 | +import ( |
4444 | + "errors" |
4445 | + "testing" |
4446 | + . "launchpad.net/gocheck" |
4447 | + "launchpad.net/nuntium/mms" |
4448 | +) |
4449 | + |
4450 | +type PushDecodeTestSuite struct { |
4451 | + pdu *PushPDU |
4452 | +} |
4453 | + |
4454 | +var _ = Suite(&PushDecodeTestSuite{}) |
4455 | + |
4456 | +func Test(t *testing.T) { TestingT(t) } |
4457 | + |
4458 | +func (s *PushDecodeTestSuite) SetUpTest(c *C) { |
4459 | + s.pdu = new(PushPDU) |
4460 | +} |
4461 | + |
4462 | +func (s *PushDecodeTestSuite) TestDecodeVodaphoneSpain(c *C) { |
4463 | + inputBytes := []byte{ |
4464 | + 0x00, 0x06, 0x26, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, |
4465 | + 0x6e, 0x2f, 0x76, 0x6e, 0x64, 0x2e, 0x77, 0x61, 0x70, 0x2e, 0x6d, 0x6d, 0x73, |
4466 | + 0x2d, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x00, 0xaf, 0x84, 0xb4, 0x81, |
4467 | + 0x8d, 0xdf, 0x8c, 0x82, 0x98, 0x4e, 0x4f, 0x4b, 0x35, 0x43, 0x64, 0x7a, 0x30, |
4468 | + 0x38, 0x42, 0x41, 0x73, 0x77, 0x61, 0x62, 0x77, 0x55, 0x48, 0x00, 0x8d, 0x90, |
4469 | + 0x89, 0x18, 0x80, 0x2b, 0x33, 0x34, 0x36, 0x30, 0x30, 0x39, 0x34, 0x34, 0x34, |
4470 | + 0x36, 0x33, 0x2f, 0x54, 0x59, 0x50, 0x45, 0x3d, 0x50, 0x4c, 0x4d, 0x4e, 0x00, |
4471 | + 0x8a, 0x80, 0x8e, 0x02, 0x74, 0x00, 0x88, 0x05, 0x81, 0x03, 0x02, 0xa3, 0x00, |
4472 | + 0x83, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6d, 0x6d, 0x31, 0x66, 0x65, |
4473 | + 0x31, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x6c, 0x65, 0x74, 0x73, 0x2f, 0x4e, 0x4f, |
4474 | + 0x4b, 0x35, 0x43, 0x64, 0x7a, 0x30, 0x38, 0x42, 0x41, 0x73, 0x77, 0x61, 0x62, |
4475 | + 0x77, 0x55, 0x48, 0x00, |
4476 | + } |
4477 | + dec := NewDecoder(inputBytes) |
4478 | + c.Assert(dec.Decode(s.pdu), IsNil) |
4479 | + |
4480 | + c.Check(int(s.pdu.HeaderLength), Equals, 38) |
4481 | + c.Check(int(s.pdu.ApplicationId), Equals, mms.PUSH_APPLICATION_ID) |
4482 | + c.Check(s.pdu.ContentType, Equals, mms.VND_WAP_MMS_MESSAGE) |
4483 | + c.Check(len(s.pdu.Data), Equals, 106) |
4484 | +} |
4485 | + |
4486 | +func (s *PushDecodeTestSuite) TestDecodeTelecomPersonal(c *C) { |
4487 | + inputBytes := []byte{ |
4488 | + 0x01, 0x06, 0x26, 0x61, 0x70, 0x70, 0x6C, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, |
4489 | + 0x6e, 0x2f, 0x76, 0x6e, 0x64, 0x2e, 0x77, 0x61, 0x70, 0x2e, 0x6d, 0x6d, 0x73, |
4490 | + 0x2d, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x00, 0xaf, 0x84, 0xb4, 0x86, |
4491 | + 0xc3, 0x95, 0x8c, 0x82, 0x98, 0x6d, 0x30, 0x34, 0x42, 0x4b, 0x6b, 0x73, 0x69, |
4492 | + 0x6d, 0x30, 0x35, 0x40, 0x6d, 0x6d, 0x73, 0x2e, 0x70, 0x65, 0x72, 0x73, 0x6f, |
4493 | + 0x6e, 0x61, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x72, 0x00, 0x8d, 0x90, |
4494 | + 0x89, 0x19, 0x80, 0x2b, 0x35, 0x34, 0x33, 0x35, 0x31, 0x35, 0x39, 0x32, 0x34, |
4495 | + 0x39, 0x30, 0x36, 0x2f, 0x54, 0x59, 0x50, 0x45, 0x3d, 0x50, 0x4c, 0x4d, 0x4e, |
4496 | + 0x00, 0x8a, 0x80, 0x8e, 0x02, 0x74, 0x00, 0x88, 0x05, 0x81, 0x03, 0x02, 0xa2, |
4497 | + 0xff, 0x83, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x31, 0x37, 0x32, 0x2e, |
4498 | + 0x32, 0x35, 0x2e, 0x37, 0x2e, 0x31, 0x33, 0x31, 0x2f, 0x3f, 0x6d, 0x65, 0x73, |
4499 | + 0x73, 0x61, 0x67, 0x65, 0x2d, 0x69, 0x64, 0x3d, 0x6d, 0x30, 0x34, 0x42, 0x4b, |
4500 | + 0x68, 0x34, 0x33, 0x65, 0x30, 0x33, 0x00, |
4501 | + } |
4502 | + dec := NewDecoder(inputBytes) |
4503 | + c.Assert(dec.Decode(s.pdu), IsNil) |
4504 | + |
4505 | + c.Check(int(s.pdu.HeaderLength), Equals, 38) |
4506 | + c.Check(int(s.pdu.ApplicationId), Equals, mms.PUSH_APPLICATION_ID) |
4507 | + c.Check(s.pdu.ContentType, Equals, mms.VND_WAP_MMS_MESSAGE) |
4508 | + c.Check(len(s.pdu.Data), Equals, 122) |
4509 | +} |
4510 | + |
4511 | +func (s *PushDecodeTestSuite) TestDecodeATTUSA(c *C) { |
4512 | + inputBytes := []byte{ |
4513 | + 0x01, 0x06, 0x27, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, |
4514 | + 0x6e, 0x2f, 0x76, 0x6e, 0x64, 0x2e, 0x77, 0x61, 0x70, 0x2e, 0x6d, 0x6d, 0x73, |
4515 | + 0x2d, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x00, 0xaf, 0x84, 0x8d, 0x01, |
4516 | + 0x82, 0xb4, 0x84, 0x8c, 0x82, 0x98, 0x44, 0x32, 0x30, 0x34, 0x30, 0x37, 0x31, |
4517 | + 0x36, 0x35, 0x36, 0x32, 0x34, 0x36, 0x30, 0x30, 0x30, 0x30, 0x34, 0x30, 0x30, |
4518 | + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x8d, 0x90, 0x89, 0x18, 0x80, |
4519 | + 0x2b, 0x31, 0x37, 0x37, 0x34, 0x32, 0x37, 0x30, 0x30, 0x36, 0x35, 0x39, 0x2f, |
4520 | + 0x54, 0x59, 0x50, 0x45, 0x3d, 0x50, 0x4c, 0x4d, 0x4e, 0x00, 0x96, 0x02, 0xea, |
4521 | + 0x00, 0x8a, 0x80, 0x8e, 0x02, 0x80, 0x00, 0x88, 0x05, 0x81, 0x03, 0x05, 0x46, |
4522 | + 0x00, 0x83, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x31, 0x36, 0x36, 0x2e, |
4523 | + 0x32, 0x31, 0x36, 0x2e, 0x31, 0x36, 0x36, 0x2e, 0x36, 0x37, 0x3a, 0x38, 0x30, |
4524 | + 0x30, 0x34, 0x2f, 0x30, 0x34, 0x30, 0x37, 0x31, 0x36, 0x35, 0x36, 0x32, 0x34, |
4525 | + 0x36, 0x30, 0x30, 0x30, 0x30, 0x34, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, |
4526 | + 0x30, 0x30, 0x00, |
4527 | + } |
4528 | + dec := NewDecoder(inputBytes) |
4529 | + c.Assert(dec.Decode(s.pdu), IsNil) |
4530 | + |
4531 | + c.Check(int(s.pdu.HeaderLength), Equals, 39) |
4532 | + c.Check(int(s.pdu.ApplicationId), Equals, mms.PUSH_APPLICATION_ID) |
4533 | + c.Check(s.pdu.ContentType, Equals, mms.VND_WAP_MMS_MESSAGE) |
4534 | + c.Check(len(s.pdu.Data), Equals, 130) |
4535 | +} |
4536 | + |
4537 | +func (s *PushDecodeTestSuite) TestDecodeSoneraFinland(c *C) { |
4538 | + inputBytes := []byte{ |
4539 | + 0x00, 0x06, 0x07, 0xbe, 0xaf, 0x84, 0x8d, 0xf2, 0xb4, 0x81, 0x8c, 0x82, 0x98, |
4540 | + 0x41, 0x42, 0x73, 0x54, 0x4c, 0x4e, 0x41, 0x4c, 0x41, 0x6d, 0x6d, 0x4e, 0x33, |
4541 | + 0x77, 0x72, 0x38, 0x32, 0x00, 0x8d, 0x92, 0x89, 0x19, 0x80, 0x2b, 0x33, 0x35, |
4542 | + 0x38, 0x34, 0x30, 0x37, 0x36, 0x39, 0x34, 0x34, 0x38, 0x34, 0x2f, 0x54, 0x59, |
4543 | + 0x50, 0x45, 0x3d, 0x50, 0x4c, 0x4d, 0x4e, 0x00, 0x86, 0x81, 0x8a, 0x80, 0x8e, |
4544 | + 0x03, 0x03, 0x15, 0x85, 0x88, 0x05, 0x81, 0x03, 0x03, 0xf4, 0x7f, 0x83, 0x68, |
4545 | + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6d, 0x6d, 0x73, 0x63, 0x36, 0x31, 0x3a, |
4546 | + 0x31, 0x30, 0x30, 0x32, 0x31, 0x2f, 0x6d, 0x6d, 0x73, 0x63, 0x2f, 0x36, 0x5f, |
4547 | + 0x31, 0x3f, 0x41, 0x42, 0x73, 0x54, 0x4c, 0x4e, 0x41, 0x4c, 0x41, 0x6d, 0x6d, |
4548 | + 0x4e, 0x33, 0x77, 0x72, 0x38, 0x32, 0x00, |
4549 | + } |
4550 | + dec := NewDecoder(inputBytes) |
4551 | + c.Assert(dec.Decode(s.pdu), IsNil) |
4552 | + |
4553 | + c.Check(int(s.pdu.HeaderLength), Equals, 7) |
4554 | + c.Check(int(s.pdu.ApplicationId), Equals, mms.PUSH_APPLICATION_ID) |
4555 | + c.Check(s.pdu.ContentType, Equals, mms.VND_WAP_MMS_MESSAGE) |
4556 | + c.Check(len(s.pdu.Data), Equals, 114) |
4557 | +} |
4558 | + |
4559 | +func (s *PushDecodeTestSuite) TestOperatorWithContentLength(c *C) { |
4560 | + inputBytes := []byte{ |
4561 | + 0x01, 0x06, 0x07, 0xbe, 0x8d, 0xf0, 0xaf, 0x84, 0xb4, 0x84, 0x8c, 0x82, 0x98, |
4562 | + 0x41, 0x78, 0x67, 0x41, 0x6a, 0x45, 0x73, 0x49, 0x47, 0x46, 0x57, 0x45, 0x54, |
4563 | + 0x45, 0x53, 0x76, 0x41, 0x00, 0x8d, 0x93, 0x89, 0x18, 0x80, 0x2b, 0x33, 0x31, |
4564 | + 0x36, 0x35, 0x35, 0x35, 0x38, 0x34, 0x34, 0x32, 0x35, 0x2f, 0x54, 0x59, 0x50, |
4565 | + 0x45, 0x3d, 0x50, 0x4c, 0x4d, 0x4e, 0x00, 0x86, 0x81, 0x8a, 0x80, 0x8e, 0x03, |
4566 | + 0x01, 0xc5, 0x0d, 0x88, 0x05, 0x81, 0x03, 0x03, 0xf4, 0x80, 0x83, 0x68, 0x74, |
4567 | + 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6d, 0x70, 0x2e, 0x6d, 0x6f, 0x62, 0x69, 0x65, |
4568 | + 0x6c, 0x2e, 0x6b, 0x70, 0x6e, 0x2f, 0x6d, 0x6d, 0x73, 0x63, 0x2f, 0x30, 0x31, |
4569 | + 0x3f, 0x41, 0x78, 0x67, 0x41, 0x6a, 0x45, 0x73, 0x49, 0x47, 0x46, 0x57, 0x45, |
4570 | + 0x54, 0x45, 0x53, 0x76, 0x41, 0x00, |
4571 | + } |
4572 | + |
4573 | + dec := NewDecoder(inputBytes) |
4574 | + c.Assert(dec.Decode(s.pdu), IsNil) |
4575 | + |
4576 | + c.Check(int(s.pdu.HeaderLength), Equals, 7) |
4577 | + c.Check(int(s.pdu.ContentLength), Equals, 112) |
4578 | + c.Check(int(s.pdu.ApplicationId), Equals, mms.PUSH_APPLICATION_ID) |
4579 | + c.Check(s.pdu.ContentType, Equals, mms.VND_WAP_MMS_MESSAGE) |
4580 | + c.Check(len(s.pdu.Data), Equals, 113) |
4581 | +} |
4582 | + |
4583 | +func (s *PushDecodeTestSuite) TestDecodeNonPushPDU(c *C) { |
4584 | + inputBytes := []byte{ |
4585 | + 0x00, 0x07, 0x07, 0xbe, 0xaf, 0x84, 0x8d, 0xf2, 0xb4, 0x81, 0x8c, |
4586 | + } |
4587 | + dec := NewDecoder(inputBytes) |
4588 | + c.Assert(dec.Decode(s.pdu), DeepEquals, errors.New("7 != 6 is not a push PDU")) |
4589 | +} |
4590 | |
4591 | === added file 'ofono/pushagent.go' |
4592 | --- ofono/pushagent.go 1970-01-01 00:00:00 +0000 |
4593 | +++ ofono/pushagent.go 2015-09-02 10:42:28 +0000 |
4594 | @@ -0,0 +1,153 @@ |
4595 | +/* |
4596 | + * Copyright 2014 Canonical Ltd. |
4597 | + * |
4598 | + * Authors: |
4599 | + * Sergio Schvezov: sergio.schvezov@cannical.com |
4600 | + * |
4601 | + * This file is part of nuntium. |
4602 | + * |
4603 | + * nuntium is free software; you can redistribute it and/or modify |
4604 | + * it under the terms of the GNU General Public License as published by |
4605 | + * the Free Software Foundation; version 3. |
4606 | + * |
4607 | + * nuntium is distributed in the hope that it will be useful, |
4608 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
4609 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4610 | + * GNU General Public License for more details. |
4611 | + * |
4612 | + * You should have received a copy of the GNU General Public License |
4613 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
4614 | + */ |
4615 | + |
4616 | +package ofono |
4617 | + |
4618 | +import ( |
4619 | + "encoding/hex" |
4620 | + "fmt" |
4621 | + "log" |
4622 | + "sync" |
4623 | + |
4624 | + "launchpad.net/go-dbus/v1" |
4625 | + "launchpad.net/nuntium/mms" |
4626 | +) |
4627 | + |
4628 | +/* |
4629 | + in = "aya{sv}", out = "" |
4630 | +*/ |
4631 | +type OfonoPushNotification struct { |
4632 | + Data []byte |
4633 | + Info map[string]*dbus.Variant |
4634 | +} |
4635 | + |
4636 | +type PushAgent struct { |
4637 | + conn *dbus.Connection |
4638 | + modem dbus.ObjectPath |
4639 | + Push chan *PushPDU |
4640 | + messageChannel chan *dbus.Message |
4641 | + Registered bool |
4642 | + m sync.Mutex |
4643 | +} |
4644 | + |
4645 | +func NewPushAgent(modem dbus.ObjectPath) *PushAgent { |
4646 | + return &PushAgent{modem: modem} |
4647 | +} |
4648 | + |
4649 | +func (agent *PushAgent) Register() (err error) { |
4650 | + agent.m.Lock() |
4651 | + defer agent.m.Unlock() |
4652 | + if agent.conn == nil { |
4653 | + if agent.conn, err = dbus.Connect(dbus.SystemBus); err != nil { |
4654 | + return err |
4655 | + } |
4656 | + } |
4657 | + if agent.Registered { |
4658 | + log.Printf("Agent already registered for %s", agent.modem) |
4659 | + return nil |
4660 | + } |
4661 | + agent.Registered = true |
4662 | + log.Print("Registering agent for ", agent.modem, " on path ", AGENT_TAG, " and name ", agent.conn.UniqueName) |
4663 | + obj := agent.conn.Object("org.ofono", agent.modem) |
4664 | + _, err = obj.Call(PUSH_NOTIFICATION_INTERFACE, "RegisterAgent", AGENT_TAG) |
4665 | + if err != nil { |
4666 | + return fmt.Errorf("Cannot register agent for %s: %s", agent.modem, err) |
4667 | + } |
4668 | + agent.Push = make(chan *PushPDU) |
4669 | + agent.messageChannel = make(chan *dbus.Message) |
4670 | + go agent.watchDBusMethodCalls() |
4671 | + agent.conn.RegisterObjectPath(AGENT_TAG, agent.messageChannel) |
4672 | + log.Print("Agent Registered for ", agent.modem, " on path ", AGENT_TAG) |
4673 | + return nil |
4674 | +} |
4675 | + |
4676 | +func (agent *PushAgent) Unregister() error { |
4677 | + agent.m.Lock() |
4678 | + defer agent.m.Unlock() |
4679 | + if !agent.Registered { |
4680 | + log.Print("Agent no registered for %s", agent.modem) |
4681 | + return nil |
4682 | + } |
4683 | + log.Print("Unregistering agent on ", agent.modem) |
4684 | + obj := agent.conn.Object("org.ofono", agent.modem) |
4685 | + _, err := obj.Call(PUSH_NOTIFICATION_INTERFACE, "UnregisterAgent", AGENT_TAG) |
4686 | + if err != nil { |
4687 | + log.Print("Unregister failed ", err) |
4688 | + return err |
4689 | + } |
4690 | + agent.release() |
4691 | + agent.modem = dbus.ObjectPath("") |
4692 | + return nil |
4693 | +} |
4694 | + |
4695 | +func (agent *PushAgent) release() { |
4696 | + agent.Registered = false |
4697 | + //BUG this seems to not return, but I can't close the channel or panic |
4698 | + agent.conn.UnregisterObjectPath(AGENT_TAG) |
4699 | + close(agent.Push) |
4700 | + agent.Push = nil |
4701 | + close(agent.messageChannel) |
4702 | + agent.messageChannel = nil |
4703 | +} |
4704 | + |
4705 | +func (agent *PushAgent) watchDBusMethodCalls() { |
4706 | + var reply *dbus.Message |
4707 | + for msg := range agent.messageChannel { |
4708 | + switch { |
4709 | + case msg.Interface == PUSH_NOTIFICATION_AGENT_INTERFACE && msg.Member == "ReceiveNotification": |
4710 | + reply = agent.notificationReceived(msg) |
4711 | + case msg.Interface == PUSH_NOTIFICATION_AGENT_INTERFACE && msg.Member == "Release": |
4712 | + log.Printf("Push Agent on %s received Release", agent.modem) |
4713 | + reply = dbus.NewMethodReturnMessage(msg) |
4714 | + agent.release() |
4715 | + default: |
4716 | + log.Print("Received unkown method call on", msg.Interface, msg.Member) |
4717 | + reply = dbus.NewErrorMessage(msg, "org.freedesktop.DBus.Error.UnknownMethod", "Unknown method") |
4718 | + } |
4719 | + if err := agent.conn.Send(reply); err != nil { |
4720 | + log.Print("Could not send reply: ", err) |
4721 | + } |
4722 | + } |
4723 | +} |
4724 | + |
4725 | +func (agent *PushAgent) notificationReceived(msg *dbus.Message) (reply *dbus.Message) { |
4726 | + var push OfonoPushNotification |
4727 | + if err := msg.Args(&(push.Data), &(push.Info)); err != nil { |
4728 | + log.Print("Error in received ReceiveNotification() method call ", msg) |
4729 | + return dbus.NewErrorMessage(msg, "org.freedesktop.DBus.Error", "FormatError") |
4730 | + } else { |
4731 | + log.Print("Received ReceiveNotification() method call from ", push.Info["Sender"].Value) |
4732 | + log.Print("Push data\n", hex.Dump(push.Data)) |
4733 | + dec := NewDecoder(push.Data) |
4734 | + pdu := new(PushPDU) |
4735 | + if err := dec.Decode(pdu); err != nil { |
4736 | + log.Print("Error ", err) |
4737 | + return dbus.NewErrorMessage(msg, "org.freedesktop.DBus.Error", "DecodeError") |
4738 | + } |
4739 | + // TODO later switch on ApplicationId and ContentType to different channels |
4740 | + if pdu.ApplicationId == mms.PUSH_APPLICATION_ID && pdu.ContentType == mms.VND_WAP_MMS_MESSAGE { |
4741 | + agent.Push <- pdu |
4742 | + } else { |
4743 | + log.Print("Unhandled push pdu", pdu) |
4744 | + } |
4745 | + return dbus.NewMethodReturnMessage(msg) |
4746 | + } |
4747 | +} |
4748 | |
4749 | === added file 'ofono/wsp_params.go' |
4750 | --- ofono/wsp_params.go 1970-01-01 00:00:00 +0000 |
4751 | +++ ofono/wsp_params.go 2015-09-02 10:42:28 +0000 |
4752 | @@ -0,0 +1,150 @@ |
4753 | +/* |
4754 | + * Copyright 2014 Canonical Ltd. |
4755 | + * |
4756 | + * Authors: |
4757 | + * Sergio Schvezov: sergio.schvezov@cannical.com |
4758 | + * |
4759 | + * This file is part of nuntium. |
4760 | + * |
4761 | + * nuntium is free software; you can redistribute it and/or modify |
4762 | + * it under the terms of the GNU General Public License as published by |
4763 | + * the Free Software Foundation; version 3. |
4764 | + * |
4765 | + * nuntium is distributed in the hope that it will be useful, |
4766 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
4767 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4768 | + * GNU General Public License for more details. |
4769 | + * |
4770 | + * You should have received a copy of the GNU General Public License |
4771 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
4772 | + */ |
4773 | + |
4774 | +package ofono |
4775 | + |
4776 | +//These are the WSP assigned numbers from Table 34. PDU Type Assignments - |
4777 | +//Appendix A Assigned Numbers in WAP-230-WSP |
4778 | +const ( |
4779 | + CONNECT PDU = 0x01 |
4780 | + CONNECT_REPLY PDU = 0x02 |
4781 | + REDIRECT PDU = 0x03 |
4782 | + REPLY PDU = 0x04 |
4783 | + DISCONNECT PDU = 0x05 |
4784 | + PUSH PDU = 0x06 |
4785 | + CONFIRMED_PUSH PDU = 0x07 |
4786 | + SUSPEND PDU = 0x08 |
4787 | + RESUME PDU = 0x09 |
4788 | + GET PDU = 0x40 |
4789 | + POST PDU = 0x60 |
4790 | +) |
4791 | + |
4792 | +//These are the WSP assigned numbers from Table 38 . Well-Known Parameter |
4793 | +//Assignments - Appendix A Assigned Numbers in WAP-230-WSP |
4794 | +const ( |
4795 | + WSP_PARAMETER_TYPE_Q = 0x00 |
4796 | + WSP_PARAMETER_TYPE_CHARSET = 0x01 |
4797 | + WSP_PARAMETER_TYPE_LEVEL = 0x02 |
4798 | + WSP_PARAMETER_TYPE_TYPE = 0x03 |
4799 | + WSP_PARAMETER_TYPE_NAME_DEFUNCT = 0x05 |
4800 | + WSP_PARAMETER_TYPE_FILENAME_DEFUNCT = 0x06 |
4801 | + WSP_PARAMETER_TYPE_DIFFERENCES = 0x07 |
4802 | + WSP_PARAMETER_TYPE_PADDING = 0x08 |
4803 | + WSP_PARAMETER_TYPE_CONTENT_TYPE = 0x09 |
4804 | + WSP_PARAMETER_TYPE_START_DEFUNCT = 0x0A |
4805 | + WSP_PARAMETER_TYPE_START_INFO_DEFUNCT = 0x0B |
4806 | + WSP_PARAMETER_TYPE_COMMENT_DEFUNCT = 0x0C |
4807 | + WSP_PARAMETER_TYPE_DOMAIN_DEFUNCT = 0x0D |
4808 | + WSP_PARAMETER_TYPE_MAX_AGE = 0x0E |
4809 | + WSP_PARAMETER_TYPE_PATH_DEFUNCT = 0x0F |
4810 | + WSP_PARAMETER_TYPE_SECURE = 0x10 |
4811 | + WSP_PARAMETER_TYPE_SEC = 0x11 |
4812 | + WSP_PARAMETER_TYPE_MAC = 0x12 |
4813 | + WSP_PARAMETER_TYPE_CREATION_DATE = 0x13 |
4814 | + WSP_PARAMETER_TYPE_MODIFICATION_DATE = 0x14 |
4815 | + WSP_PARAMETER_TYPE_READ_DATE = 0x15 |
4816 | + WSP_PARAMETER_TYPE_SIZE = 0x16 |
4817 | + WSP_PARAMETER_TYPE_NAME = 0x17 |
4818 | + WSP_PARAMETER_TYPE_FILENAME = 0x18 |
4819 | + WSP_PARAMETER_TYPE_START = 0x19 |
4820 | + WSP_PARAMETER_TYPE_START_INFO = 0x1A |
4821 | + WSP_PARAMETER_TYPE_COMMENT = 0x1B |
4822 | + WSP_PARAMETER_TYPE_DOMAIN = 0x1C |
4823 | + WSP_PARAMETER_TYPE_PATH = 0x1D |
4824 | + WSP_PARAMETER_TYPE_UNTYPED = 0xFF |
4825 | +) |
4826 | + |
4827 | +//These are the WSP assigned numbers from Table 39 . Header Field Name |
4828 | +//Assignments - Appendix A Assigned Numbers in WAP-230-WSP |
4829 | +const ( |
4830 | + ACCEPT = 0x00 |
4831 | + ACCEPT_CHARSET_1 = 0x01 |
4832 | + ACCEPT_ENCODING_1 = 0x02 |
4833 | + ACCEPT_LANGUAGE = 0x03 |
4834 | + ACCEPT_RANGES = 0x04 |
4835 | + AGE = 0x05 |
4836 | + ALLOW = 0x06 |
4837 | + AUTHORIZATION = 0x07 |
4838 | + CACHE_CONTROL_1 = 0x08 |
4839 | + CONNECTION = 0x09 |
4840 | + CONTENT_BASE = 0x0A |
4841 | + CONTENT_ENCODING = 0x0B |
4842 | + CONTENT_LANGUAGE = 0x0C |
4843 | + CONTENT_LENGTH = 0x0D |
4844 | + CONTENT_LOCATION = 0x0E |
4845 | + CONTENT_MD5 = 0x0F |
4846 | + CONTENT_RANGE_1 = 0x10 |
4847 | + CONTENT_TYPE = 0x11 |
4848 | + DATE = 0x12 |
4849 | + ETAG = 0x13 |
4850 | + EXPIRES = 0x14 |
4851 | + FROM = 0x15 |
4852 | + HOST = 0x16 |
4853 | + IF_MODIFIED_SINCE = 0x17 |
4854 | + IF_MATCH = 0x18 |
4855 | + IF_NONE_MATCH = 0x19 |
4856 | + IF_RANGE = 0x1A |
4857 | + IF_UNMODIFIED_SINCE = 0x1B |
4858 | + LOCATION = 0x1C |
4859 | + LAST_MODIFIED = 0x1D |
4860 | + MAX_FORWARDS = 0x1E |
4861 | + PRAGMA = 0x1F |
4862 | + PROXY_AUTHENTICATE = 0x20 |
4863 | + PROXY_AUTHORIZATION = 0x21 |
4864 | + PUBLIC = 0x22 |
4865 | + RANGE = 0x23 |
4866 | + REFERER = 0x24 |
4867 | + RETRY_AFTER = 0x25 |
4868 | + SERVER = 0x26 |
4869 | + TRANSFER_ENCODING = 0x27 |
4870 | + UPGRADE = 0x28 |
4871 | + USER_AGENT = 0x29 |
4872 | + VARY = 0x2A |
4873 | + VIA = 0x2B |
4874 | + WARNING = 0x2C |
4875 | + WWW_AUTHENTICATE = 0x2D |
4876 | + CONTENT_DISPOSITION_1 = 0x2E |
4877 | + X_WAP_APPLICATION_ID = 0x2F |
4878 | + X_WAP_CONTENT_URI = 0x30 |
4879 | + X_WAP_INITIATOR_URI = 0x31 |
4880 | + ACCEPT_APPLICATION = 0x32 |
4881 | + BEARER_INDICATION = 0x33 |
4882 | + PUSH_FLAG = 0x34 |
4883 | + PROFILE = 0x35 |
4884 | + PROFILE_DIFF = 0x36 |
4885 | + PROFILE_WARNING_1 = 0x37 |
4886 | + EXPECT = 0x38 |
4887 | + TE = 0x39 |
4888 | + TRAILER = 0x3A |
4889 | + ACCEPT_CHARSET = 0x3B |
4890 | + ACCEPT_ENCODING = 0x3C |
4891 | + CACHE_CONTROL_2 = 0x3D |
4892 | + CONTENT_RANGE = 0x3E |
4893 | + X_WAP_TOD = 0x3F |
4894 | + CONTENT_ID = 0x40 |
4895 | + SET_COOKIE = 0x41 |
4896 | + COOKIE = 0x42 |
4897 | + ENCODING_VERSION = 0x43 |
4898 | + PROFILE_WARNING = 0x44 |
4899 | + CONTENT_DISPOSITION = 0x45 |
4900 | + X_WAP_SECURITY = 0x46 |
4901 | + CACHE_CONTROL = 0x47 |
4902 | +) |
4903 | |
4904 | === added directory 'storage' |
4905 | === renamed directory 'storage' => 'storage.moved' |
4906 | === added file 'storage/const.go' |
4907 | --- storage/const.go 1970-01-01 00:00:00 +0000 |
4908 | +++ storage/const.go 2015-09-02 10:42:28 +0000 |
4909 | @@ -0,0 +1,41 @@ |
4910 | +/* |
4911 | + * Copyright 2014 Canonical Ltd. |
4912 | + * |
4913 | + * Authors: |
4914 | + * Sergio Schvezov: sergio.schvezov@cannical.com |
4915 | + * |
4916 | + * This file is part of telepathy. |
4917 | + * |
4918 | + * mms is free software; you can redistribute it and/or modify |
4919 | + * it under the terms of the GNU General Public License as published by |
4920 | + * the Free Software Foundation; version 3. |
4921 | + * |
4922 | + * mms is distributed in the hope that it will be useful, |
4923 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
4924 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4925 | + * GNU General Public License for more details. |
4926 | + * |
4927 | + * You should have received a copy of the GNU General Public License |
4928 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
4929 | + */ |
4930 | + |
4931 | +package storage |
4932 | + |
4933 | +const ( |
4934 | + NONE = "none" |
4935 | + EXPIRED = "expired" |
4936 | + RETRIEVED = "retrieved" |
4937 | + REJECTED = "rejected" |
4938 | + DEFERRED = "deferred" |
4939 | + INDETERMINATE = "indeterminate" |
4940 | + FORWARDED = "forwarded" |
4941 | + UNREACHABLE = "unreachable" |
4942 | +) |
4943 | + |
4944 | +const ( |
4945 | + NOTIFICATION = "notification" |
4946 | + DOWNLOADED = "downloaded" |
4947 | + RECEIVED = "received" |
4948 | + DRAFT = "draft" |
4949 | + SENT = "sent" |
4950 | +) |
4951 | |
4952 | === added file 'storage/context.go' |
4953 | --- storage/context.go 1970-01-01 00:00:00 +0000 |
4954 | +++ storage/context.go 2015-09-02 10:42:28 +0000 |
4955 | @@ -0,0 +1,113 @@ |
4956 | +/* |
4957 | + * Copyright 2014 Canonical Ltd. |
4958 | + * |
4959 | + * Authors: |
4960 | + * Sergio Schvezov: sergio.schvezov@cannical.com |
4961 | + * |
4962 | + * This file is part of telepathy. |
4963 | + * |
4964 | + * mms is free software; you can redistribute it and/or modify |
4965 | + * it under the terms of the GNU General Public License as published by |
4966 | + * the Free Software Foundation; version 3. |
4967 | + * |
4968 | + * mms is distributed in the hope that it will be useful, |
4969 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
4970 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4971 | + * GNU General Public License for more details. |
4972 | + * |
4973 | + * You should have received a copy of the GNU General Public License |
4974 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
4975 | + */ |
4976 | + |
4977 | +package storage |
4978 | + |
4979 | +import ( |
4980 | + "bufio" |
4981 | + "encoding/json" |
4982 | + "errors" |
4983 | + "os" |
4984 | + "path/filepath" |
4985 | + "sync" |
4986 | + |
4987 | + "log" |
4988 | + |
4989 | + "launchpad.net/go-dbus/v1" |
4990 | + "launchpad.net/go-xdg/v0" |
4991 | +) |
4992 | + |
4993 | +var preferredContextPath string = filepath.Join(filepath.Base(os.Args[0]), "preferredContext") |
4994 | + |
4995 | +var contextMutex sync.Mutex |
4996 | + |
4997 | +type contextSettingMap map[string]dbus.ObjectPath |
4998 | + |
4999 | +func SetPreferredContext(identity string, pcObjectPath dbus.ObjectPath) error { |
5000 | + contextMutex.Lock() |
When system upstart goes away, :sys:started/ stopped/ starting/ stopping events will go away.
One option is to introduce a user session event bridge which will relay similar events from systemd. I have a working prototype for that, however incomplete information is exposed by systemd at the moment.
Another option is to key onto approximately equivalent events which are already available.
This is a master bug to track all users and port them.