| package dbus |
| |
| import ( |
| "bytes" |
| "encoding/binary" |
| "errors" |
| "io" |
| "reflect" |
| "strconv" |
| ) |
| |
| const protoVersion byte = 1 |
| |
| // Flags represents the possible flags of a D-Bus message. |
| type Flags byte |
| |
| const ( |
| // FlagNoReplyExpected signals that the message is not expected to generate |
| // a reply. If this flag is set on outgoing messages, any possible reply |
| // will be discarded. |
| FlagNoReplyExpected Flags = 1 << iota |
| // FlagNoAutoStart signals that the message bus should not automatically |
| // start an application when handling this message. |
| FlagNoAutoStart |
| // FlagAllowInteractiveAuthorization may be set on a method call |
| // message to inform the receiving side that the caller is prepared |
| // to wait for interactive authorization, which might take a |
| // considerable time to complete. For instance, if this flag is set, |
| // it would be appropriate to query the user for passwords or |
| // confirmation via Polkit or a similar framework. |
| FlagAllowInteractiveAuthorization |
| ) |
| |
| // Type represents the possible types of a D-Bus message. |
| type Type byte |
| |
| const ( |
| TypeMethodCall Type = 1 + iota |
| TypeMethodReply |
| TypeError |
| TypeSignal |
| typeMax |
| ) |
| |
| func (t Type) String() string { |
| switch t { |
| case TypeMethodCall: |
| return "method call" |
| case TypeMethodReply: |
| return "reply" |
| case TypeError: |
| return "error" |
| case TypeSignal: |
| return "signal" |
| } |
| return "invalid" |
| } |
| |
| // HeaderField represents the possible byte codes for the headers |
| // of a D-Bus message. |
| type HeaderField byte |
| |
| const ( |
| FieldPath HeaderField = 1 + iota |
| FieldInterface |
| FieldMember |
| FieldErrorName |
| FieldReplySerial |
| FieldDestination |
| FieldSender |
| FieldSignature |
| FieldUnixFDs |
| fieldMax |
| ) |
| |
| // An InvalidMessageError describes the reason why a D-Bus message is regarded as |
| // invalid. |
| type InvalidMessageError string |
| |
| func (e InvalidMessageError) Error() string { |
| return "dbus: invalid message: " + string(e) |
| } |
| |
| // fieldType are the types of the various header fields. |
| var fieldTypes = [fieldMax]reflect.Type{ |
| FieldPath: objectPathType, |
| FieldInterface: stringType, |
| FieldMember: stringType, |
| FieldErrorName: stringType, |
| FieldReplySerial: uint32Type, |
| FieldDestination: stringType, |
| FieldSender: stringType, |
| FieldSignature: signatureType, |
| FieldUnixFDs: uint32Type, |
| } |
| |
| // requiredFields lists the header fields that are required by the different |
| // message types. |
| var requiredFields = [typeMax][]HeaderField{ |
| TypeMethodCall: {FieldPath, FieldMember}, |
| TypeMethodReply: {FieldReplySerial}, |
| TypeError: {FieldErrorName, FieldReplySerial}, |
| TypeSignal: {FieldPath, FieldInterface, FieldMember}, |
| } |
| |
| // Message represents a single D-Bus message. |
| type Message struct { |
| Type |
| Flags |
| Headers map[HeaderField]Variant |
| Body []interface{} |
| |
| serial uint32 |
| } |
| |
| type header struct { |
| Field byte |
| Variant |
| } |
| |
| // DecodeMessage tries to decode a single message in the D-Bus wire format |
| // from the given reader. The byte order is figured out from the first byte. |
| // The possibly returned error can be an error of the underlying reader, an |
| // InvalidMessageError or a FormatError. |
| func DecodeMessage(rd io.Reader) (msg *Message, err error) { |
| var order binary.ByteOrder |
| var hlength, length uint32 |
| var typ, flags, proto byte |
| var headers []header |
| |
| b := make([]byte, 1) |
| _, err = rd.Read(b) |
| if err != nil { |
| return |
| } |
| switch b[0] { |
| case 'l': |
| order = binary.LittleEndian |
| case 'B': |
| order = binary.BigEndian |
| default: |
| return nil, InvalidMessageError("invalid byte order") |
| } |
| |
| dec := newDecoder(rd, order) |
| dec.pos = 1 |
| |
| msg = new(Message) |
| vs, err := dec.Decode(Signature{"yyyuu"}) |
| if err != nil { |
| return nil, err |
| } |
| if err = Store(vs, &typ, &flags, &proto, &length, &msg.serial); err != nil { |
| return nil, err |
| } |
| msg.Type = Type(typ) |
| msg.Flags = Flags(flags) |
| |
| // get the header length separately because we need it later |
| b = make([]byte, 4) |
| _, err = io.ReadFull(rd, b) |
| if err != nil { |
| return nil, err |
| } |
| binary.Read(bytes.NewBuffer(b), order, &hlength) |
| if hlength+length+16 > 1<<27 { |
| return nil, InvalidMessageError("message is too long") |
| } |
| dec = newDecoder(io.MultiReader(bytes.NewBuffer(b), rd), order) |
| dec.pos = 12 |
| vs, err = dec.Decode(Signature{"a(yv)"}) |
| if err != nil { |
| return nil, err |
| } |
| if err = Store(vs, &headers); err != nil { |
| return nil, err |
| } |
| |
| msg.Headers = make(map[HeaderField]Variant) |
| for _, v := range headers { |
| msg.Headers[HeaderField(v.Field)] = v.Variant |
| } |
| |
| dec.align(8) |
| body := make([]byte, int(length)) |
| if length != 0 { |
| _, err := io.ReadFull(rd, body) |
| if err != nil { |
| return nil, err |
| } |
| } |
| |
| if err = msg.IsValid(); err != nil { |
| return nil, err |
| } |
| sig, _ := msg.Headers[FieldSignature].value.(Signature) |
| if sig.str != "" { |
| buf := bytes.NewBuffer(body) |
| dec = newDecoder(buf, order) |
| vs, err := dec.Decode(sig) |
| if err != nil { |
| return nil, err |
| } |
| msg.Body = vs |
| } |
| |
| return |
| } |
| |
| // EncodeTo encodes and sends a message to the given writer. The byte order must |
| // be either binary.LittleEndian or binary.BigEndian. If the message is not |
| // valid or an error occurs when writing, an error is returned. |
| func (msg *Message) EncodeTo(out io.Writer, order binary.ByteOrder) error { |
| if err := msg.IsValid(); err != nil { |
| return err |
| } |
| var vs [7]interface{} |
| switch order { |
| case binary.LittleEndian: |
| vs[0] = byte('l') |
| case binary.BigEndian: |
| vs[0] = byte('B') |
| default: |
| return errors.New("dbus: invalid byte order") |
| } |
| body := new(bytes.Buffer) |
| enc := newEncoder(body, order) |
| if len(msg.Body) != 0 { |
| enc.Encode(msg.Body...) |
| } |
| vs[1] = msg.Type |
| vs[2] = msg.Flags |
| vs[3] = protoVersion |
| vs[4] = uint32(len(body.Bytes())) |
| vs[5] = msg.serial |
| headers := make([]header, 0, len(msg.Headers)) |
| for k, v := range msg.Headers { |
| headers = append(headers, header{byte(k), v}) |
| } |
| vs[6] = headers |
| var buf bytes.Buffer |
| enc = newEncoder(&buf, order) |
| enc.Encode(vs[:]...) |
| enc.align(8) |
| body.WriteTo(&buf) |
| if buf.Len() > 1<<27 { |
| return InvalidMessageError("message is too long") |
| } |
| if _, err := buf.WriteTo(out); err != nil { |
| return err |
| } |
| return nil |
| } |
| |
| // IsValid checks whether msg is a valid message and returns an |
| // InvalidMessageError if it is not. |
| func (msg *Message) IsValid() error { |
| if msg.Flags & ^(FlagNoAutoStart|FlagNoReplyExpected|FlagAllowInteractiveAuthorization) != 0 { |
| return InvalidMessageError("invalid flags") |
| } |
| if msg.Type == 0 || msg.Type >= typeMax { |
| return InvalidMessageError("invalid message type") |
| } |
| for k, v := range msg.Headers { |
| if k == 0 || k >= fieldMax { |
| return InvalidMessageError("invalid header") |
| } |
| if reflect.TypeOf(v.value) != fieldTypes[k] { |
| return InvalidMessageError("invalid type of header field") |
| } |
| } |
| for _, v := range requiredFields[msg.Type] { |
| if _, ok := msg.Headers[v]; !ok { |
| return InvalidMessageError("missing required header") |
| } |
| } |
| if path, ok := msg.Headers[FieldPath]; ok { |
| if !path.value.(ObjectPath).IsValid() { |
| return InvalidMessageError("invalid path name") |
| } |
| } |
| if iface, ok := msg.Headers[FieldInterface]; ok { |
| if !isValidInterface(iface.value.(string)) { |
| return InvalidMessageError("invalid interface name") |
| } |
| } |
| if member, ok := msg.Headers[FieldMember]; ok { |
| if !isValidMember(member.value.(string)) { |
| return InvalidMessageError("invalid member name") |
| } |
| } |
| if errname, ok := msg.Headers[FieldErrorName]; ok { |
| if !isValidInterface(errname.value.(string)) { |
| return InvalidMessageError("invalid error name") |
| } |
| } |
| if len(msg.Body) != 0 { |
| if _, ok := msg.Headers[FieldSignature]; !ok { |
| return InvalidMessageError("missing signature") |
| } |
| } |
| return nil |
| } |
| |
| // Serial returns the message's serial number. The returned value is only valid |
| // for messages received by eavesdropping. |
| func (msg *Message) Serial() uint32 { |
| return msg.serial |
| } |
| |
| // String returns a string representation of a message similar to the format of |
| // dbus-monitor. |
| func (msg *Message) String() string { |
| if err := msg.IsValid(); err != nil { |
| return "<invalid>" |
| } |
| s := msg.Type.String() |
| if v, ok := msg.Headers[FieldSender]; ok { |
| s += " from " + v.value.(string) |
| } |
| if v, ok := msg.Headers[FieldDestination]; ok { |
| s += " to " + v.value.(string) |
| } |
| s += " serial " + strconv.FormatUint(uint64(msg.serial), 10) |
| if v, ok := msg.Headers[FieldReplySerial]; ok { |
| s += " reply_serial " + strconv.FormatUint(uint64(v.value.(uint32)), 10) |
| } |
| if v, ok := msg.Headers[FieldUnixFDs]; ok { |
| s += " unixfds " + strconv.FormatUint(uint64(v.value.(uint32)), 10) |
| } |
| if v, ok := msg.Headers[FieldPath]; ok { |
| s += " path " + string(v.value.(ObjectPath)) |
| } |
| if v, ok := msg.Headers[FieldInterface]; ok { |
| s += " interface " + v.value.(string) |
| } |
| if v, ok := msg.Headers[FieldErrorName]; ok { |
| s += " error " + v.value.(string) |
| } |
| if v, ok := msg.Headers[FieldMember]; ok { |
| s += " member " + v.value.(string) |
| } |
| if len(msg.Body) != 0 { |
| s += "\n" |
| } |
| for i, v := range msg.Body { |
| s += " " + MakeVariant(v).String() |
| if i != len(msg.Body)-1 { |
| s += "\n" |
| } |
| } |
| return s |
| } |