Merge lp:~rsalveti/nuntium/using_golang_go_instead into lp:nuntium

Proposed by Ricardo Salveti
Status: Superseded
Proposed branch: lp:~rsalveti/nuntium/using_golang_go_instead
Merge into: lp:nuntium
Diff against target: 6845 lines (+6526/-0) (has conflicts)
58 files modified
.bzr-builddeb/default.conf (+2/-0)
.gitignore (+1/-0)
.travis.yml (+16/-0)
README.md (+17/-0)
cmd/nuntium-decode-cli/decode.go (+70/-0)
cmd/nuntium-inject-push/main.go (+45/-0)
cmd/nuntium-inject-push/payload.go (+219/-0)
cmd/nuntium-inject-push/push.go (+50/-0)
cmd/nuntium-inject-push/server.go (+47/-0)
cmd/nuntium/loop.go (+76/-0)
cmd/nuntium/main.go (+84/-0)
cmd/nuntium/mediator.go (+412/-0)
debian/changelog (+165/-0)
debian/compat (+1/-0)
debian/control (+68/-0)
debian/copyright (+22/-0)
debian/docs (+2/-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-tools.install (+2/-0)
debian/nuntium.conf (+6/-0)
debian/nuntium.install (+2/-0)
debian/rules (+36/-0)
docs/architecture.md (+28/-0)
docs/assets/generate_msc.sh (+14/-0)
docs/assets/receiving_success_deferral_disabled.msc (+19/-0)
docs/assets/send_success_delivery_disabled.msc (+17/-0)
docs/testing.md (+124/-0)
mms/attachments.go (+295/-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 (+453/-0)
mms/encoder_test.go (+141/-0)
mms/mms.go (+442/-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 (+427/-0)
ofono/push.go (+112/-0)
ofono/push_decode_test.go (+171/-0)
ofono/pushagent.go (+153/-0)
ofono/wsp_params.go (+150/-0)
scripts/testcoverage.sh (+34/-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:~rsalveti/nuntium/using_golang_go_instead
Reviewer Review Type Date Requested Status
Ubuntu Phablet Team Pending
Review via email: mp+257013@code.launchpad.net

This proposal has been superseded by a proposal from 2015-04-21.

Commit message

control: building with golang-go instead (only package currently using gccgo)

Description of the change

control: building with golang-go instead (only package currently using gccgo)

To post a comment you must log in.
92. By Ricardo Salveti

Merging trunk

Unmerged revisions

92. By Ricardo Salveti

Merging trunk

91. By Ricardo Salveti

control: building with golang-go instead (only package currently using gccgo)

90. By Sergio Schvezov

Release 1.4

[Manuel de la Pena]
Adding support for multiple recipients. (LP: #1398440)

89. By Sergio Schvezov

Adding support for multiple recipients

88. By Sergio Schvezov

Release commit

87. By Sergio Schvezov

Upstream packaging

86. By Ricardo Salveti

Merge pull request #13 from ubuntu-phonedations/nuntium-inject-push

Adding nuntium-inject-push

85. By sergiusens <email address hidden>

Merge pull request #11 from ubuntu-phonedations/testdoc

Adding testing document.

84. By sergiusens <email address hidden>

Merge pull request #10 from ubuntu-phonedations/archdoc

Adding README.md and architecture document

83. By Sergio Schvezov

Relocating artifacts

Signed-off-by: Sergio Schvezov <email address hidden>

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

Subscribers

People subscribed via source and target branches