nuntium-1.4+15.10.20150902/ 0000755 0000153 0000161 00000000000 12571563531 015415 5 ustar pbuser pbgroup 0000000 0000000 nuntium-1.4+15.10.20150902/.travis.yml 0000644 0000153 0000161 00000001000 12571563375 017523 0 ustar pbuser pbgroup 0000000 0000000 language: go
go:
- 1.2
- tip
before_install:
- go get github.com/axw/gocov/gocov
- go get github.com/mattn/goveralls
- if ! go get code.google.com/p/go.tools/cmd/cover; then go get golang.org/x/tools/cmd/cover; fi
script:
- COVERALLS="-repotoken $COVERALLS_TOKEN" ./scripts/testcoverage.sh
env:
- secure: "DygCBexI9tfMqcZoAQsuhhgCtCNRqkEWtigZoal1gVTvDHCFSBbgs3DfwkVgIXITV8Al8KejOWOFRTo2EmTluRuRSiQIFI0DL1cdU/PyxmZxLVK0tF3X8Yh7yTEMxpcl3jJf+8AVIHzUsspASWcV1qyp70JP0Kgjo81qkbIHPzg="
nuntium-1.4+15.10.20150902/storage/ 0000755 0000153 0000161 00000000000 12571563531 017061 5 ustar pbuser pbgroup 0000000 0000000 nuntium-1.4+15.10.20150902/storage/context.go 0000644 0000153 0000161 00000005121 12571563375 021101 0 ustar pbuser pbgroup 0000000 0000000 /*
* Copyright 2014 Canonical Ltd.
*
* Authors:
* Sergio Schvezov: sergio.schvezov@cannical.com
*
* This file is part of telepathy.
*
* mms is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* mms is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package storage
import (
"bufio"
"encoding/json"
"errors"
"os"
"path/filepath"
"sync"
"log"
"launchpad.net/go-dbus/v1"
"launchpad.net/go-xdg/v0"
)
var preferredContextPath string = filepath.Join(filepath.Base(os.Args[0]), "preferredContext")
var contextMutex sync.Mutex
type contextSettingMap map[string]dbus.ObjectPath
func SetPreferredContext(identity string, pcObjectPath dbus.ObjectPath) error {
contextMutex.Lock()
defer contextMutex.Unlock()
pcFilePath, err := xdg.Cache.Ensure(preferredContextPath)
if err != nil {
return err
}
return writeContext(identity, pcObjectPath, pcFilePath)
}
func GetPreferredContext(identity string) (pcObjectPath dbus.ObjectPath, err error) {
contextMutex.Lock()
defer contextMutex.Unlock()
pcFilePath, err := xdg.Cache.Find(preferredContextPath)
if err != nil {
return pcObjectPath, err
}
cs, err := readContext(pcFilePath)
if err != nil {
return pcObjectPath, err
}
if p, ok := cs[identity]; ok {
return p, nil
}
return pcObjectPath, errors.New("path for identity not found")
}
func readContext(storePath string) (cs contextSettingMap, err error) {
file, err := os.Open(storePath)
if err != nil {
cs = make(contextSettingMap)
return cs, err
}
jsonReader := json.NewDecoder(file)
if err = jsonReader.Decode(&cs); err != nil {
cs = make(contextSettingMap)
}
return cs, err
}
func writeContext(identity string, pc dbus.ObjectPath, storePath string) error {
cs, readErr := readContext(storePath)
if readErr != nil {
log.Println("Cannot read previous context state")
}
file, err := os.Create(storePath)
if err != nil {
log.Println(err)
return err
}
defer func() {
file.Close()
if err != nil {
os.Remove(storePath)
}
}()
w := bufio.NewWriter(file)
defer w.Flush()
cs[identity] = pc
jsonWriter := json.NewEncoder(w)
if err := jsonWriter.Encode(cs); err != nil {
log.Println(err)
return err
}
return nil
}
nuntium-1.4+15.10.20150902/storage/const.go 0000644 0000153 0000161 00000002127 12571563375 020546 0 ustar pbuser pbgroup 0000000 0000000 /*
* Copyright 2014 Canonical Ltd.
*
* Authors:
* Sergio Schvezov: sergio.schvezov@cannical.com
*
* This file is part of telepathy.
*
* mms is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* mms is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package storage
const (
NONE = "none"
EXPIRED = "expired"
RETRIEVED = "retrieved"
REJECTED = "rejected"
DEFERRED = "deferred"
INDETERMINATE = "indeterminate"
FORWARDED = "forwarded"
UNREACHABLE = "unreachable"
)
const (
NOTIFICATION = "notification"
DOWNLOADED = "downloaded"
RECEIVED = "received"
DRAFT = "draft"
SENT = "sent"
)
nuntium-1.4+15.10.20150902/storage/mmstate.go 0000644 0000153 0000161 00000003732 12571563375 021075 0 ustar pbuser pbgroup 0000000 0000000 /*
* Copyright 2014 Canonical Ltd.
*
* Authors:
* Sergio Schvezov: sergio.schvezov@cannical.com
*
* This file is part of telepathy.
*
* mms is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* mms is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package storage
//SendInfo is a map where every key is a destination and the value can be any of:
//
// - "none": no report has been received yet.
// - "expired": recipient did not retrieve the MMS before expiration.
// - "retrieved": MMS successfully retrieved by the recipient.
// - "rejected": recipient rejected the MMS.
// - "deferred": recipient decided to retrieve the MMS at a later time.
// - "indeterminate": cannot determine if the MMS reached its destination.
// - "forwarded": recipient forwarded the MMS without retrieving it first.
// - "unreachable": recipient is not reachable.
type SendInfo map[string]string
//Status represents an MMS' state
//
// Id represents the transacion ID for the MMS if using delivery request reports
//
// State can be:
// - "notification": m-Notify.Ind PDU not yet downloaded.
// - "downloaded": m-Retrieve.Conf PDU downloaded, but not yet acknowledged.
// - "received": m-Retrieve.Conf PDU downloaded and successfully acknowledged.
// - "draft": m-Send.Req PDU ready for sending.
// - "sent": m-Send.Req PDU successfully sent.
//
// SendState contains the sent state for each delivered message associated to
// a particular MMS
type MMSState struct {
Id string
State string
ContentLocation string
SendState SendInfo
}
nuntium-1.4+15.10.20150902/storage/storage.go 0000644 0000153 0000161 00000006254 12571563375 021071 0 ustar pbuser pbgroup 0000000 0000000 /*
* Copyright 2014 Canonical Ltd.
*
* Authors:
* Sergio Schvezov: sergio.schvezov@cannical.com
*
* This file is part of telepathy.
*
* mms is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* mms is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package storage
import (
"bufio"
"encoding/json"
"os"
"path"
"launchpad.net/go-xdg/v0"
)
const SUBPATH = "nuntium/store"
func Create(uuid, contentLocation string) error {
state := MMSState{
State: NOTIFICATION,
ContentLocation: contentLocation,
}
storePath, err := xdg.Data.Ensure(path.Join(SUBPATH, uuid+".db"))
if err != nil {
return err
}
return writeState(state, storePath)
}
func Destroy(uuid string) error {
if storePath, err := xdg.Data.Ensure(path.Join(SUBPATH, uuid+".db")); err == nil {
if err := os.Remove(storePath); err != nil {
return err
}
} else {
return err
}
if mmsPath, err := GetMMS(uuid); err == nil {
if err := os.Remove(mmsPath); err != nil {
return err
}
} else {
return err
}
return nil
}
func CreateResponseFile(uuid string) (*os.File, error) {
filePath, err := xdg.Cache.Ensure(path.Join(SUBPATH, uuid+".m-notifyresp.ind"))
if err != nil {
return nil, err
}
return os.Create(filePath)
}
func UpdateDownloaded(uuid, filePath string) error {
mmsPath, err := xdg.Data.Ensure(path.Join(SUBPATH, uuid+".mms"))
if err != nil {
return err
}
if err := os.Rename(filePath, mmsPath); err != nil {
//TODO delete file
return err
}
state := MMSState{
State: DOWNLOADED,
}
storePath, err := xdg.Data.Find(path.Join(SUBPATH, uuid+".db"))
if err != nil {
return err
}
return writeState(state, storePath)
}
func UpdateRetrieved(uuid string) error {
state := MMSState{
State: RETRIEVED,
}
storePath, err := xdg.Data.Find(path.Join(SUBPATH, uuid+".db"))
if err != nil {
return err
}
return writeState(state, storePath)
}
func CreateSendFile(uuid string) (*os.File, error) {
state := MMSState{
State: DRAFT,
}
storePath, err := xdg.Data.Ensure(path.Join(SUBPATH, uuid+".db"))
if err != nil {
return nil, err
}
if err := writeState(state, storePath); err != nil {
os.Remove(storePath)
return nil, err
}
filePath, err := xdg.Cache.Ensure(path.Join(SUBPATH, uuid+".m-send.req"))
if err != nil {
return nil, err
}
return os.Create(filePath)
}
func GetMMS(uuid string) (string, error) {
return xdg.Data.Find(path.Join(SUBPATH, uuid+".mms"))
}
func writeState(state MMSState, storePath string) error {
file, err := os.Create(storePath)
if err != nil {
return err
}
defer func() {
file.Close()
if err != nil {
os.Remove(storePath)
}
}()
w := bufio.NewWriter(file)
defer w.Flush()
jsonWriter := json.NewEncoder(w)
if err := jsonWriter.Encode(state); err != nil {
return err
}
return nil
}
nuntium-1.4+15.10.20150902/telepathy/ 0000755 0000153 0000161 00000000000 12571563531 017414 5 ustar pbuser pbgroup 0000000 0000000 nuntium-1.4+15.10.20150902/telepathy/const.go 0000644 0000153 0000161 00000003170 12571563375 021100 0 ustar pbuser pbgroup 0000000 0000000 /*
* Copyright 2014 Canonical Ltd.
*
* Authors:
* Sergio Schvezov: sergio.schvezov@cannical.com
*
* This file is part of telepathy.
*
* mms is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* mms is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package telepathy
const (
MMS_DBUS_NAME = "org.ofono.mms"
MMS_DBUS_PATH = "/org/ofono/mms"
MMS_MESSAGE_DBUS_IFACE = "org.ofono.mms.Message"
MMS_SERVICE_DBUS_IFACE = "org.ofono.mms.Service"
MMS_MANAGER_DBUS_IFACE = "org.ofono.mms.Manager"
)
const (
identityProperty string = "Identity"
useDeliveryReportsProperty string = "UseDeliveryReports"
modemObjectPathProperty string = "ModemObjectPath"
messageAddedSignal string = "MessageAdded"
messageRemovedSignal string = "MessageRemoved"
serviceAddedSignal string = "ServiceAdded"
serviceRemovedSignal string = "ServiceRemoved"
preferredContextProperty string = "PreferredContext"
propertyChangedSignal string = "PropertyChanged"
statusProperty string = "Status"
)
const (
PERMANENT_ERROR = "PermanentError"
SENT = "Sent"
TRANSIENT_ERROR = "TransientError"
)
const (
PLMN = "/TYPE=PLMN"
)
nuntium-1.4+15.10.20150902/telepathy/service.go 0000644 0000153 0000161 00000030546 12571563375 021421 0 ustar pbuser pbgroup 0000000 0000000 /*
* Copyright 2014 Canonical Ltd.
*
* Authors:
* Sergio Schvezov: sergio.schvezov@cannical.com
*
* This file is part of telepathy.
*
* mms is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* mms is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package telepathy
import (
"errors"
"fmt"
"log"
"path/filepath"
"reflect"
"strings"
"time"
"github.com/ubuntu-phonedations/nuntium/mms"
"github.com/ubuntu-phonedations/nuntium/storage"
"launchpad.net/go-dbus/v1"
)
//Payload is used to build the dbus messages; this is a workaround as v1 of go-dbus
//tries to encode and decode private fields.
type Payload struct {
Path dbus.ObjectPath
Properties map[string]dbus.Variant
}
type MMSService struct {
payload Payload
Properties map[string]dbus.Variant
conn *dbus.Connection
msgChan chan *dbus.Message
messageHandlers map[dbus.ObjectPath]*MessageInterface
msgDeleteChan chan dbus.ObjectPath
identity string
outMessage chan *OutgoingMessage
}
type Attachment struct {
Id string
MediaType string
FilePath string
Offset uint64
Length uint64
}
type OutAttachment struct {
Id string
ContentType string
FilePath string
}
type OutgoingMessage struct {
Recipients []string
Attachments []OutAttachment
Reply *dbus.Message
}
func NewMMSService(conn *dbus.Connection, modemObjPath dbus.ObjectPath, identity string, outgoingChannel chan *OutgoingMessage, useDeliveryReports bool) *MMSService {
properties := make(map[string]dbus.Variant)
properties[identityProperty] = dbus.Variant{identity}
serviceProperties := make(map[string]dbus.Variant)
serviceProperties[useDeliveryReportsProperty] = dbus.Variant{useDeliveryReports}
serviceProperties[modemObjectPathProperty] = dbus.Variant{modemObjPath}
payload := Payload{
Path: dbus.ObjectPath(MMS_DBUS_PATH + "/" + identity),
Properties: properties,
}
service := MMSService{
payload: payload,
Properties: serviceProperties,
conn: conn,
msgChan: make(chan *dbus.Message),
msgDeleteChan: make(chan dbus.ObjectPath),
messageHandlers: make(map[dbus.ObjectPath]*MessageInterface),
outMessage: outgoingChannel,
identity: identity,
}
go service.watchDBusMethodCalls()
go service.watchMessageDeleteCalls()
conn.RegisterObjectPath(payload.Path, service.msgChan)
return &service
}
func (service *MMSService) watchMessageDeleteCalls() {
for msgObjectPath := range service.msgDeleteChan {
if err := service.MessageRemoved(msgObjectPath); err != nil {
log.Print("Failed to delete ", msgObjectPath, ": ", err)
}
}
}
func (service *MMSService) watchDBusMethodCalls() {
for msg := range service.msgChan {
var reply *dbus.Message
if msg.Interface != MMS_SERVICE_DBUS_IFACE {
log.Println("Received unkown method call on", msg.Interface, msg.Member)
reply = dbus.NewErrorMessage(
msg,
"org.freedesktop.DBus.Error.UnknownInterface",
fmt.Sprintf("No such interface '%s' at object path '%s'", msg.Interface, msg.Path))
continue
}
switch msg.Member {
case "GetMessages":
reply = dbus.NewMethodReturnMessage(msg)
//TODO implement store and forward
var payload []Payload
if err := reply.AppendArgs(payload); err != nil {
log.Print("Cannot parse payload data from services")
reply = dbus.NewErrorMessage(msg, "Error.InvalidArguments", "Cannot parse services")
}
if err := service.conn.Send(reply); err != nil {
log.Println("Could not send reply:", err)
}
case "GetProperties":
reply = dbus.NewMethodReturnMessage(msg)
if pc, err := service.GetPreferredContext(); err == nil {
service.Properties[preferredContextProperty] = dbus.Variant{pc}
} else {
// Using "/" as an invalid 'path' even though it could be considered 'incorrect'
service.Properties[preferredContextProperty] = dbus.Variant{dbus.ObjectPath("/")}
}
if err := reply.AppendArgs(service.Properties); err != nil {
log.Print("Cannot parse payload data from services")
reply = dbus.NewErrorMessage(msg, "Error.InvalidArguments", "Cannot parse services")
}
if err := service.conn.Send(reply); err != nil {
log.Println("Could not send reply:", err)
}
case "SetProperty":
if err := service.setProperty(msg); err != nil {
log.Println("Property set failed:", err)
reply = dbus.NewErrorMessage(msg, "Error.InvalidArguments", err.Error())
} else {
reply = dbus.NewMethodReturnMessage(msg)
}
if err := service.conn.Send(reply); err != nil {
log.Println("Could not send reply:", err)
}
case "SendMessage":
var outMessage OutgoingMessage
outMessage.Reply = dbus.NewMethodReturnMessage(msg)
if err := msg.Args(&outMessage.Recipients, &outMessage.Attachments); err != nil {
log.Print("Cannot parse payload data from services")
reply = dbus.NewErrorMessage(msg, "Error.InvalidArguments", "Cannot parse New Message")
if err := service.conn.Send(reply); err != nil {
log.Println("Could not send reply:", err)
}
} else {
service.outMessage <- &outMessage
}
default:
log.Println("Received unkown method call on", msg.Interface, msg.Member)
reply = dbus.NewErrorMessage(
msg,
"org.freedesktop.DBus.Error.UnknownMethod",
fmt.Sprintf("No such method '%s' at object path '%s'", msg.Member, msg.Path))
if err := service.conn.Send(reply); err != nil {
log.Println("Could not send reply:", err)
}
}
}
}
func getUUIDFromObjectPath(objectPath dbus.ObjectPath) (string, error) {
str := string(objectPath)
defaultError := fmt.Errorf("%s is not a proper object path for a Message", str)
if str == "" {
return "", defaultError
}
uuid := filepath.Base(str)
if uuid == "" || uuid == ".." || uuid == "." {
return "", defaultError
}
return uuid, nil
}
func (service *MMSService) SetPreferredContext(context dbus.ObjectPath) error {
// make set a noop if we are setting the same thing
if pc, err := service.GetPreferredContext(); err != nil && context == pc {
return nil
}
if err := storage.SetPreferredContext(service.identity, context); err != nil {
return err
}
signal := dbus.NewSignalMessage(service.payload.Path, MMS_SERVICE_DBUS_IFACE, propertyChangedSignal)
if err := signal.AppendArgs(preferredContextProperty, dbus.Variant{context}); err != nil {
return err
}
return service.conn.Send(signal)
}
func (service *MMSService) GetPreferredContext() (dbus.ObjectPath, error) {
return storage.GetPreferredContext(service.identity)
}
func (service *MMSService) setProperty(msg *dbus.Message) error {
var propertyName string
var propertyValue dbus.Variant
if err := msg.Args(&propertyName, &propertyValue); err != nil {
return err
}
switch propertyName {
case preferredContextProperty:
preferredContextObjectPath := dbus.ObjectPath(reflect.ValueOf(propertyValue.Value).String())
service.Properties[preferredContextProperty] = dbus.Variant{preferredContextObjectPath}
return service.SetPreferredContext(preferredContextObjectPath)
default:
errors.New("property cannot be set")
}
return errors.New("unhandled property")
}
//MessageRemoved emits the MessageRemoved signal with the path of the removed
//message.
//It also actually removes the message from storage.
func (service *MMSService) MessageRemoved(objectPath dbus.ObjectPath) error {
service.messageHandlers[objectPath].Close()
delete(service.messageHandlers, objectPath)
uuid, err := getUUIDFromObjectPath(objectPath)
if err != nil {
return err
}
if err := storage.Destroy(uuid); err != nil {
return err
}
signal := dbus.NewSignalMessage(service.payload.Path, MMS_SERVICE_DBUS_IFACE, messageRemovedSignal)
if err := signal.AppendArgs(objectPath); err != nil {
return err
}
if err := service.conn.Send(signal); err != nil {
return err
}
return nil
}
//IncomingMessageAdded emits a MessageAdded with the path to the added message which
//is taken as a parameter and creates an object path on the message interface.
func (service *MMSService) IncomingMessageAdded(mRetConf *mms.MRetrieveConf) error {
payload, err := service.parseMessage(mRetConf)
if err != nil {
return err
}
service.messageHandlers[payload.Path] = NewMessageInterface(service.conn, payload.Path, service.msgDeleteChan)
return service.MessageAdded(&payload)
}
//MessageAdded emits a MessageAdded with the path to the added message which
//is taken as a parameter
func (service *MMSService) MessageAdded(msgPayload *Payload) error {
signal := dbus.NewSignalMessage(service.payload.Path, MMS_SERVICE_DBUS_IFACE, messageAddedSignal)
if err := signal.AppendArgs(msgPayload.Path, msgPayload.Properties); err != nil {
return err
}
return service.conn.Send(signal)
}
func (service *MMSService) isService(identity string) bool {
path := dbus.ObjectPath(MMS_DBUS_PATH + "/" + identity)
if path == service.payload.Path {
return true
}
return false
}
func (service *MMSService) Close() {
service.conn.UnregisterObjectPath(service.payload.Path)
close(service.msgChan)
close(service.msgDeleteChan)
}
func (service *MMSService) parseMessage(mRetConf *mms.MRetrieveConf) (Payload, error) {
params := make(map[string]dbus.Variant)
params["Status"] = dbus.Variant{"received"}
//TODO retrieve date correctly
date := parseDate(mRetConf.Date)
params["Date"] = dbus.Variant{date}
if mRetConf.Subject != "" {
params["Subject"] = dbus.Variant{mRetConf.Subject}
}
sender := mRetConf.From
if strings.HasSuffix(mRetConf.From, PLMN) {
params["Sender"] = dbus.Variant{sender[:len(sender)-len(PLMN)]}
}
params["Recipients"] = dbus.Variant{parseRecipients(strings.Join(mRetConf.To, ","))}
if smil, err := mRetConf.GetSmil(); err == nil {
params["Smil"] = dbus.Variant{smil}
}
var attachments []Attachment
dataParts := mRetConf.GetDataParts()
for i := range dataParts {
var filePath string
if f, err := storage.GetMMS(mRetConf.UUID); err == nil {
filePath = f
} else {
return Payload{}, err
}
attachment := Attachment{
Id: dataParts[i].ContentId,
MediaType: dataParts[i].MediaType,
FilePath: filePath,
Offset: uint64(dataParts[i].Offset),
Length: uint64(len(dataParts[i].Data)),
}
attachments = append(attachments, attachment)
}
params["Attachments"] = dbus.Variant{attachments}
payload := Payload{Path: service.genMessagePath(mRetConf.UUID), Properties: params}
return payload, nil
}
func parseDate(unixTime uint64) string {
const layout = "2014-03-30T18:15:30-0300"
date := time.Unix(int64(unixTime), 0)
return date.Format(time.RFC3339)
}
func parseRecipients(to string) []string {
recipients := strings.Split(to, ",")
for i := range recipients {
if strings.HasSuffix(recipients[i], PLMN) {
recipients[i] = recipients[i][:len(recipients[i])-len(PLMN)]
}
}
return recipients
}
func (service *MMSService) MessageDestroy(uuid string) error {
msgObjectPath := service.genMessagePath(uuid)
if msgInterface, ok := service.messageHandlers[msgObjectPath]; ok {
msgInterface.Close()
delete(service.messageHandlers, msgObjectPath)
}
return fmt.Errorf("no message interface handler for object path %s", msgObjectPath)
}
func (service *MMSService) MessageStatusChanged(uuid, status string) error {
msgObjectPath := service.genMessagePath(uuid)
if msgInterface, ok := service.messageHandlers[msgObjectPath]; ok {
return msgInterface.StatusChanged(status)
}
return fmt.Errorf("no message interface handler for object path %s", msgObjectPath)
}
func (service *MMSService) ReplySendMessage(reply *dbus.Message, uuid string) (dbus.ObjectPath, error) {
msgObjectPath := service.genMessagePath(uuid)
reply.AppendArgs(msgObjectPath)
if err := service.conn.Send(reply); err != nil {
return "", err
}
msg := NewMessageInterface(service.conn, msgObjectPath, service.msgDeleteChan)
service.messageHandlers[msgObjectPath] = msg
service.MessageAdded(msg.GetPayload())
return msgObjectPath, nil
}
//TODO randomly creating a uuid until the download manager does this for us
func (service *MMSService) genMessagePath(uuid string) dbus.ObjectPath {
return dbus.ObjectPath(MMS_DBUS_PATH + "/" + service.identity + "/" + uuid)
}
nuntium-1.4+15.10.20150902/telepathy/message.go 0000644 0000153 0000161 00000006761 12571563375 021407 0 ustar pbuser pbgroup 0000000 0000000 /*
* Copyright 2014 Canonical Ltd.
*
* Authors:
* Sergio Schvezov: sergio.schvezov@cannical.com
*
* This file is part of telepathy.
*
* mms is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* mms is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package telepathy
import (
"fmt"
"log"
"sort"
"launchpad.net/go-dbus/v1"
)
var validStatus sort.StringSlice
func init() {
validStatus = sort.StringSlice{SENT, PERMANENT_ERROR, TRANSIENT_ERROR}
sort.Strings(validStatus)
}
type MessageInterface struct {
conn *dbus.Connection
objectPath dbus.ObjectPath
msgChan chan *dbus.Message
deleteChan chan dbus.ObjectPath
status string
}
func NewMessageInterface(conn *dbus.Connection, objectPath dbus.ObjectPath, deleteChan chan dbus.ObjectPath) *MessageInterface {
msgInterface := MessageInterface{
conn: conn,
objectPath: objectPath,
deleteChan: deleteChan,
msgChan: make(chan *dbus.Message),
status: "draft",
}
go msgInterface.watchDBusMethodCalls()
conn.RegisterObjectPath(msgInterface.objectPath, msgInterface.msgChan)
return &msgInterface
}
func (msgInterface *MessageInterface) Close() {
close(msgInterface.msgChan)
msgInterface.msgChan = nil
msgInterface.conn.UnregisterObjectPath(msgInterface.objectPath)
}
func (msgInterface *MessageInterface) watchDBusMethodCalls() {
var reply *dbus.Message
for msg := range msgInterface.msgChan {
if msg.Interface != MMS_MESSAGE_DBUS_IFACE {
log.Println("Received unkown method call on", msg.Interface, msg.Member)
reply = dbus.NewErrorMessage(msg, "org.freedesktop.DBus.Error.UnknownMethod", "Unknown method")
continue
}
switch msg.Member {
case "Delete":
reply = dbus.NewMethodReturnMessage(msg)
//TODO implement store and forward
if err := msgInterface.conn.Send(reply); err != nil {
log.Println("Could not send reply:", err)
}
msgInterface.deleteChan <- msgInterface.objectPath
default:
log.Println("Received unkown method call on", msg.Interface, msg.Member)
reply = dbus.NewErrorMessage(msg, "org.freedesktop.DBus.Error.UnknownMethod", "Unknown method")
if err := msgInterface.conn.Send(reply); err != nil {
log.Println("Could not send reply:", err)
}
}
}
}
func (msgInterface *MessageInterface) StatusChanged(status string) error {
i := validStatus.Search(status)
if i < validStatus.Len() && validStatus[i] == status {
msgInterface.status = status
signal := dbus.NewSignalMessage(msgInterface.objectPath, MMS_MESSAGE_DBUS_IFACE, propertyChangedSignal)
if err := signal.AppendArgs(statusProperty, dbus.Variant{status}); err != nil {
return err
}
if err := msgInterface.conn.Send(signal); err != nil {
return err
}
log.Print("Status changed for ", msgInterface.objectPath, " to ", status)
return nil
}
return fmt.Errorf("status %s is not a valid status", status)
}
func (msgInterface *MessageInterface) GetPayload() *Payload {
properties := make(map[string]dbus.Variant)
properties["Status"] = dbus.Variant{msgInterface.status}
return &Payload{
Path: msgInterface.objectPath,
Properties: properties,
}
}
nuntium-1.4+15.10.20150902/telepathy/manager.go 0000644 0000153 0000161 00000010302 12571563375 021357 0 ustar pbuser pbgroup 0000000 0000000 /*
* Copyright 2014 Canonical Ltd.
*
* Authors:
* Sergio Schvezov: sergio.schvezov@cannical.com
*
* This file is part of telepathy.
*
* mms is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* mms is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package telepathy
import (
"fmt"
"log"
"launchpad.net/go-dbus/v1"
)
type MMSManager struct {
conn *dbus.Connection
msgChan chan *dbus.Message
services []*MMSService
}
func NewMMSManager(conn *dbus.Connection) (*MMSManager, error) {
name := conn.RequestName(MMS_DBUS_NAME, dbus.NameFlagDoNotQueue)
err := <-name.C
if err != nil {
return nil, fmt.Errorf("Could not aquire name %s", MMS_DBUS_NAME)
}
log.Printf("Registered %s on bus as %s", conn.UniqueName, name.Name)
manager := MMSManager{conn: conn, msgChan: make(chan *dbus.Message)}
go manager.watchDBusMethodCalls()
conn.RegisterObjectPath(MMS_DBUS_PATH, manager.msgChan)
return &manager, nil
}
func (manager *MMSManager) watchDBusMethodCalls() {
var reply *dbus.Message
for msg := range manager.msgChan {
switch {
case msg.Interface == MMS_MANAGER_DBUS_IFACE && msg.Member == "GetServices":
log.Print("Received GetServices()")
reply = manager.getServices(msg)
default:
log.Println("Received unkown method call on", msg.Interface, msg.Member)
reply = dbus.NewErrorMessage(msg, "org.freedesktop.DBus.Error.UnknownMethod", "Unknown method")
}
if err := manager.conn.Send(reply); err != nil {
log.Print("Could not send reply: ", err)
}
}
}
func (manager *MMSManager) getServices(msg *dbus.Message) *dbus.Message {
var payloads []Payload
for i, _ := range manager.services {
payloads = append(payloads, manager.services[i].payload)
}
reply := dbus.NewMethodReturnMessage(msg)
if err := reply.AppendArgs(payloads); err != nil {
log.Print("Cannot parse payload data from services")
return dbus.NewErrorMessage(msg, "Error.InvalidArguments", "Cannot parse services")
}
return reply
}
func (manager *MMSManager) serviceAdded(payload *Payload) error {
log.Print("Service added ", payload.Path)
signal := dbus.NewSignalMessage(MMS_DBUS_PATH, MMS_MANAGER_DBUS_IFACE, serviceAddedSignal)
if err := signal.AppendArgs(payload.Path, payload.Properties); err != nil {
return err
}
if err := manager.conn.Send(signal); err != nil {
return fmt.Errorf("Cannot send ServiceAdded for %s", payload.Path)
}
return nil
}
func (manager *MMSManager) AddService(identity string, modemObjPath dbus.ObjectPath, outgoingChannel chan *OutgoingMessage, useDeliveryReports bool) (*MMSService, error) {
for i := range manager.services {
if manager.services[i].isService(identity) {
return manager.services[i], nil
}
}
service := NewMMSService(manager.conn, modemObjPath, identity, outgoingChannel, useDeliveryReports)
if err := manager.serviceAdded(&service.payload); err != nil {
return &MMSService{}, err
}
manager.services = append(manager.services, service)
return service, nil
}
func (manager *MMSManager) serviceRemoved(payload *Payload) error {
log.Print("Service removed ", payload.Path)
signal := dbus.NewSignalMessage(MMS_DBUS_PATH, MMS_MANAGER_DBUS_IFACE, serviceRemovedSignal)
if err := signal.AppendArgs(payload.Path); err != nil {
return err
}
if err := manager.conn.Send(signal); err != nil {
return fmt.Errorf("Cannot send ServiceRemoved for %s", payload.Path)
}
return nil
}
func (manager *MMSManager) RemoveService(identity string) error {
for i := range manager.services {
if manager.services[i].isService(identity) {
manager.serviceRemoved(&manager.services[i].payload)
manager.services[i].Close()
manager.services = append(manager.services[:i], manager.services[i+1:]...)
log.Print("Service left: ", len(manager.services))
return nil
}
}
return fmt.Errorf("Cannot find service serving %s", identity)
}
nuntium-1.4+15.10.20150902/docs/ 0000755 0000153 0000161 00000000000 12571563531 016345 5 ustar pbuser pbgroup 0000000 0000000 nuntium-1.4+15.10.20150902/docs/architecture.md 0000644 0000153 0000161 00000001405 12571563375 021357 0 ustar pbuser pbgroup 0000000 0000000 # Architecture
## Overview
`nuntium` lies in between `ofono` over the system bus and `telepathy-ofono`
over the session bus.
`nuntium` registers an agent against `ofono`'s `push notification` plugin and
sets up to receive DBus method calls from `ofono` when push messages arrive.
And it creates an instance on the session to handle method calls from
`telepathy-ofono` to send messages and signal message and service events.
### Receiving an MMS
This is a simplified scenario for an incoming message with deferral's set to
false:

### Sending an MMS
This is a simplified scenario for sending a message with message delivery set
to false:

nuntium-1.4+15.10.20150902/docs/testing.md 0000644 0000153 0000161 00000007216 12571563375 020360 0 ustar pbuser pbgroup 0000000 0000000 # Testing nuntium
## Tools
These are generic debugging tools provided as part of nuntium or described as
external aids that can be used for troubleshooting. These do not stub
components, so are safe for operator testing.
### nuntium-decode-cli
This tool allows you to easily decode an *M-Retrieve.conf* displaying the
output of what is decoded to stdout with an option to drop the decoded
artifacts into a specific path.
Install it by running:
go get github.com/ubuntu-phonedations/nuntium/cmd/nuntium-decode-cli
Refer to the tool's [documentation](http://godoc.org/github.com/ubuntu-phonedations/nuntium/cmd/nuntium-decode-cli)
for more information.
### nuntium-preferred-context
*Needs implementation*
This tool allows reading or writing the preferred context `nuntium` will use
when trying to activate a context.
Install it by running:
go get github.com/ubuntu-phonedations/nuntium/cmd/nuntium-preferred-context
Refer to the tool's
[documentation](http://godoc.org/github.com/ubuntu-phonedations/nuntium/cmd/nuntium-preferred-context)
for more information.
### tcpdump
When doing operator testing and MMS debugging is needed, tcpdump can provide
the right tools to debug the issue.
To get it going, on the device, do:
sudo mount -o remount,rw /
sudo apt install tcpdump
sudo tcpdump -w [file]
The capture `[file]` can be analyzed to better understand the problem.
### network-test-session
Additionally to `tcpdump`, a useful tool to pinpoint problems is
`network-test-session`, refer to the [documentation](https://github.com/sergiusens/network-test-session/blob/master/README.md)
for this tool on how to use it.
## Testing tools
This section describes a list of external tools to tests and a short summary
of what can be done with them. Some of these tools remove the operator from
the picture, so in the case of doing operator testing, these really should be
avoided.
These tools don't yet mock or stub `ofono`'s APN contexts logic but override
the logic in code completely if needed.
### nuntium-inject-push
This tool is meant to inject a push notification message through the
`ReceiveNotification` method nuntium exposes on the system bus and
simulate a black box workflow as described in
[Receiving an MMS](architecture.md#receiving-an-mms).
Install it by running:
go get github.com/ubuntu-phonedations/nuntium/cmd/nuntium-inject-push
This tool does not mock out ofono completely, but instead what it creates a
local server to serve an mms that would be passed on from the Push
Notification with a Content Location such that this local server would be
used to fetch the MMS.
If no MMS is specified a in tool *m-retrieve.conf* would be used. Once the
content is served once, the tool exits.
The agent is registered on the system bus, as such, it should be run like:
sudo nuntium-inject-push --end-point :1.356
where `:1.356` is the dbus name that nuntium has on the system bus, this can
be discovered by looking at the nuntium logs.
To use a specifc mms, just use the cli switch like:
sudo nuntium-inject-push --end-point :1.356
### nuntium-stub-send
*Needs implementation*
This tool verifies and simulates sending an MMS simulating what would happen
when [Sending an MMS](architecture.md#sending-an-mms)
It will track that the correct signals are raised over the bus.
Install it by running:
go get github.com/ubuntu-phonedations/nuntium/cmd/nuntium-stub-send
Refer to the tool's
[documentation](http://godoc.org/github.com/ubuntu-phonedations/nuntium/cmd/nuntium-stub-send)
for more information.
## Installing on Ubuntu
On Ubuntu (vivid+), all of the `nuntium-*` tools can be installed by
sudo apt install nuntium-tools
nuntium-1.4+15.10.20150902/docs/assets/ 0000755 0000153 0000161 00000000000 12571563531 017647 5 ustar pbuser pbgroup 0000000 0000000 nuntium-1.4+15.10.20150902/docs/assets/send_success_delivery_disabled.png 0000644 0000153 0000161 00000062305 12571563375 026604 0 ustar pbuser pbgroup 0000000 0000000 PNG
IHDR F s\ pHYs + IDATxkPTW@4H%jc$A
AB8eD0LbX$b&8 rӡDH8F 1#jZ 4ih~^zn{˴Z kb3 Fa ա0
P `u( :F X
Bprr@ ?h4i[&l<