protocol.go

  1package daemonrpc
  2
  3import "encoding/json"
  4
  5// Request from client to daemon. Has an ID for matching responses.
  6type Request struct {
  7	ID     uint64          `json:"id"`
  8	Method string          `json:"method"`
  9	Params json.RawMessage `json:"params,omitempty"`
 10}
 11
 12// Response from daemon to client. Matched to request by ID.
 13type Response struct {
 14	ID     uint64          `json:"id"`
 15	Result json.RawMessage `json:"result,omitempty"`
 16	Error  *Error          `json:"error,omitempty"`
 17}
 18
 19// Event pushed from daemon to subscribed clients. No ID field.
 20type Event struct {
 21	Type string          `json:"type"`
 22	Data json.RawMessage `json:"data,omitempty"`
 23}
 24
 25// Error returned in a Response.
 26type Error struct {
 27	Code    int    `json:"code"`
 28	Message string `json:"message"`
 29}
 30
 31func (e *Error) Error() string { return e.Message }
 32
 33// Message is a union type for wire decoding. Exactly one of the
 34// fields will be populated based on the presence of "id" and "type".
 35type Message struct {
 36	Request  *Request
 37	Response *Response
 38	Event    *Event
 39}
 40
 41// Discriminate: if "type" present → Event, if "method" present → Request, else → Response.
 42func DecodeMessage(raw json.RawMessage) (Message, error) {
 43	var probe struct {
 44		Type   string  `json:"type"`
 45		Method string  `json:"method"`
 46		ID     *uint64 `json:"id"`
 47	}
 48	if err := json.Unmarshal(raw, &probe); err != nil {
 49		return Message{}, err
 50	}
 51
 52	var m Message
 53	switch {
 54	case probe.Type != "":
 55		var ev Event
 56		if err := json.Unmarshal(raw, &ev); err != nil {
 57			return m, err
 58		}
 59		m.Event = &ev
 60	case probe.Method != "":
 61		var req Request
 62		if err := json.Unmarshal(raw, &req); err != nil {
 63			return m, err
 64		}
 65		m.Request = &req
 66	default:
 67		var resp Response
 68		if err := json.Unmarshal(raw, &resp); err != nil {
 69			return m, err
 70		}
 71		m.Response = &resp
 72	}
 73	return m, nil
 74}
 75
 76// Standard error codes.
 77const (
 78	ErrCodeParse      = -32700
 79	ErrCodeInvalidReq = -32600
 80	ErrCodeNotFound   = -32601
 81	ErrCodeInternal   = -32603
 82)
 83
 84// RPC method names.
 85const (
 86	MethodPing            = "Ping"
 87	MethodGetStatus       = "GetStatus"
 88	MethodGetAccounts     = "GetAccounts"
 89	MethodReloadConfig    = "ReloadConfig"
 90	MethodFetchEmails     = "FetchEmails"
 91	MethodFetchEmailBody  = "FetchEmailBody"
 92	MethodSendEmail       = "SendEmail"
 93	MethodDeleteEmails    = "DeleteEmails"
 94	MethodArchiveEmails   = "ArchiveEmails"
 95	MethodMoveEmails      = "MoveEmails"
 96	MethodMarkRead        = "MarkRead"
 97	MethodFetchFolders    = "FetchFolders"
 98	MethodRefreshFolder   = "RefreshFolder"
 99	MethodSubscribe       = "Subscribe"
100	MethodUnsubscribe     = "Unsubscribe"
101	MethodSendRSVP        = "SendRSVP"
102	MethodGetCachedEmails = "GetCachedEmails"
103	MethodGetCachedBody   = "GetCachedBody"
104	MethodExportContacts  = "ExportContacts"
105)
106
107// Event type names.
108const (
109	EventNewMail        = "NewMail"
110	EventSyncStarted    = "SyncStarted"
111	EventSyncComplete   = "SyncComplete"
112	EventSyncError      = "SyncError"
113	EventEmailsUpdated  = "EmailsUpdated"
114	EventConfigReloaded = "ConfigReloaded"
115)
116
117// Param/result types for RPC methods.
118
119type PingResult struct {
120	Pong bool `json:"pong"`
121}
122
123type StatusResult struct {
124	Running  bool     `json:"running"`
125	Uptime   int64    `json:"uptime_seconds"`
126	Accounts []string `json:"accounts"`
127	PID      int      `json:"pid"`
128}
129
130type AccountInfo struct {
131	ID       string `json:"id"`
132	Name     string `json:"name"`
133	Email    string `json:"email"`
134	Protocol string `json:"protocol"`
135}
136
137type FetchEmailsParams struct {
138	AccountID string `json:"account_id"`
139	Folder    string `json:"folder"`
140	Limit     uint32 `json:"limit"`
141	Offset    uint32 `json:"offset"`
142}
143
144type FetchEmailBodyParams struct {
145	AccountID string `json:"account_id"`
146	Folder    string `json:"folder"`
147	UID       uint32 `json:"uid"`
148}
149
150type FetchEmailBodyResult struct {
151	Body         string           `json:"body"`
152	BodyMIMEType string           `json:"body_mime_type,omitempty"`
153	Attachments  []AttachmentInfo `json:"attachments"`
154}
155
156type AttachmentInfo struct {
157	Filename         string `json:"filename"`
158	PartID           string `json:"part_id"`
159	Encoding         string `json:"encoding"`
160	MIMEType         string `json:"mime_type"`
161	IsCalendarInvite bool   `json:"is_calendar_invite,omitempty"`
162	CalendarData     []byte `json:"calendar_data,omitempty"`
163}
164
165type SendEmailParams struct {
166	AccountID    string            `json:"account_id"`
167	To           []string          `json:"to"`
168	Cc           []string          `json:"cc,omitempty"`
169	Bcc          []string          `json:"bcc,omitempty"`
170	Subject      string            `json:"subject"`
171	Body         string            `json:"body"`
172	HTMLBody     string            `json:"html_body,omitempty"`
173	Attachments  map[string][]byte `json:"attachments,omitempty"`
174	InReplyTo    string            `json:"in_reply_to,omitempty"`
175	References   []string          `json:"references,omitempty"`
176	SignSMIME    bool              `json:"sign_smime,omitempty"`
177	EncryptSMIME bool              `json:"encrypt_smime,omitempty"`
178	SignPGP      bool              `json:"sign_pgp,omitempty"`
179	EncryptPGP   bool              `json:"encrypt_pgp,omitempty"`
180}
181
182type DeleteEmailsParams struct {
183	AccountID string   `json:"account_id"`
184	Folder    string   `json:"folder"`
185	UIDs      []uint32 `json:"uids"`
186}
187
188type ArchiveEmailsParams struct {
189	AccountID string   `json:"account_id"`
190	Folder    string   `json:"folder"`
191	UIDs      []uint32 `json:"uids"`
192}
193
194type MoveEmailsParams struct {
195	AccountID    string   `json:"account_id"`
196	UIDs         []uint32 `json:"uids"`
197	SourceFolder string   `json:"source_folder"`
198	DestFolder   string   `json:"dest_folder"`
199}
200
201type MarkReadParams struct {
202	AccountID string   `json:"account_id"`
203	Folder    string   `json:"folder"`
204	UIDs      []uint32 `json:"uids"`
205	Read      bool     `json:"read"`
206}
207
208type FetchFoldersParams struct {
209	AccountID string `json:"account_id"`
210}
211
212type RefreshFolderParams struct {
213	AccountID string `json:"account_id"`
214	Folder    string `json:"folder"`
215}
216
217type SubscribeParams struct {
218	AccountID string `json:"account_id"`
219	Folder    string `json:"folder"`
220}
221
222type UnsubscribeParams struct {
223	AccountID string `json:"account_id"`
224	Folder    string `json:"folder"`
225}
226
227type SendRSVPParams struct {
228	AccountID   string   `json:"account_id"`
229	OriginalICS []byte   `json:"original_ics"`
230	Response    string   `json:"response"`
231	InReplyTo   string   `json:"in_reply_to,omitempty"`
232	References  []string `json:"references,omitempty"`
233}
234
235type GetCachedEmailsParams struct {
236	Folder string `json:"folder"`
237}
238
239type GetCachedBodyParams struct {
240	Folder    string `json:"folder"`
241	UID       uint32 `json:"uid"`
242	AccountID string `json:"account_id"`
243}
244
245type ExportContactsParams struct {
246	Format string `json:"format"` // "json" or "csv"
247}
248
249// Event data types.
250
251type NewMailEvent struct {
252	AccountID string `json:"account_id"`
253	Folder    string `json:"folder"`
254}
255
256type SyncStartedEvent struct {
257	AccountID string `json:"account_id"`
258	Folder    string `json:"folder"`
259}
260
261type SyncCompleteEvent struct {
262	AccountID  string `json:"account_id"`
263	Folder     string `json:"folder"`
264	EmailCount int    `json:"email_count"`
265}
266
267type SyncErrorEvent struct {
268	AccountID string `json:"account_id"`
269	Folder    string `json:"folder"`
270	Error     string `json:"error"`
271}