Merge lp:~xnox/nuntium/drop-sys-events into lp:nuntium/packaging

Proposed by Alfonso Sanchez-Beato
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
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.

To post a comment you must log in.
Revision history for this message
Dimitri John Ledkov (xnox) wrote : Posted in a previous version of this proposal

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.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
Alfonso Sanchez-Beato (alfonsosanchezbeato) wrote : Posted in a previous version of this proposal
Revision history for this message
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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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'
3326Binary 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()
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches