-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathenex.go
More file actions
126 lines (111 loc) · 3.2 KB
/
enex.go
File metadata and controls
126 lines (111 loc) · 3.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package enex
import (
"bytes"
"encoding/xml"
"errors"
"fmt"
"io"
"regexp"
)
type (
// Export represents Evernote enex file structure
Export struct {
XMLName xml.Name `xml:"en-export"`
Date string `xml:"export-date,attr"`
Notes []Note `xml:"note"`
}
// Note is one note in Evernote
Note struct {
XMLName xml.Name `xml:"note"`
Title string `xml:"title"`
Content []byte `xml:"content"`
Updated string `xml:"updated"`
Created string `xml:"created"`
Tags []string `xml:"tag"`
Attributes NoteAttributes `xml:"note-attributes"`
Resources []Resource `xml:"resource"`
}
// NoteAttributes contain the note metadata
NoteAttributes struct {
Source string `xml:"source"`
SourceApplication string `xml:"source-application"`
Latitude string `xml:"latitude"`
Longitude string `xml:"longitude"`
Altitude string `xml:"altitude"`
Author string `xml:"author"`
SourceUrl string `xml:"source-url"`
}
// Resource embedded in the note
Resource struct {
ID string
Type string
Data Data `xml:"data"`
Mime string `xml:"mime"`
Width int `xml:"width"`
Height int `xml:"height"`
Attributes Attributes `xml:"resource-attributes"`
Recognition []byte `xml:"recognition"`
}
// Attributes of the resource
Attributes struct {
Timestamp string `xml:"timestamp"`
Filename string `xml:"file-name"`
SourceUrl string `xml:"source-url"`
}
// Recognition for the resource
Recognition struct {
XMLName xml.Name `xml:"recoIndex"`
ObjID string `xml:"objID,attr"`
ObjType string `xml:"objType,attr"`
}
// Data object in base64
Data struct {
XMLName xml.Name `xml:"data"`
Encoding string `xml:"encoding,attr"`
Content []byte `xml:",innerxml"`
}
// Content of Evernote Notes
Content struct {
Text []byte `xml:",innerxml"`
}
)
var hashRe = regexp.MustCompile(`\b[0-9a-f]{32}\b`)
// Decode will return an Export from evernote
func Decode(data io.Reader) (*Export, error) {
var e Export
err := newDecoder(data).Decode(&e)
for i := range e.Notes {
var c Content
var reader = bytes.NewReader(e.Notes[i].Content)
if err := newDecoder(reader).Decode(&c); err != nil {
// EOF is a known case when the content is empty
if !errors.Is(err, io.EOF) {
return nil, fmt.Errorf("decoding note %s: %w", e.Notes[i].Title, err)
}
}
e.Notes[i].Content = c.Text
for j := range e.Notes[i].Resources {
if res := e.Notes[i].Resources[j]; len(res.Recognition) == 0 {
hash := hashRe.FindString(res.Attributes.SourceUrl)
if len(hash) > 0 {
e.Notes[i].Resources[j].ID = hash
}
continue
}
var rec Recognition
decoder := newDecoder(bytes.NewReader(e.Notes[i].Resources[j].Recognition))
err = decoder.Decode(&rec)
if err != nil {
return nil, fmt.Errorf("decoding resource %s: %w", e.Notes[i].Resources[j].Attributes.Filename, err)
}
e.Notes[i].Resources[j].ID = rec.ObjID
e.Notes[i].Resources[j].Type = rec.ObjType
}
}
return &e, err
}
func newDecoder(r io.Reader) *xml.Decoder {
d := xml.NewDecoder(r)
d.Strict = false
return d
}