Class: Pikuri::Thunderbird::Profile

Inherits:
Object
  • Object
show all
Defined in:
lib/pikuri/thunderbird/profile.rb

Overview

Locates the active Thunderbird profile on disk and derives the paths the mail/calendar backends read. Discovery never guesses: it either resolves one profile unambiguously, raises DiscoveryError with a fix, or (when Thunderbird isn't installed) returns nil so the extension can simply omit the tools.

profile = Pikuri::Thunderbird::Profile.discover  # => Profile or nil
profile&.gloda_path      # …/global-messages-db.sqlite
profile&.gloda?          # is the Gloda index present?
profile&.calendar_dir    # …/calendar-data

Two install roots are probed, in this order: snap (+~/snap/thunderbird/common/.thunderbird+, confirmed) and apt (+~/.thunderbird+, recalled). Flatpak is out of scope. If both roots hold a profiles.ini (a stale apt profile left behind by an apt→snap migration is the common case), discovery raises rather than guess which is live — the one-time fix (delete the obsolete root) beats a liveness heuristic that guesses wrong exactly there. An explicit profile_dir: override bypasses all of this.

Implementation details

Within a root, the active profile is the profiles.ini section with Default=1 (or the sole profile when there's exactly one and none is marked default); Path is resolved relative to the ini's directory when IsRelative=1. The confirmed real-world ini has no [Install…] sections (Firefox's per-install default mechanism), so the plain Default=1 scan suffices.

Defined Under Namespace

Classes: DiscoveryError

Constant Summary collapse

SNAP_ROOT =

Returns snap install root (confirmed on a live box).

Returns:

  • (String)

    snap install root (confirmed on a live box).

File.expand_path('~/snap/thunderbird/common/.thunderbird')
APT_ROOT =

Returns apt/deb install root (recalled).

Returns:

  • (String)

    apt/deb install root (recalled).

File.expand_path('~/.thunderbird')
ROOTS =

Returns probed roots in preference order.

Returns:

  • (Hash{Symbol => String})

    probed roots in preference order.

{ snap: SNAP_ROOT, apt: APT_ROOT }.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(dir:, variant:) ⇒ Profile

Parameters:

  • dir (String)

    resolved profile directory (absolute).

  • variant (Symbol)

    originating root (+:snap+/+:apt+/+:override+).



85
86
87
88
# File 'lib/pikuri/thunderbird/profile.rb', line 85

def initialize(dir:, variant:)
  @dir = dir
  @variant = variant
end

Instance Attribute Details

#dirString (readonly)

Returns absolute path of the resolved profile directory.

Returns:

  • (String)

    absolute path of the resolved profile directory.



49
50
51
# File 'lib/pikuri/thunderbird/profile.rb', line 49

def dir
  @dir
end

#variantSymbol (readonly)

Returns :snap, :apt, or :override — which root this came from. Drives #outbox_dir (snap confinement differs).

Returns:

  • (Symbol)

    :snap, :apt, or :override — which root this came from. Drives #outbox_dir (snap confinement differs).



53
54
55
# File 'lib/pikuri/thunderbird/profile.rb', line 53

def variant
  @variant
end

Class Method Details

.discover(profile_dir: nil) ⇒ Profile?

Discover the active profile, or return nil when Thunderbird is not installed (no profiles.ini under any probed root).

Parameters:

  • profile_dir (String, nil) (defaults to: nil)

    explicit override; when given, it is used verbatim (expanded) and the root scan is skipped.

Returns:

  • (Profile, nil)

    the resolved profile, or nil if none found.

Raises:

  • (DiscoveryError)

    if profile_dir isn't a directory, if both roots hold a profile, or if a root has several profiles and none is marked Default=1.



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/pikuri/thunderbird/profile.rb', line 64

def self.discover(profile_dir: nil)
  return from_override(profile_dir) if profile_dir

  present = ROOTS.select { |_, root| File.file?(File.join(root, 'profiles.ini')) }
  return nil if present.empty?

  if present.size > 1
    paths = present.values.join(' and ')
    raise DiscoveryError,
          "Found Thunderbird profiles under two roots (#{paths}). This usually " \
          'means a stale profile from an old install. Delete the obsolete root, ' \
          'or pass profile_dir: to pick one explicitly.'
  end

  variant, root = present.first
  new(dir: resolve_within(root), variant: variant)
end

Instance Method Details

#calendar_cache_dbString

Returns cached network-calendar store (CalDAV etc.).

Returns:

  • (String)

    cached network-calendar store (CalDAV etc.).



101
# File 'lib/pikuri/thunderbird/profile.rb', line 101

def calendar_cache_db = File.join(calendar_dir, 'cache.sqlite')

#calendar_dbsArray<String>

Returns existing calendar DBs (cache first — it holds the CalDAV events; local holds storage calendars). Empty when the calendar-data dir has neither.

Returns:

  • (Array<String>)

    existing calendar DBs (cache first — it holds the CalDAV events; local holds storage calendars). Empty when the calendar-data dir has neither.



109
110
111
# File 'lib/pikuri/thunderbird/profile.rb', line 109

def calendar_dbs
  [calendar_cache_db, calendar_local_db].select { |p| File.file?(p) }
end

#calendar_dirString

Returns the calendar stores directory.

Returns:

  • (String)

    the calendar stores directory.



98
# File 'lib/pikuri/thunderbird/profile.rb', line 98

def calendar_dir = File.join(@dir, 'calendar-data')

#calendar_local_dbString

Returns local ("storage") calendar store.

Returns:

  • (String)

    local ("storage") calendar store.



104
# File 'lib/pikuri/thunderbird/profile.rb', line 104

def calendar_local_db = File.join(calendar_dir, 'local.sqlite')

#gloda?Boolean

Returns whether the Gloda index exists (the mail backend's available? gate).

Returns:

  • (Boolean)

    whether the Gloda index exists (the mail backend's available? gate).



95
# File 'lib/pikuri/thunderbird/profile.rb', line 95

def gloda? = File.file?(gloda_path)

#gloda_pathString

Returns path to the Gloda full-text index DB.

Returns:

  • (String)

    path to the Gloda full-text index DB.



91
# File 'lib/pikuri/thunderbird/profile.rb', line 91

def gloda_path = File.join(@dir, 'global-messages-db.sqlite')

#outbox_dirString

A directory a snap-confined Thunderbird can read a hand-off file from, for the .ics import hand-off (CalendarCreate). Snap's home interface blocks hidden $HOME dirs (so the ~/.cache snapshot dir is unreadable by the confined app — see DESIGN.md § "Platform confinement"); we stage instead in the variant's own readable root: the snap-owned data area for :snap (readable regardless of the dotfile rule), the unconfined profile dir for +:apt+/+:override+. The caller creates the dir and reaps the file.

Returns:

  • (String)

    …/snap/thunderbird/common/pikuri-outbox or <profile>/pikuri-outbox.



124
125
126
127
# File 'lib/pikuri/thunderbird/profile.rb', line 124

def outbox_dir
  base = @variant == :snap ? File.dirname(SNAP_ROOT) : @dir
  File.join(base, 'pikuri-outbox')
end