~hkdb/geary/disco-3.34.1

« back to all changes in this revision

Viewing changes to src/engine/api/geary-account.vala

  • Committer: hkdb
  • Date: 2019-10-08 10:54:21 UTC
  • Revision ID: hkdb@3df.io-20191008105421-3dkwnpnhcamm77to
Tags: upstream-3.34.1-disco
ImportĀ upstreamĀ versionĀ 3.34.1-disco

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright 2016 Software Freedom Conservancy Inc.
 
3
 * Copyright 2018-2019 Michael Gratton <mike@vee.net>.
 
4
 *
 
5
 * This software is licensed under the GNU Lesser General Public License
 
6
 * (version 2.1 or later).  See the COPYING file in this distribution.
 
7
 */
 
8
 
 
9
/**
 
10
 * Account is the basic interface to the user's email account, {@link Folder}s, and various signals,
 
11
 * monitors, and notifications of account activity.
 
12
 *
 
13
 * In addition to providing an interface to the various Folders, Account offers aggregation signals
 
14
 * indicating changes to all Folders as they're discovered.  Because some mail interfaces don't
 
15
 * provide account-wide notification, these signals may only be fired when the Folder is open,
 
16
 * which sometimes happens in the background as background synchronization occurs.
 
17
 *
 
18
 * Accounts must be opened and closed with {@link open_async} and {@link close_async}.  Most
 
19
 * methods only work if the Account is opened (if not, that will be mentioned in their
 
20
 * documentation).
 
21
 *
 
22
 * A list of all Accounts may be retrieved from the {@link Engine} singleton.
 
23
 */
 
24
 
 
25
public abstract class Geary.Account : BaseObject, Loggable {
 
26
 
 
27
 
 
28
    /** Number of times to attempt re-authentication. */
 
29
    internal const uint AUTH_ATTEMPTS_MAX = 3;
 
30
 
 
31
 
 
32
     /**
 
33
     * Denotes the account's current status.
 
34
     *
 
35
     * @see Account.current_status
 
36
     * @see ClientService.current_status
 
37
     */
 
38
    [Flags]
 
39
    public enum Status {
 
40
 
 
41
        /**
 
42
         * The account is currently online and operating normally.
 
43
         *
 
44
         * This flags will be set when the account's {@link incoming}
 
45
         * service's {@link ClientService.current_status} is {@link
 
46
         * ClientService.Status.CONNECTED}.
 
47
         */
 
48
        ONLINE,
 
49
 
 
50
        /**
 
51
         * One or of the account's services is degraded.
 
52
         *
 
53
         * This flag will be set when one or both of its services has
 
54
         * encountered a problem. Consult the {@link
 
55
         * ClientService.current_status} to determine which and the
 
56
         * exact problem.
 
57
         */
 
58
        SERVICE_PROBLEM;
 
59
 
 
60
 
 
61
        /** Determines if the {@link ONLINE} flag is set. */
 
62
        public bool is_online() {
 
63
            return (this & ONLINE) == ONLINE;
 
64
        }
 
65
 
 
66
        /** Determines if the {@link SERVICE_PROBLEM} flag is set. */
 
67
        public bool has_service_problem() {
 
68
            return (this & SERVICE_PROBLEM) == SERVICE_PROBLEM;
 
69
        }
 
70
 
 
71
    }
 
72
 
 
73
    /**
 
74
     * A utility method to sort a Gee.Collection of {@link Folder}s by
 
75
     * their {@link FolderPath}s to ensure they comport with {@link
 
76
     * folders_available_unavailable}, {@link folders_created}, {@link
 
77
     * folders_deleted} signals' contracts.
 
78
     */
 
79
    public static Gee.BidirSortedSet<Folder>
 
80
        sort_by_path(Gee.Collection<Folder> folders) {
 
81
        Gee.TreeSet<Folder> sorted =
 
82
            new Gee.TreeSet<Folder>(Account.folder_path_comparator);
 
83
        sorted.add_all(folders);
 
84
        return sorted;
 
85
    }
 
86
 
 
87
    /**
 
88
     * Comparator used to sort folders.
 
89
     *
 
90
     * @see sort_by_path
 
91
     */
 
92
    public static int folder_path_comparator(Geary.Folder a, Geary.Folder b) {
 
93
        return a.path.compare_to(b.path);
 
94
    }
 
95
 
 
96
 
 
97
    /**
 
98
     * The account's current configuration.
 
99
     */
 
100
    public AccountInformation information { get; protected set; }
 
101
 
 
102
    /**
 
103
     * The account's current status.
 
104
     *
 
105
     * This property's value is set based on the {@link
 
106
     * ClientService.current_status} of the account's {@link incoming}
 
107
     * and {@link outgoing} services. See {@link Status} for more
 
108
     * information.
 
109
     *
 
110
     * The initial value for this property is {@link Status.ONLINE},
 
111
     * which may or may not be incorrect. However the once the account
 
112
     * has been opened, its services will begin checking connectivity
 
113
     * and the value will be updated to match in due course.
 
114
     *
 
115
     * @see ClientService.current_status
 
116
     */
 
117
    public Status current_status { get; protected set; default = ONLINE; }
 
118
 
 
119
    /**
 
120
     * The service manager for the incoming email service.
 
121
     */
 
122
    public ClientService incoming { get; private set; }
 
123
 
 
124
    /**
 
125
     * The service manager for the outgoing email service.
 
126
     */
 
127
    public ClientService outgoing { get; private set; }
 
128
 
 
129
    /**
 
130
     * The contact information store for this account.
 
131
     */
 
132
    public Geary.ContactStore contact_store { get; protected set; }
 
133
 
 
134
    public Geary.ProgressMonitor search_upgrade_monitor { get; protected set; }
 
135
    public Geary.ProgressMonitor db_upgrade_monitor { get; protected set; }
 
136
    public Geary.ProgressMonitor db_vacuum_monitor { get; protected set; }
 
137
    public Geary.ProgressMonitor opening_monitor { get; protected set; }
 
138
    public Geary.ProgressMonitor sending_monitor { get; protected set; }
 
139
 
 
140
 
 
141
    public signal void opened();
 
142
 
 
143
    public signal void closed();
 
144
 
 
145
    public signal void email_sent(Geary.RFC822.Message rfc822);
 
146
 
 
147
    /**
 
148
     * Emitted to notify the client that some problem has occurred.
 
149
     *
 
150
     * The engine uses this signal to report internal errors and other
 
151
     * issues that the client should notify the user about. The {@link
 
152
     * ProblemReport} class provides context about the nature of the
 
153
     * problem itself.
 
154
     */
 
155
    public signal void report_problem(Geary.ProblemReport problem);
 
156
 
 
157
    /**
 
158
     * Fired when folders become available or unavailable in the account.
 
159
     *
 
160
     * Folders become available when the account is first opened or when
 
161
     * they're created later; they become unavailable when the account is
 
162
     * closed or they're deleted later.
 
163
     *
 
164
     * Folders are ordered for the convenience of the caller from the
 
165
     * top of the hierarchy to lower in the hierarchy.  In other
 
166
     * words, parents are listed before children, assuming the
 
167
     * collections are traversed in natural order.
 
168
     *
 
169
     * @see sort_by_path
 
170
     */
 
171
    public signal void
 
172
        folders_available_unavailable(Gee.BidirSortedSet<Folder>? available,
 
173
                                      Gee.BidirSortedSet<Folder>? unavailable);
 
174
 
 
175
    /**
 
176
     * Fired when new folders have been created.
 
177
     *
 
178
     * This is fired in response to new folders appearing, for example
 
179
     * the user created a new folder. It will be fired after {@link
 
180
     * folders_available_unavailable} has been fired to mark the
 
181
     * folders as having been made available.
 
182
     *
 
183
     * Folders are ordered for the convenience of the caller from the
 
184
     * top of the hierarchy to lower in the hierarchy.  In other
 
185
     * words, parents are listed before children, assuming the
 
186
     * collection is traversed in natural order.
 
187
     */
 
188
    public signal void folders_created(Gee.BidirSortedSet<Geary.Folder> created);
 
189
 
 
190
    /**
 
191
     * Fired when existing folders are deleted.
 
192
     *
 
193
     * This is fired in response to existing folders being removed,
 
194
     * for example if the user deleted a folder. it will be fired
 
195
     * after {@link folders_available_unavailable} has been fired to
 
196
     * mark the folders as having been made unavailable.
 
197
     *
 
198
     * Folders are ordered for the convenience of the caller from the
 
199
     * top of the hierarchy to lower in the hierarchy.  In other
 
200
     * words, parents are listed before children, assuming the
 
201
     * collection is traversed in natural order.
 
202
     */
 
203
    public signal void folders_deleted(Gee.BidirSortedSet<Geary.Folder> deleted);
 
204
 
 
205
    /**
 
206
     * Fired when a Folder's contents is detected having changed.
 
207
     */
 
208
    public signal void folders_contents_altered(Gee.Collection<Geary.Folder> altered);
 
209
 
 
210
    /**
 
211
     * Fired when a Folder's type is detected having changed.
 
212
     */
 
213
    public signal void folders_special_type(Gee.Collection<Geary.Folder> altered);
 
214
 
 
215
    /**
 
216
     * Fired when emails are appended to a folder in this account.
 
217
     */
 
218
    public signal void email_appended(Geary.Folder folder, Gee.Collection<Geary.EmailIdentifier> ids);
 
219
 
 
220
    /**
 
221
     * Fired when emails are inserted to a folder in this account.
 
222
     *
 
223
     * @see Folder.email_inserted
 
224
     */
 
225
    public signal void email_inserted(Geary.Folder folder, Gee.Collection<Geary.EmailIdentifier> ids);
 
226
 
 
227
    /**
 
228
     * Fired when emails are removed from a folder in this account.
 
229
     */
 
230
    public signal void email_removed(Geary.Folder folder, Gee.Collection<Geary.EmailIdentifier> ids);
 
231
 
 
232
    /**
 
233
     * Fired when one or more emails have been locally saved to a folder with
 
234
     * the full set of Fields.
 
235
     */
 
236
    public signal void email_locally_complete(Geary.Folder folder,
 
237
        Gee.Collection<Geary.EmailIdentifier> ids);
 
238
 
 
239
    /**
 
240
     * Fired when one or more emails have been discovered (added) to the Folder, but not necessarily
 
241
     * appended (i.e. old email pulled down due to user request or background fetching).
 
242
     */
 
243
    public signal void email_discovered(Geary.Folder folder, Gee.Collection<Geary.EmailIdentifier> ids);
 
244
 
 
245
    /**
 
246
     * Fired when the supplied email flags have changed from any folder.
 
247
     */
 
248
    public signal void email_flags_changed(Geary.Folder folder,
 
249
        Gee.Map<Geary.EmailIdentifier, Geary.EmailFlags> map);
 
250
 
 
251
    /** {@inheritDoc} */
 
252
    public Logging.Flag loggable_flags {
 
253
        get; protected set; default = Logging.Flag.ALL;
 
254
    }
 
255
 
 
256
    /** {@inheritDoc} */
 
257
    public Loggable? loggable_parent { get { return null; } }
 
258
 
 
259
 
 
260
    protected Account(AccountInformation information,
 
261
                      ClientService incoming,
 
262
                      ClientService outgoing) {
 
263
        this.information = information;
 
264
        this.incoming = incoming;
 
265
        this.outgoing = outgoing;
 
266
 
 
267
        incoming.notify["current-status"].connect(
 
268
            on_service_status_notify
 
269
        );
 
270
        outgoing.notify["current-status"].connect(
 
271
            on_service_status_notify
 
272
        );
 
273
    }
 
274
 
 
275
    /**
 
276
     * Opens the {@link Account} and makes it and its {@link Folder}s available for use.
 
277
     *
 
278
     * @throws EngineError.CORRUPT if the local store is corrupt or unusable
 
279
     * @throws EngineError.PERMISSIONS if the local store is inaccessible
 
280
     * @throws EngineError.VERSION if the local store was created or updated for a different
 
281
     *         version of Geary.
 
282
     */
 
283
    public abstract async void open_async(Cancellable? cancellable = null) throws Error;
 
284
 
 
285
    /**
 
286
     * Closes the {@link Account}, which makes most its operations unavailable.
 
287
     *
 
288
     * This does not delete the Account, merely closes database and network channels.
 
289
     *
 
290
     * Returns without error if the Account is already closed.
 
291
     */
 
292
    public abstract async void close_async(Cancellable? cancellable = null) throws Error;
 
293
 
 
294
    /**
 
295
     * Returns true if this account is open, else false.
 
296
     */
 
297
    public abstract bool is_open();
 
298
 
 
299
    /**
 
300
     * Rebuild the local data stores for this {@link Account}.
 
301
     *
 
302
     * This should only be used if {@link open_async} throws {@link EngineError.CORRUPT},
 
303
     * indicating that the local data store is corrupted and cannot be used.
 
304
     *
 
305
     * ''rebuild_async() will delete all local data''.
 
306
     *
 
307
     * If the Account is backed by a synchronized copy on the network, it will rebuild its local
 
308
     * mail store.  If not, the data is forever deleted.  Hence, it's best to query the user before
 
309
     * calling this method.
 
310
     *
 
311
     * Unlike most methods in Account, this should only be called when the Account is closed.
 
312
     */
 
313
    public abstract async void rebuild_async(Cancellable? cancellable = null) throws Error;
 
314
 
 
315
    /**
 
316
     * Returns an email identifier from its serialised form.
 
317
     *
 
318
     * This is useful for converting a string representation of a
 
319
     * email id back into an actual instance of an id. This does not
 
320
     * guarantee that the email represented by the id will exist.
 
321
     *
 
322
     * @see EmailIdentifier.to_variant
 
323
     * @throws EngineError.BAD_PARAMETERS when the variant is not the
 
324
     * have the correct type.
 
325
     */
 
326
    public abstract EmailIdentifier to_email_identifier(GLib.Variant serialised)
 
327
        throws EngineError.BAD_PARAMETERS;
 
328
 
 
329
    /**
 
330
     * Returns the folder path from its serialised form.
 
331
     *
 
332
     * This is useful for converting a string representation of a
 
333
     * folder path back into an actual instance of a path. This does
 
334
     * not guarantee that the folder represented by the path will
 
335
     * exist.
 
336
     *
 
337
     * @see FolderPath.to_variant
 
338
     * @throws EngineError.BAD_PARAMETERS when the variant is not the
 
339
     * have the correct type or if no folder root with an appropriate
 
340
     * label exists.
 
341
     */
 
342
    public abstract FolderPath to_folder_path(GLib.Variant serialised)
 
343
        throws EngineError.BAD_PARAMETERS;
 
344
 
 
345
    /**
 
346
     * Determines if a folder is known to the engine.
 
347
     *
 
348
     * This method only considers currently known folders, it does not
 
349
     * check the remote to see if a previously folder exists.
 
350
     */
 
351
    public virtual bool has_folder(FolderPath path) {
 
352
        try {
 
353
            get_folder(path);
 
354
            return true;
 
355
        } catch (EngineError.NOT_FOUND err) {
 
356
            return false;
 
357
        }
 
358
    }
 
359
 
 
360
    /**
 
361
     * Returns the folder represented by a specific path.
 
362
     *
 
363
     * This method only considers currently known folders, it does not
 
364
     * check the remote to see if a previously folder exists.
 
365
     *
 
366
     * @throws EngineError.NOT_FOUND if the folder does not exist.
 
367
     */
 
368
    public abstract Folder get_folder(FolderPath path)
 
369
        throws EngineError.NOT_FOUND;
 
370
 
 
371
    /**
 
372
     * Lists all the currently-available folders found under the parent path
 
373
     * unless it's null, in which case it lists all the root folders.  If the
 
374
     * parent path cannot be found, EngineError.NOT_FOUND is thrown.  If no
 
375
     * folders exist in the root, EngineError.NOT_FOUND may be thrown as well.
 
376
     * However, the caller should be prepared to deal with an empty list being
 
377
     * returned instead.
 
378
     *
 
379
     * The same Geary.Folder objects (instances) will be returned if the same path is submitted
 
380
     * multiple times.  This means that multiple callers may be holding references to the same
 
381
     * Folders.  This is important when thinking of opening and closing folders and signal
 
382
     * notifications.
 
383
     */
 
384
    public abstract Gee.Collection<Geary.Folder> list_matching_folders(Geary.FolderPath? parent)
 
385
        throws Error;
 
386
 
 
387
    /**
 
388
     * Lists all currently-available folders.  See caveats under
 
389
     * list_matching_folders().
 
390
     */
 
391
    public abstract Gee.Collection<Geary.Folder> list_folders() throws Error;
 
392
 
 
393
    /**
 
394
     * Returns the folder representing the given special folder type.  If no such folder exists,
 
395
     * null is returned.
 
396
     */
 
397
    public virtual Geary.Folder? get_special_folder(Geary.SpecialFolderType special) throws Error {
 
398
        return traverse<Folder>(list_folders())
 
399
            .first_matching(f => f.special_folder_type == special);
 
400
    }
 
401
 
 
402
    /**
 
403
     * Returns the Folder object with the given special folder type.  The folder will be
 
404
     * created on the server if it doesn't already exist.  An error will be thrown if the
 
405
     * folder doesn't exist and can't be created.  The only valid special folder types that
 
406
     * can be required are: DRAFTS, SENT, SPAM, and TRASH.
 
407
     */
 
408
    public abstract async Geary.Folder get_required_special_folder_async(Geary.SpecialFolderType special,
 
409
        Cancellable? cancellable = null) throws Error;
 
410
 
 
411
    /**
 
412
     * Submits a ComposedEmail for delivery.  Messages may be scheduled for later delivery or immediately
 
413
     * sent.  Subscribe to the "email-sent" signal to be notified of delivery.  Note that that signal
 
414
     * does not return the ComposedEmail object but an RFC822-formatted object.  Allowing for the
 
415
     * subscriber to attach some kind of token for later comparison is being considered.
 
416
     */
 
417
    public abstract async void send_email_async(Geary.ComposedEmail composed, Cancellable? cancellable = null)
 
418
        throws Error;
 
419
 
 
420
    /**
 
421
     * Search the local account for emails referencing a Message-ID value
 
422
     * (which can appear in the Message-ID header itself, as well as the
 
423
     * In-Reply-To header, and maybe more places).  Fetch the requested fields,
 
424
     * optionally ignoring emails that don't have the requested fields set.
 
425
     * Don't include emails that appear in any of the blacklisted folders in
 
426
     * the result.  If null is included in the blacklist, omit emails appearing
 
427
     * in no folders.  Return a map of Email object to a list of FolderPaths
 
428
     * it's in, which can be null if it's in no folders.
 
429
     */
 
430
    public abstract async Gee.MultiMap<Geary.Email, Geary.FolderPath?>? local_search_message_id_async(
 
431
        Geary.RFC822.MessageID message_id, Geary.Email.Field requested_fields, bool partial_ok,
 
432
        Gee.Collection<Geary.FolderPath?>? folder_blacklist, Geary.EmailFlags? flag_blacklist,
 
433
        Cancellable? cancellable = null) throws Error;
 
434
 
 
435
    /**
 
436
     * Return a single email fulfilling the required fields.  The email to pull
 
437
     * is identified by an EmailIdentifier from a previous call to
 
438
     * local_search_message_id_async() or local_search_async().  Throw
 
439
     * EngineError.NOT_FOUND if the email isn't found and
 
440
     * EngineError.INCOMPLETE_MESSAGE if the fields aren't available.
 
441
     */
 
442
    public abstract async Geary.Email local_fetch_email_async(Geary.EmailIdentifier email_id,
 
443
        Geary.Email.Field required_fields, Cancellable? cancellable = null) throws Error;
 
444
 
 
445
    /**
 
446
     * Create a new {@link SearchQuery} for this {@link Account}.
 
447
     *
 
448
     * See {@link Geary.SearchQuery.Strategy} for more information about how its interpreted by the
 
449
     * Engine.  In particular, note that it's an advisory parameter only and may have no effect,
 
450
     * especially on server searches.  However, it may also have a dramatic effect on what search
 
451
     * results are returned and so should be used with some caution.  Whether this parameter is
 
452
     * user-configurable, available through GSettings or another configuration mechanism, or simply
 
453
     * baked into the caller's code is up to the caller.  CONSERVATIVE is designed to be a good
 
454
     * default.
 
455
     *
 
456
     * The SearchQuery object can only be used with calls into this Account.
 
457
     *
 
458
     * Dropping the last reference to the SearchQuery will close it.
 
459
     */
 
460
    public abstract async Geary.SearchQuery open_search(string query,
 
461
                                                        SearchQuery.Strategy strategy,
 
462
                                                        GLib.Cancellable? cancellable)
 
463
        throws GLib.Error;
 
464
 
 
465
    /**
 
466
     * Performs a search with the given query.  Optionally, a list of folders not to search
 
467
     * can be passed as well as a list of email identifiers to restrict the search to only those messages.
 
468
     * Returns a list of EmailIdentifiers, or null if there are no results.
 
469
     * The list is limited to a maximum number of results and starting offset,
 
470
     * so you can walk the table.  limit can be negative to mean "no limit" but
 
471
     * offset must not be negative.
 
472
     */
 
473
    public abstract async Gee.Collection<Geary.EmailIdentifier>? local_search_async(Geary.SearchQuery query,
 
474
        int limit = 100, int offset = 0, Gee.Collection<Geary.FolderPath?>? folder_blacklist = null,
 
475
        Gee.Collection<Geary.EmailIdentifier>? search_ids = null, Cancellable? cancellable = null) throws Error;
 
476
 
 
477
    /**
 
478
     * Given a list of mail IDs, returns a set of casefolded words that match for the query.
 
479
     */
 
480
    public abstract async Gee.Set<string>? get_search_matches_async(Geary.SearchQuery query,
 
481
        Gee.Collection<Geary.EmailIdentifier> ids, Cancellable? cancellable = null) throws Error;
 
482
 
 
483
    /**
 
484
     * Return a map of each passed-in email identifier to the set of folders
 
485
     * that contain it.  If an email id doesn't appear in the resulting map,
 
486
     * it isn't contained in any folders.  Return null if the resulting map
 
487
     * would be empty.  Only throw database errors et al., not errors due to
 
488
     * the email id not being found.
 
489
     */
 
490
    public abstract async Gee.MultiMap<Geary.EmailIdentifier, Geary.FolderPath>? get_containing_folders_async(
 
491
        Gee.Collection<Geary.EmailIdentifier> ids, Cancellable? cancellable) throws Error;
 
492
 
 
493
    /** {@inheritDoc} */
 
494
    public virtual string to_string() {
 
495
        return "%s(%s)".printf(
 
496
            this.get_type().name(),
 
497
            this.information.id
 
498
        );
 
499
    }
 
500
 
 
501
    /** Fires a {@link opened} signal. */
 
502
    protected virtual void notify_opened() {
 
503
        opened();
 
504
    }
 
505
 
 
506
    /** Fires a {@link closed} signal. */
 
507
    protected virtual void notify_closed() {
 
508
        closed();
 
509
    }
 
510
 
 
511
    /** Fires a {@link folders_available_unavailable} signal. */
 
512
    protected virtual void
 
513
        notify_folders_available_unavailable(Gee.BidirSortedSet<Folder>? available,
 
514
                                             Gee.BidirSortedSet<Folder>? unavailable) {
 
515
        folders_available_unavailable(available, unavailable);
 
516
    }
 
517
 
 
518
    /** Fires a {@link folders_created} signal. */
 
519
    protected virtual void notify_folders_created(Gee.BidirSortedSet<Geary.Folder> created) {
 
520
        folders_created(created);
 
521
    }
 
522
 
 
523
    /** Fires a {@link folders_deleted} signal. */
 
524
    protected virtual void notify_folders_deleted(Gee.BidirSortedSet<Geary.Folder> deleted) {
 
525
        folders_deleted(deleted);
 
526
    }
 
527
 
 
528
    /** Fires a {@link folders_contents_altered} signal. */
 
529
    protected virtual void notify_folders_contents_altered(Gee.Collection<Geary.Folder> altered) {
 
530
        folders_contents_altered(altered);
 
531
    }
 
532
 
 
533
    /** Fires a {@link email_appended} signal. */
 
534
    protected virtual void notify_email_appended(Geary.Folder folder, Gee.Collection<Geary.EmailIdentifier> ids) {
 
535
        email_appended(folder, ids);
 
536
    }
 
537
 
 
538
    /** Fires a {@link email_inserted} signal. */
 
539
    protected virtual void notify_email_inserted(Geary.Folder folder, Gee.Collection<Geary.EmailIdentifier> ids) {
 
540
        email_inserted(folder, ids);
 
541
    }
 
542
 
 
543
    /** Fires a {@link email_removed} signal. */
 
544
    protected virtual void notify_email_removed(Geary.Folder folder, Gee.Collection<Geary.EmailIdentifier> ids) {
 
545
        email_removed(folder, ids);
 
546
    }
 
547
 
 
548
    /** Fires a {@link email_locally_complete} signal. */
 
549
    protected virtual void notify_email_locally_complete(Geary.Folder folder,
 
550
        Gee.Collection<Geary.EmailIdentifier> ids) {
 
551
        email_locally_complete(folder, ids);
 
552
    }
 
553
 
 
554
    /** Fires a {@link email_discovered} signal. */
 
555
    protected virtual void notify_email_discovered(Geary.Folder folder,
 
556
        Gee.Collection<Geary.EmailIdentifier> ids) {
 
557
        email_discovered(folder, ids);
 
558
    }
 
559
 
 
560
    /** Fires a {@link email_flags_changed} signal. */
 
561
    protected virtual void notify_email_flags_changed(Geary.Folder folder,
 
562
        Gee.Map<Geary.EmailIdentifier, Geary.EmailFlags> flag_map) {
 
563
        email_flags_changed(folder, flag_map);
 
564
    }
 
565
 
 
566
    protected virtual void notify_email_sent(RFC822.Message message) {
 
567
        email_sent(message);
 
568
    }
 
569
 
 
570
    /** Fires a {@link report_problem} signal for this account. */
 
571
    protected virtual void notify_report_problem(ProblemReport report) {
 
572
        report_problem(report);
 
573
    }
 
574
 
 
575
    /**
 
576
     * Fires a {@link report_problem} signal for this account.
 
577
     */
 
578
    protected virtual void notify_account_problem(Error? err) {
 
579
        report_problem(new AccountProblemReport(this.information, err));
 
580
    }
 
581
 
 
582
    /** Fires a {@link report_problem} signal for a service for this account. */
 
583
    protected virtual void notify_service_problem(ServiceInformation service,
 
584
                                                  Error? err) {
 
585
        report_problem(
 
586
            new ServiceProblemReport(this.information, service, err)
 
587
        );
 
588
    }
 
589
 
 
590
    private void on_service_status_notify() {
 
591
        Status new_status = 0;
 
592
        // Don't consider service status UNKNOWN to indicate being
 
593
        // offline, since clients will indicate offline status, but
 
594
        // not indicate online status. So when at startup, or when
 
595
        // restarting services, we don't want to cause them to
 
596
        // spuriously indicate being offline.
 
597
        if (incoming.current_status != UNREACHABLE) {
 
598
            new_status |= ONLINE;
 
599
        }
 
600
        if (incoming.current_status.is_error() ||
 
601
            outgoing.current_status.is_error()) {
 
602
            new_status |= SERVICE_PROBLEM;
 
603
        }
 
604
        this.current_status = new_status;
 
605
    }
 
606
 
 
607
}