-
Notifications
You must be signed in to change notification settings - Fork 0
/
core.clj
171 lines (158 loc) · 6.35 KB
/
core.clj
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
(ns cc.journeyman.real-name.core
"Resolve real names from user names in a platform independent fashion."
(:require [clojure.java.io :refer [reader]]
[clojure.java.shell :refer [sh]]
[clojure.string :refer [lower-case split starts-with? trim]])
(:import [java.io StringReader]
[java.util Locale])
(:gen-class))
(defn- mac-os-x
"Mac OS X, use the `id -F` command."
[username]
(let [response (sh "id" "-F" username)]
(if (zero? (:exit response))
(trim (:out response))
(throw (ex-info
(format "Real name for `%s` not found because `%s`."
username
(trim (:err response)))
{:username username
:error (:err response)
:os-name (System/getProperty "os.name")
:os-version (System/getProperty "os.version")})))))
(defn delimited-record->map
"Split this `record` into fields delimited by this `delimiter-pattern` and
return it as a map with these `keys`."
[^String record ^java.util.regex.Pattern delimiter-pattern keys]
(apply assoc
(cons {} (interleave keys (split record delimiter-pattern)))))
(defn process-gecos
"Process this `gecos` field into a map of its sub-fields. See
https://en.wikipedia.org/wiki/Gecos_field"
[gecos]
(delimited-record->map (str gecos ",?") #"," [:real-name :address :work-phone :home-phone :other]))
(defn process-passwd-line
"Process this `line` from a passwd file"
[line]
(let [record (delimited-record->map line #":" [:uname :pass :uid :gid :gecos :sh])]
(when record (assoc record :gecos (process-gecos (:gecos record))))))
(defn process-passwd
"Process the password file into a map whose keys are user names and whose
values are maps of associated records from lines in the file."
[]
(reduce #(assoc %1 (:uname %2) %2)
{}
(map process-passwd-line
(remove #(starts-with? % "#")(line-seq (reader "/etc/passwd"))))))
(defn- unix
"Generic unix, parse the GECOS field of the passwd record matching `username`."
([username]
(let [real-name (-> ((process-passwd) username) :gecos :real-name)]
(if real-name
real-name
(throw (ex-info (format "Real name for `%s` not found." username)
{:username username
:os-name (System/getProperty "os.name")
:os-version (System/getProperty "os.version")}))))))
(def full-name-in-locale
"Microsoft's own translations of `Full Name` into a variety of languages,
taken from their language portal. Clearly there must be some os-call that
they're using to resolve the translation, but I don't know it; so this is
the best I can currently do."
{"ar" "الاسم الكامل"
"bg" "Пълно име"
"ca" "Nom complet"
"cs" "Celé jméno"
"da" "Fulde navn"
"de" "Vollständiger Name"
"el" "Ονοματεπώνυμο" ;; Greek
"en" "Full Name"
"es" "Nombre completo"
"et" "Täielik nimi" ;; Microsoft also uses 'Täisnimi' in some contexts!
"fi" "Koko nimi"
"fr" "Nom complet"
"ga" "Ainm agus Sloinne" ;; Microsoft also uses 'Ainm iomlán' in some contexts!
"hu" "Teljes név"
"is" "Fullt Nafn"
"it" "Nome completo"
"jp" "氏名" ;; Microsoft also uses 'フル ネーム' and '完全名' in some contexts!
"lt" "Vardas, pavardė" ;; or 'Visas pavadinimas', or 'Visas vardas', or 'vardas ir pavardė'!
"lv" "Pilns vārds" ;; or 'Pilnais vārds', or 'Pilns nosaukums'
"nb" "Fullt navn" ;; Norse
"nl" "Volledige naam"
"nn" "Fullt namn" ;; also Norse
"no" "Fullt namn" ;; also Norse
"pl" "Imię i nazwisko" ;; or 'Pełna nazwa'
"pt" "Nome completo"
"ro" "Nume complet"
"ru" "Полное название" ;; or 'ФИО', or 'Полное имя'
"se" "Fullständigt namn"
"sk" "Celé meno"
"sl" "Polno ime" ;; or 'ime in priimek'
"tr" "Tam Adı" ;; or 'Tam Ad', or 'Adı-soyadı', or 'Ad ve soyadı', or 'Adı ve Soyadı'
"uk" "Повне ім’я" ;; or 'Ім'я та прізвище'
"zh" "全名"})
(defn- windows7+
"Very experimental, probably wrong, certainly works only in selected
locales."
[username]
(let [response (sh "net" "user" username)
full-name (full-name-in-locale
(first (split (str (Locale/getDefault)) #"_")))]
(if (zero? (:exit response))
(trim
(subs
(first (filter
;; Cast to lower-case because although in English,
;; Microsoft uses the capitalisation 'Full Name',
;; according to their own language portal in other
;; languages they don't; and I don't trust them!
#(starts-with? (lower-case %) (lower-case full-name))
(line-seq (reader (StringReader. (:out response))))))
(count full-name)))
(throw (ex-info
(format "Real name for `%s` not found" username)
{:username username
:response response
:locale (Locale/getDefault)
:localised-key full-name
:os-name (System/getProperty "os.name")
:os-version (System/getProperty "os.version")})))))
(defn username->real-name
"Given this `username`, return the associated real name if found; else
throw exception."
[^String username]
(let [os-name (System/getProperty "os.name")]
(case os-name
("AIX" "FreeBSD" "Irix" "Linux" "Solaris") (unix username)
"Mac OS X" (mac-os-x username)
;; "Windows 95"
;; "Windows NT"
;; "Windows Vista"
("Windows 7"
"Windows 8"
"Windows 8.1"
"Windows 10"
"Windows 11") (windows7+ username)
;; else
(throw (ex-info
(format "No hack available to find real name for user on `%s`."
os-name)
{:username username
:os-name os-name})))))
(defn get-real-name
"Get the real name of the user with this `username`, or of the current user
if no username passed."
([]
(get-real-name (System/getProperty "user.name")))
([^String username]
(username->real-name username)))
(defn getRealName
"Get the real name of the user with this `username`, or of the current user
if no username passed.
Essentially a wrapper around `get-real-name` for the convenience of Java
users."
([]
(get-real-name))
([username]
(get-real-name username)))