1// Copyright 2013 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package packet
6
7import (
8	"bytes"
9	"image"
10	"image/jpeg"
11	"io"
12	"io/ioutil"
13)
14
15const UserAttrImageSubpacket = 1
16
17// UserAttribute is capable of storing other types of data about a user
18// beyond name, email and a text comment. In practice, user attributes are typically used
19// to store a signed thumbnail photo JPEG image of the user.
20// See RFC 4880, section 5.12.
21type UserAttribute struct {
22	Contents []*OpaqueSubpacket
23}
24
25// NewUserAttributePhoto creates a user attribute packet
26// containing the given images.
27func NewUserAttributePhoto(photos ...image.Image) (uat *UserAttribute, err error) {
28	uat = new(UserAttribute)
29	for _, photo := range photos {
30		var buf bytes.Buffer
31		// RFC 4880, Section 5.12.1.
32		data := []byte{
33			0x10, 0x00, // Little-endian image header length (16 bytes)
34			0x01,       // Image header version 1
35			0x01,       // JPEG
36			0, 0, 0, 0, // 12 reserved octets, must be all zero.
37			0, 0, 0, 0,
38			0, 0, 0, 0}
39		if _, err = buf.Write(data); err != nil {
40			return
41		}
42		if err = jpeg.Encode(&buf, photo, nil); err != nil {
43			return
44		}
45		uat.Contents = append(uat.Contents, &OpaqueSubpacket{
46			SubType:  UserAttrImageSubpacket,
47			Contents: buf.Bytes()})
48	}
49	return
50}
51
52// NewUserAttribute creates a new user attribute packet containing the given subpackets.
53func NewUserAttribute(contents ...*OpaqueSubpacket) *UserAttribute {
54	return &UserAttribute{Contents: contents}
55}
56
57func (uat *UserAttribute) parse(r io.Reader) (err error) {
58	// RFC 4880, section 5.13
59	b, err := ioutil.ReadAll(r)
60	if err != nil {
61		return
62	}
63	uat.Contents, err = OpaqueSubpackets(b)
64	return
65}
66
67// Serialize marshals the user attribute to w in the form of an OpenPGP packet, including
68// header.
69func (uat *UserAttribute) Serialize(w io.Writer) (err error) {
70	var buf bytes.Buffer
71	for _, sp := range uat.Contents {
72		sp.Serialize(&buf)
73	}
74	if err = serializeHeader(w, packetTypeUserAttribute, buf.Len()); err != nil {
75		return err
76	}
77	_, err = w.Write(buf.Bytes())
78	return
79}
80
81// ImageData returns zero or more byte slices, each containing
82// JPEG File Interchange Format (JFIF), for each photo in the
83// user attribute packet.
84func (uat *UserAttribute) ImageData() (imageData [][]byte) {
85	for _, sp := range uat.Contents {
86		if sp.SubType == UserAttrImageSubpacket && len(sp.Contents) > 16 {
87			imageData = append(imageData, sp.Contents[16:])
88		}
89	}
90	return
91}
92