Настраиваем Mozilla Thunderbird в корпоративной среде Windows

в 15:20, , рубрики: ldap, powershell, thunderbird, windows, системное администрирование

Будем следовать принципу: Чем меньше надо настраивать пользователю, тем меньше вероятность того что он что то поломает. Думаю пользователь с вводом своего пароля справится.

Необходимо настроить:

  1. Файл конфигурации для подключения к серверу.
  2. Справочник контактов из LDAP.
  3. Подпись сотрудника в письме в соответствии с корпоративными стандартами.

Имеем на данный момент:

  1. Установленный почтовый клиент Thunderbird на рабочих станциях средствами групповой политики.
  2. Почта на biz.mail.ru (может быть и другой)
  3. Пользователи в AD с логином вида i.ivanov@domain.cn

Установка Thunderbird средствами GPO

Мы не будем качать .msi файлы от сторонних разработчиков, я не доверяю перепакованным программам, тем более что Thunderbird умеет ставиться тихой установкой из командной строки. Этим преимуществом мы и воспользуемся, а чтоб не переустанавливать его каждый раз при загрузке системы будем проверять ключи в реестре.
Скачиваем Thunderbird с сайта и закидываем в шару (права должны быть на чтение всем ПК в домене)
Сам скрипт

set VERSION=52.7.0
set SHARE="ПАПКА"
if %PROCESSOR_ARCHITECTURE% == x86 (
  set REGISTRY_KEY_NAME="HKLMSOFTWAREMozillaMozilla Thunderbird"
) else (
  set REGISTRY_KEY_NAME="HKLMSOFTWAREWow6432NodeMozillaMozilla Thunderbird"
)
reg query %REGISTRY_KEY_NAME% /v CurrentVersion | find "%VERSION% (ru)"
if ERRORLEVEL 1 "\%SHARE%Thunderbird Setup %VERSION%.exe" -ms

Необходимо поменять первые переменные. Версия и папка.
Версия соответствует имени файла, на момент написания статьи актуальная версия 52.7.0.
Имя файла Thunderbird Setup 52.7.0.exe

Сохраняем в ту же папку, обзываем InstallMozillaThunderbird.bat и добавляем в GPO на старт скрипта при запуске системы.

P.S. таким-же методом можно установить и Mozilla Firefox.

Настраиваем Thunderbird при запуске.

При первом запуске Thunderbird генерирует папку вида 123.default в папке %appdata%ThunderbirdProfiles, а в файле %appdata%Thunderbirdprofiles.ini создает ссылку на данную папку.

Поэтому мы создадим данные настройки раньше, при входе пользователя.

Заходим в групповые политики и создаем политику.
Конфигурация пользователей => Настройка =>Конфигурация Windows => INI-файлы.

Создаем 5 ключей

Путь к файлу Имя раздела Имя свойства Значение свойства
%AppData%Thunderbirdprofiles.ini Profile0 Default 1
%AppData%Thunderbirdprofiles.ini Profile0 IsRelative 1
%AppData%Thunderbirdprofiles.ini Profile0 Name %username%
%AppData%Thunderbirdprofiles.ini Profile0 Path Profiles/%username%.default
%AppData%Thunderbirdprofiles.ini General StartWithLastProfile 1

Файл profiles.ini сконфигурирован, остается создать папку Profiles/%username%.default и заполнить ее файлами конфигураций.

За настройку Thunderbird отвечает файл prefs.js
Его мы и будем генерировать своими данными для доступа к IMAP, а так же к LDAP через KerberOS.

Я начал с написания PowerShell который вставляем в GPO при входе пользователя. Нам важно его запускать правами пользователя который выполнил вход.

Конфигурация пользователей => Политики => Конфигурация Windows => Сценарии (вход/выход из системы) => Вход в систему => Сценарии PowerShell

start.ps1

$profiledir = "$env:APPDATAThunderbirdProfiles$env:UserName.default"
md $profiledir #Создаем папку для профиля.
powershell "\domain.cnNETLOGONsoftnew_prefs.ps1" #тут мы генерируем файл

new_prefs.ps1

#Ищем полное имя пользователя (Фамилия Имя Отчество)
$UserName = $env:username
$Filter = "(&(objectCategory=User)(samAccountName=$UserName))"
$Searcher = New-Object System.DirectoryServices.DirectorySearcher
$Searcher.Filter = $Filter
$ADUserPath = $Searcher.FindOne()
$ADUser = $ADUserPath.GetDirectoryEntry()
$ADDisplayName = $ADUser.DisplayName

############################################################################################################################

$domain="mail.ru" #Почтовый домен
$imap="imap.mail.ru" #imap сервер
$dc="dc1.domain.cn" #Контролер домена
$bdn="CN=Users,DC=domain,DC=cn" #Base DN

$file="$env:appdataThunderbirdProfiles$env:username.defaultprefs.js"
echo '#######################' | out-file $file -encoding UTF8
echo 'user_pref("ldap_2.autoComplete.directoryServer", "ldap_2.servers.company");' | out-file $file -encoding UTF8 -Append
echo 'user_pref("ldap_2.autoComplete.useDirectory", true);' | out-file $file -encoding UTF8 -Append
echo 'user_pref("ldap_2.servers.company.auth.dn", "");' | out-file $file -encoding UTF8 -Append
echo 'user_pref("ldap_2.servers.company.auth.saslmech", "GSSAPI");' | out-file $file -encoding UTF8 -Append
echo 'user_pref("ldap_2.servers.company.description", "company");' | out-file $file -encoding UTF8 -Append
echo 'user_pref("ldap_2.servers.company.filename", "ldap.mab");' | out-file $file -encoding UTF8 -Append
echo 'user_pref("ldap_2.servers.company.maxHits", 100);' | out-file $file -encoding UTF8 -Append
$id1 = echo 'user_pref("ldap_2.servers.company.uri", "ldap://'
$id2 = echo $dc/$bdn'??sub?(objectclass=*)");'
echo $id1$id2 | out-file $file -encoding UTF8 -Append
echo 'user_pref("mail.ab_remote_content.migrated", 1);' | out-file $file -encoding UTF8 -Append
echo 'user_pref("mail.account.account1.identities", "id1");' | out-file $file -encoding UTF8 -Append
echo 'user_pref("mail.account.account1.server", "server1");' | out-file $file -encoding UTF8 -Append
echo 'user_pref("mail.account.account2.server", "server2");' | out-file $file -encoding UTF8 -Append
echo 'user_pref("mail.account.lastKey", 2);' | out-file $file -encoding UTF8 -Append
echo 'user_pref("mail.accountmanager.accounts", "account1,account2");' | out-file $file -encoding UTF8 -Append
echo 'user_pref("mail.accountmanager.defaultaccount", "account1");' | out-file $file -encoding UTF8 -Append
echo 'user_pref("mail.accountmanager.localfoldersserver", "server2");' | out-file $file -encoding UTF8 -Append
echo 'user_pref("mail.append_preconfig_smtpservers.version", 2);' | out-file $file -encoding UTF8 -Append
echo 'user_pref("mail.attachment.store.version", 1);' | out-file $file -encoding UTF8 -Append
echo 'user_pref("mail.default_charsets.migrated", 1);' | out-file $file -encoding UTF8 -Append
echo 'user_pref("mail.folder.views.version", 1);' | out-file $file -encoding UTF8 -Append
echo 'user_pref("mail.font.windows.version", 2);' | out-file $file -encoding UTF8 -Append

$id1 = echo 'user_pref("mail.identity.id1.draft_folder", "imap://' 
$id2 = echo $env:username%40$domain@$imap/Drafts'");'
echo $id1$id2 | out-file $file -encoding UTF8 -Append

echo 'user_pref("mail.identity.id1.attach_signature", true);' | out-file $file -encoding UTF8 -Append

echo 'user_pref("mail.identity.id1.drafts_folder_picker_mode", "0");' | out-file $file -encoding UTF8 -Append

$id1 = echo 'user_pref("mail.identity.id1.fcc_folder", "imap://'
$id2 = echo $env:username%40$domain@$imap/Sent'");'
echo $id1$id2 | out-file $file -encoding UTF8 -Append

echo 'user_pref("mail.identity.id1.fcc_folder_picker_mode", "0");' | out-file $file -encoding UTF8 -Append

$id1 = echo 'user_pref("mail.identity.id1.fullName", "'
$id2 = echo $ADDisplayName'");'
echo $id1$id2 | out-file $file -encoding UTF8 -Append

echo 'user_pref("mail.identity.id1.htmlSigFormat", true);'  | out-file $file -encoding UTF8 -Append
echo 'user_pref("mail.identity.id1.reply_on_top", 1);'  | out-file $file -encoding UTF8 -Append

$id1 = echo 'user_pref("mail.identity.id1.sig_file", "C:\Users\'
$id2 = echo $env:username\AppData\Roaming\Thunderbird\Profiles\$env:username.default\signature.htm'");'

echo $id1$id2 | out-file $file -encoding UTF8 -Append

echo 'user_pref("mail.identity.id1.sig_file-rel", "[ProfD]signature.htm");' | out-file $file -encoding UTF8 -Append

echo 'user_pref("mail.identity.id1.sign_mail", false);' | out-file $file -encoding UTF8 -Append
echo 'user_pref("mail.identity.id1.smtpServer", "smtp1");' | out-file $file -encoding UTF8 -Append

$id1 = echo 'user_pref("mail.identity.id1.stationery_folder", "imap://'
$id2 = echo $env:username%40$domain@$imap/Templates'");'
echo $id1$id2 | out-file $file -encoding UTF8 -Append

echo 'user_pref("mail.identity.id1.tmpl_folder_picker_mode", "0");' | out-file $file -encoding UTF8 -Append

$id1 = echo 'user_pref("mail.identity.id1.useremail", "'
$id2 = echo $env:username@$domain'");'
echo $id1$id2 | out-file $file -encoding UTF8 -Append

echo 'user_pref("mail.identity.id1.valid", true);' | out-file $file -encoding UTF8 -Append
echo 'user_pref("mail.openMessageBehavior.version", 1);' | out-file $file -encoding UTF8 -Append
echo 'user_pref("mail.rights.version", 1);' | out-file $file -encoding UTF8 -Append

$id1 = echo 'user_pref("mail.root.imap", "C:\Users\'
$id2 = echo $env:username\AppData\Roaming\Thunderbird\Profiles\$env:username.default\ImapMail'");'
echo $id1$id2 | out-file $file -encoding UTF8 -Append

echo 'user_pref("mail.root.imap-rel", "[ProfD]ImapMail");' | out-file $file -encoding UTF8 -Append

$id1 = echo 'user_pref("mail.root.none", "C:\Users\'
$id2 = echo $env:username\AppData\Roaming\Thunderbird\Profiles\$env:username.default\Mail'");'
echo $id1$id2 | out-file $file -encoding UTF8 -Append

echo 'user_pref("mail.root.none-rel", "[ProfD]Mail");' | out-file $file -encoding UTF8 -Append
echo 'user_pref("mail.server.server1.cacheCapa.acl", false);' | out-file $file -encoding UTF8 -Append
echo 'user_pref("mail.server.server1.cacheCapa.quota", false);' | out-file $file -encoding UTF8 -Append
echo 'user_pref("mail.server.server1.canChangeStoreType", true);' | out-file $file -encoding UTF8 -Append
echo 'user_pref("mail.server.server1.check_new_mail", true);' | out-file $file -encoding UTF8 -Append

$id1 = echo 'user_pref("mail.server.server1.directory", "C:\Users\'
$id2 = echo $env:username\AppData\Roaming\Thunderbird\Profiles\$env:username.default\ImapMail\$imap'");'
echo $id1$id2 | out-file $file -encoding UTF8 -Append

$id1 = echo 'user_pref("mail.server.server1.directory-rel", "[ProfD]ImapMail/'
$id2 = echo $imap'");'
echo $id1$id2 | out-file $file -encoding UTF8 -Append

$id1 = echo 'user_pref("mail.server.server1.hostname", "'
$id2 = echo $imap'");'
echo $id1$id2 | out-file $file -encoding UTF8 -Append

echo 'user_pref("mail.server.server1.login_at_startup", true);' | out-file $file -encoding UTF8 -Append
echo 'user_pref("mail.server.server1.max_cached_connections", 5);' | out-file $file -encoding UTF8 -Append

$id1 = echo 'user_pref("mail.server.server1.name", "'
$id2 = echo $env:username@$domain'");'
echo $id1$id2 | out-file $file -encoding UTF8 -Append

echo 'user_pref("mail.server.server1.port", 993);' | out-file $file -encoding UTF8 -Append
echo 'user_pref("mail.server.server1.socketType", 3);' | out-file $file -encoding UTF8 -Append

$id1 = echo 'user_pref("mail.server.server1.spamActionTargetAccount", "imap://'
$id2 = echo $env:username%40$domain@$imap'");'
echo $id1$id2 | out-file $file -encoding UTF8 -Append

echo 'user_pref("mail.server.server1.storeContractID", "@mozilla.org/msgstore/berkeleystore;1");' | out-file $file -encoding UTF8 -Append
echo 'user_pref("mail.server.server1.type", "imap");' | out-file $file -encoding UTF8 -Append

$id1 = echo 'user_pref("mail.server.server1.userName", "'
$id2 = echo $env:username@$domain'");'
echo $id1$id2 | out-file $file -encoding UTF8 -Append

$id1 = echo 'user_pref("mail.server.server2.directory", "C:\Users\'
$id2 = echo $env:username\AppData\Roaming\Thunderbird\Profiles\$env:username.default\Mail\Local Folders'");'
echo $id1$id2 | out-file $file -encoding UTF8 -Append

echo 'user_pref("mail.server.server2.directory-rel", "[ProfD]Mail/Local Folders");' | out-file $file -encoding UTF8 -Append
echo 'user_pref("mail.server.server2.hostname", "Local Folders");' | out-file $file -encoding UTF8 -Append
echo 'user_pref("mail.server.server2.name", "Локальные папки");' | out-file $file -encoding UTF8 -Append
echo 'user_pref("mail.server.server2.storeContractID", "@mozilla.org/msgstore/berkeleystore;1");' | out-file $file -encoding UTF8 -Append
echo 'user_pref("mail.server.server2.type", "none");' | out-file $file -encoding UTF8 -Append
echo 'user_pref("mail.server.server2.userName", "nobody");' | out-file $file -encoding UTF8 -Append
echo 'user_pref("mail.smtpserver.smtp1.authMethod", 3);' | out-file $file -encoding UTF8 -Append
echo 'user_pref("mail.smtpserver.smtp1.description", "mail.ru");' | out-file $file -encoding UTF8 -Append
echo 'user_pref("mail.smtpserver.smtp1.hostname", "smtp.mail.ru");' | out-file $file -encoding UTF8 -Append
echo 'user_pref("mail.smtpserver.smtp1.port", 465);' | out-file $file -encoding UTF8 -Append
echo 'user_pref("mail.smtpserver.smtp1.try_ssl", 3);' | out-file $file -encoding UTF8 -Append

$id1 = echo 'user_pref("mail.smtpserver.smtp1.username", "'
$id2 = echo $env:username@$domain'");'
echo $id1$id2 | out-file $file -encoding UTF8 -Append

echo 'user_pref("mail.smtpservers", "smtp1");' | out-file $file -encoding UTF8 -Append
echo 'user_pref("mail.spam.version", 1);' | out-file $file -encoding UTF8 -Append
echo 'user_pref("mail.taskbar.lastgroupid", "8216C80C92C4E828");' | out-file $file -encoding UTF8 -Append
echo 'user_pref("mail.ui-rdf.version", 15);' | out-file $file -encoding UTF8 -Append
echo 'user_pref("mail.winsearch.firstRunDone", true);' | out-file $file -encoding UTF8 -Append

Теперь при запуске Thunderbird будет запрашиваться только пароль от почты.
Скрипт однозначно работает c imap сервером imap.mail.ru. С другими не пробовал, возможно надо будет допиливать.

Вы могли заметить при генерации prefs.js мы указали что подпись брать с файла signature.htm которая находится в той-же папке где и prefs.js. Будем делать теперь подпись.

Настраиваем подпись электронной почты.

Для создания красивой подписи нам нужен какой нибудь сервис где можно генерировать подпись и на основе ее будем делать подпись для наших пользователей.
Я пользовался сервисом mailsig(точка)ru (не реклама)
Можно и самому сделать подпись на том же HTML, но мне было лень.

На выходе получаем код
Настраиваем Mozilla Thunderbird в корпоративной среде Windows - 1

Добавляем еще одну строчку в start.ps1

powershell "\domain.cnNETLOGONsoftsignature.ps1" #тут мы генерируем подпись

Конечно можно было бы всё сразу вместить в один файл, к сожалению я люблю когда все лежит по своим местам. Да и проще разбираться когда файл называется так-же как и файл который он создает.

signature.ps1


#Находим данные о пользователи из AD
$UserName = $env:username
$Filter = "(&(objectCategory=User)(samAccountName=$UserName))"
$Searcher = New-Object System.DirectoryServices.DirectorySearcher
$Searcher.Filter = $Filter
$ADUserPath = $Searcher.FindOne()
$ADUser = $ADUserPath.GetDirectoryEntry()
$ADDisplayName = $ADUser.DisplayName
$ADEmailAddress = $ADUser.mail
$ADInfo = $ADUser.otherMailbox
$ADTitle = $ADUser.title
$ADTelePhoneNumber = $ADUser.TelephoneNumber
$ADipPhone = $ADUser.ipPhone
$ADOffice = $ADUser.physicalDeliveryOfficeName #Номер Офиса
$ADСompany = $ADUser.company
$ADOffice = $ADUser.physicalDeliveryOfficeName

############################################################################################################

$Site="http://mail.ru"
$Logo="http://mail.ru/logo.png"                    #85*85px
$Banner="http://mail.ru/banner.png"             #440*58px
$BannerSite="http://mail.ru/"
$Tel="84951234567"
$Fax="84951234567"
$Address="г. Москва, Красная площадь д. 3"

$signature = "$env:appdataThunderbirdProfiles$env:username.defaultsignature.htm" #Место куда будем сохранять

$html = '<table border="0" cellpadding="0" cellspacing="0" style="margin:0;padding:0;width:440px;"><tr><td style="font-size:14px;line-height:16px;font-weight:bold;"><span style="font-family:Verdana,Geneva,sans-serif;font-size:14px;line-height:16px;font-weight:bold;color:#333333;font-weight:bold;">'+$ADDisplayName+'</span></td></tr><tr><td style="font-size:12px;line-height:14px;"><span style="font-family:Verdana,Geneva,sans-serif;font-size:12px;line-height:14px;color:#333333;">'+$ADTitle+'</span></td></tr><tr><td style="height:7px;line-height:7px;"></td></tr><tr><td><table border="0" cellpadding="0" cellspacing="0" style="margin:0;padding:0;width:440px;border-top-style:solid;border-top-width:2px;border-bottom-style:solid;border-bottom-width:2px;border-color:#1b5cbd"><tr><td colspan="3" style="height:8px;line-height:8px;"></td></tr><tr><td style="width:100px;min-height:85px;vertical-align:middle;border-right-style:solid;border-right-width:1px;border-right-color:#333333"><a href="'+$Site+'" target="_blank"><img src="'+$Logo+'" width="85" height="85" border="0" style="display:block;" nosend="1" alt=""/></a></td><td style="width: 15px;"></td><td style="width:325px;vertical-align:top;"><table border="0" cellpadding="0" cellspacing="0" style="margin:0;padding:0;width:325px;border:0 none;"><tr><td style="font-size:12px;line-height:14px;"><span style="font-family:Verdana,Geneva,sans-serif;font-size:12px;line-height:14px;color:#333333;font-weight: bold;">Моб.: </span><a href="tel:'+$ADTelePhoneNumber+'" style="font-family:Verdana,Geneva,sans-serif;color:#333333 !important;text-decoration:none !important;font-size:12px;line-height:14px;"><span style="font-family:Verdana,Geneva,sans-serif;font-size:12px;line-height:14px;color:#333333;">'+$ADTelePhoneNumber+'</span></a></td></tr><tr><td style="font-size:12px;line-height:14px;"><span style="font-family:Verdana,Geneva,sans-serif;font-size:12px;line-height:14px;color:#333333;font-weight: bold;">Email: </span><a href="mailto:'+$ADEmailAddress+'" style="font-family:Verdana,Geneva,sans-serif;font-size:12px;line-height:14px;color:#1b5cbd !important;text-decoration:none !important;outline:none;"><span style="font-family:Verdana,Geneva,sans-serif;font-size:12px;line-height:14px;color:#1b5cbd !important;">'+$ADEmailAddress+'</span></a></td></tr><tr><td style="height:4px;line-height:4px;"></td></tr><tr><td style="font-size:12px;line-height:14px;"><span style="font-family:Verdana,Geneva,sans-serif;font-size:12px;line-height:14px;color:#333333;font-weight: bold;">'+$ADСompany+'</span></td></tr><tr><td style="font-family:Verdana,Geneva,sans-serif;font-size:12px;line-height:14px;"><span style="font-family:Verdana,Geneva,sans-serif;font-size:12px;line-height:14px;color:#333333;">Офис: </span><a href="tel:'+$Tel+'" style="font-family:Verdana,Geneva,sans-serif;color:#333333 !important;text-decoration:none !important;font-size:12px;line-height:14px;"><span style="font-family:Verdana,Geneva,sans-serif;font-size:12px;line-height:14px;color:#333333;">'+$Tel+'</span></a><span style="font-family:Verdana,Geneva,sans-serif;font-size:12px;line-height:14px;color:#333333;"> / </span><span style="font-family:Verdana,Geneva,sans-serif;font-size:12px;line-height:14px;color:#333333;">Факс: '+$Fax+'</span></td></tr><tr><td style="font-family:Verdana,Geneva,sans-serif;font-size:12px;line-height:14px;"><a href="https://yandex.ru/maps/?text='+$Address+'&l=map" style="font-family:Verdana,Geneva,sans-serif;color:#333333 !important;text-decoration:none !important;font-size:12px;line-height:14px;"><span style="font-family:Verdana,Geneva,sans-serif;font-size:12px;line-height:14px;color:#333333;">'+$Address+' Офис '+$ADOffice+'</span></a></td></tr><tr><td style="font-family:Verdana,Geneva,sans-serif;font-size:12px;line-height:14px;"><a href="'+$Site+'" style="font-family:Verdana,Geneva,sans-serif;font-size:12px;line-height:14px;color:#0043a6 !important;text-decoration:none !important;outline:none;"><span style="font-family:Verdana,Geneva,sans-serif;font-size:12px;line-height:14px;color:#0043a6 !important;text-decoration:none !important;outline:none;">'+$Site+'</span></a></td></tr></table></td></tr><tr><td colspan="3" style="height:8px;line-height:8px;"></td></tr></table></td></tr><tr><td style="height:5px;line-height:5px;"> </td></tr><tr><td><a target="_blank" href="'+$BannerSite+'" style="display:block;width:440px;height:58px;"><img src="'+$Banner+'" width="440" height="58" alt="" border="0" nosend="1"/></a></td></tr><tr style="height: 4px;"><td style="height: 4px;line-height: 4px;"></td></tr><tr><td style="font-size: 10px;line-height: 11px;"></td></tr></table><span style="font-family:Verdana,Geneva,sans-serif;color:#000000;font-size: 10px;line-height: 11px;">Информация в этом сообщении предназначена исключительно для конкретных лиц, которым она адресована. В сообщении может содержаться конфиденциальная информация, которая не может быть раскрыта или использована кем-либо, кроме адресатов. Если вы не адресат этого сообщения, то использование, переадресация, копирование или распространение содержания сообщения или его части незаконно и запрещено. Если Вы получили это сообщение ошибочно, пожалуйста, незамедлительно сообщите отправителю об этом и удалите со всем содержимым само сообщение и любые возможные его копии и приложения.</br></br>The information contained in this communication is intended solely for the use of the individual or entity to whom it is addressed and others authorized to receive it. It may contain confidential or legally privileged information. The contents may not be disclosed or used by anyone other than the addressee. If you are not the intended recipient(s), any use, disclosure, copying, distribution or any action taken or omitted to be taken in reliance on it is prohibited and may be unlawful. If you have received this communication in error please notify us immediately by responding to this email and then delete the e-mail and all attachments and any copies thereof.</span>'

echo $html | out-file $signature -encoding UTF8

У нас должно получиться 3 файла.
start.ps1 — Его мы запускаем при входе пользователя.
new_prefs.ps1 — Создает prefs.js в папке Thunderbird.
signature.ps1 — Создает подпись в почте.

В зависимости от настроек безопасности PowerShell скрипты могут не выполняться. Если видите ошибку что скрипт не имеет цифровой подписи, прошу ознакомиться с данной инструкцией для решения проблемы.

Автор: webdiez

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js