summaryrefslogtreecommitdiffstats
path: root/comm/mail/components/about-support/AboutSupportUnix.jsm
blob: a27b3c99c579bc626fa19700025fc16db779bcc7 (plain)
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
127
128
129
130
131
132
133
134
135
136
137
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

var EXPORTED_SYMBOLS = ["AboutSupportPlatform"];

// JS ctypes are needed to get at the data we need
var { ctypes } = ChromeUtils.importESModule(
  "resource://gre/modules/ctypes.sys.mjs"
);
var GFile = ctypes.StructType("GFile");
var GFileInfo = ctypes.StructType("GFileInfo");
var GError = ctypes.StructType("GError");
var GCancellable = ctypes.StructType("GCancellable");

var G_FILE_ATTRIBUTE_FILESYSTEM_TYPE = "filesystem::type";

var kNetworkFilesystems = ["afs", "cifs", "nfs", "smb"];

// These libraries might not be available on all systems.
var gLibsExist = false;
try {
  // GC is responsible for closing these libraries if they exist.
  var glib = ctypes.open("libglib-2.0.so.0");
  var gobject = ctypes.open("libgobject-2.0.so.0");
  var gio = ctypes.open("libgio-2.0.so.0");
  gLibsExist = true;
} catch (ex) {}

if (gLibsExist) {
  var g_free = glib.declare(
    "g_free",
    ctypes.default_abi,
    ctypes.void_t,
    ctypes.voidptr_t
  );
  var g_object_unref = gobject.declare(
    "g_object_unref",
    ctypes.default_abi,
    ctypes.void_t,
    ctypes.voidptr_t
  );
}

var AboutSupportPlatform = {
  /**
   * Given an nsIFile, gets the file system type. The type is returned as a
   * string. Possible values are "network", "local", "unknown" and null.
   */
  getFileSystemType(aFile) {
    // Check if the libs exist.
    if (!gLibsExist) {
      return "unknown";
    }

    try {
      // Given a UTF-8 string, converts it to the current Glib locale.
      let g_filename_from_utf8 = glib.declare(
        "g_filename_from_utf8",
        ctypes.default_abi,
        ctypes.char.ptr, // return type: glib locale string
        ctypes.char.ptr, // in: utf8string
        ctypes.ssize_t, // in: len
        ctypes.size_t.ptr, // out: bytes_read
        ctypes.size_t.ptr, // out: bytes_written
        GError.ptr // out: error
      );
      // Yes, we want function scoping for variables we need to free in the
      // finally block. I think this is better than declaring lots of variables
      // on top.
      var filePath = g_filename_from_utf8(aFile.path, -1, null, null, null);
      if (filePath.isNull()) {
        throw new Error(
          "Unable to convert " + aFile.path + " into GLib encoding"
        );
      }

      // Given a path, creates a new GFile for it.
      let g_file_new_for_path = gio.declare(
        "g_file_new_for_path",
        ctypes.default_abi,
        GFile.ptr, // return type: a newly-allocated GFile
        ctypes.char.ptr // in: path
      );
      var glibFile = g_file_new_for_path(filePath);

      // Given a GFile, queries the given attributes and returns them
      // as a GFileInfo.
      let g_file_query_filesystem_info = gio.declare(
        "g_file_query_filesystem_info",
        ctypes.default_abi,
        GFileInfo.ptr, // return type
        GFile.ptr, // in: file
        ctypes.char.ptr, // in: attributes
        GCancellable.ptr, // in: cancellable
        GError.ptr // out: error
      );
      var glibFileInfo = g_file_query_filesystem_info(
        glibFile,
        G_FILE_ATTRIBUTE_FILESYSTEM_TYPE,
        null,
        null
      );
      if (glibFileInfo.isNull()) {
        throw new Error("Unabled to retrieve GLib file info for " + aFile.path);
      }

      let g_file_info_get_attribute_string = gio.declare(
        "g_file_info_get_attribute_string",
        ctypes.default_abi,
        ctypes.char.ptr, // return type: file system type (do not free)
        GFileInfo.ptr, // in: info
        ctypes.char.ptr // in: attribute
      );
      let fsType = g_file_info_get_attribute_string(
        glibFileInfo,
        G_FILE_ATTRIBUTE_FILESYSTEM_TYPE
      );
      if (fsType.isNull()) {
        return "unknown";
      } else if (kNetworkFilesystems.includes(fsType.readString())) {
        return "network";
      }
      return "local";
    } finally {
      if (filePath) {
        g_free(filePath);
      }
      if (glibFile && !glibFile.isNull()) {
        g_object_unref(glibFile);
      }
      if (glibFileInfo && !glibFileInfo.isNull()) {
        g_object_unref(glibFileInfo);
      }
    }
  },
};