tdeioslave media: safe removal and locking are now avaiable also for

unlocked and mounted medium.

Signed-off-by: Michele Calgaro <michele.calgaro@yahoo.it>
pull/35/head
Michele Calgaro 4 years ago
parent 800775a5f1
commit 3740009719
Signed by: MicheleC
GPG Key ID: 2A75B7CA8ADED5CF

@ -311,7 +311,6 @@ TQStringVariantMap MediaManager::unlock(const TQString &uid, const TQString &pas
return result; return result;
} }
return m_halbackend->unlock(uid, password); return m_halbackend->unlock(uid, password);
#else #else
// if (!m_fstabbackend) { // if (!m_fstabbackend) {
result["errStr"] = i18n("Feature only available with HAL or TDE hardware backend"); result["errStr"] = i18n("Feature only available with HAL or TDE hardware backend");

@ -40,55 +40,25 @@
#include "dialog.h" #include "dialog.h"
#include "tdeio_media_mounthelper.h" #include "tdeio_media_mounthelper.h"
const Medium MountHelper::findMedium(const KURL &url) const Medium MountHelper::findMedium(const TQString &device)
{ {
DCOPRef mediamanager("kded", "mediamanager"); DCOPReply reply = m_mediamanager.call("properties", device);
if (!reply.isValid())
// Try filename first {
DCOPReply reply = mediamanager.call("properties", url.fileName()); m_errorStr = i18n("The TDE mediamanager is not running.\n");
if (!reply.isValid()) {
m_errorStr = i18n("The TDE mediamanager is not running.")+"\n";
return Medium(TQString::null, TQString::null, TQString::null); return Medium(TQString::null, TQString::null, TQString::null);
} }
const Medium &medium = Medium::create(reply); const Medium &medium = Medium::create(reply);
if (medium.id().isEmpty()) {
// Try full URL now
reply = mediamanager.call("properties", url.prettyURL());
if (!reply.isValid()) {
m_errorStr = i18n("Internal Error");
return Medium(TQString::null, TQString::null, TQString::null);
}
return Medium::create(reply);
} else {
return medium; return medium;
} }
}
MountHelper::MountHelper() : TDEApplication() void MountHelper::mount(const Medium &medium)
{
TDECmdLineArgs *args = TDECmdLineArgs::parsedArgs();
m_errorStr = TQString::null;
KURL url(args->url(0));
const Medium medium = findMedium(url);
if (medium.id().isEmpty())
{ {
if (m_errorStr.isEmpty()) { if (medium.id().isEmpty()) {
m_errorStr+= i18n("%1 cannot be found.").arg(url.prettyURL()); m_errorStr = i18n("Try to mount an unknown medium.");
}
errorAndExit(); errorAndExit();
} }
TQString device = medium.deviceNode(); TQString device = medium.deviceNode();
if (!medium.isMountable() && !medium.isEncrypted() && !args->isSet("e") && !args->isSet("s"))
{
m_errorStr = i18n("%1 is not a mountable or encrypted media.").arg(device);
errorAndExit();
}
if (args->isSet("m"))
{
// Mount drive
if (!medium.isMountable()) { if (!medium.isMountable()) {
m_errorStr = i18n("%1 is not a mountable media.").arg(device); m_errorStr = i18n("%1 is not a mountable media.").arg(device);
errorAndExit(); errorAndExit();
@ -98,23 +68,24 @@ MountHelper::MountHelper() : TDEApplication()
errorAndExit(); errorAndExit();
} }
DCOPRef mediamanager("kded", "mediamanager"); DCOPReply reply = m_mediamanager.call("mount", medium.id());
DCOPReply reply = mediamanager.call("mount", medium.id());
TQStringVariantMap mountResult; TQStringVariantMap mountResult;
if (reply.isValid()) { if (reply.isValid()) {
reply.get(mountResult); reply.get(mountResult);
} }
if (mountResult.contains("result") && mountResult["result"].toBool()) { if (!mountResult.contains("result") || !mountResult["result"].toBool()) {
::exit(0);
}
else {
m_errorStr = mountResult.contains("errStr") ? mountResult["errStr"].toString() : i18n("Unknown mount error."); m_errorStr = mountResult.contains("errStr") ? mountResult["errStr"].toString() : i18n("Unknown mount error.");
errorAndExit(); errorAndExit();
} }
} }
else if (args->isSet("u"))
void MountHelper::unmount(const Medium &medium)
{ {
// Unmount drive if (medium.id().isEmpty()) {
m_errorStr = i18n("Try to unmount an unknown medium.");
errorAndExit();
}
TQString device = medium.deviceNode();
if (!medium.isMountable()) { if (!medium.isMountable()) {
m_errorStr = i18n("%1 is not a mountable media.").arg(device); m_errorStr = i18n("%1 is not a mountable media.").arg(device);
errorAndExit(); errorAndExit();
@ -124,24 +95,25 @@ MountHelper::MountHelper() : TDEApplication()
errorAndExit(); errorAndExit();
} }
DCOPRef mediamanager("kded", "mediamanager"); DCOPReply reply = m_mediamanager.call("unmount", medium.id());
DCOPReply reply = mediamanager.call("unmount", medium.id());
TQStringVariantMap unmountResult; TQStringVariantMap unmountResult;
if (reply.isValid()) { if (reply.isValid()) {
reply.get(unmountResult); reply.get(unmountResult);
} }
if (unmountResult.contains("result") && unmountResult["result"].toBool()) { if (!unmountResult.contains("result") || !unmountResult["result"].toBool()) {
::exit(0);
}
else {
m_errorStr = unmountResult.contains("errStr") ? unmountResult["errStr"].toString() : i18n("Unknown unmount error."); m_errorStr = unmountResult.contains("errStr") ? unmountResult["errStr"].toString() : i18n("Unknown unmount error.");
kdDebug() << "medium unmount " << m_errorStr << endl; kdDebug() << "medium unmount " << m_errorStr << endl;
errorAndExit(); errorAndExit();
} }
} }
else if (args->isSet("k"))
void MountHelper::unlock(const Medium &medium)
{ {
// Unlock drive if (medium.id().isEmpty()) {
m_errorStr = i18n("Try to unlock an unknown medium.");
errorAndExit();
}
TQString device = medium.deviceNode();
if (!medium.isEncrypted()) if (!medium.isEncrypted())
{ {
m_errorStr = i18n("%1 is not an encrypted media.").arg(device); m_errorStr = i18n("%1 is not an encrypted media.").arg(device);
@ -160,14 +132,20 @@ MountHelper::MountHelper() : TDEApplication()
iconName = KMimeType::mimeType(mime)->icon(mime, false); iconName = KMimeType::mimeType(mime)->icon(mime, false);
} }
m_mediumId = medium.id(); m_mediumId = medium.id();
dialog = new Dialog(url.prettyURL(), iconName); m_dialog = new Dialog(device, iconName);
connect(dialog, TQT_SIGNAL(user1Clicked()), this, TQT_SLOT(slotSendPassword())); connect(m_dialog, TQT_SIGNAL(user1Clicked()), this, TQT_SLOT(slotSendPassword()));
connect(dialog, TQT_SIGNAL(cancelClicked()), this, TQT_SLOT(slotCancel())); connect(m_dialog, TQT_SIGNAL(cancelClicked()), this, TQT_SLOT(slotCancel()));
dialog->show(); m_dialog->show();
} }
else if (args->isSet("l"))
void MountHelper::lock(const Medium &medium)
{ {
// Lock drive if (medium.id().isEmpty())
{
m_errorStr = i18n("Try to lock an unknown medium.");
errorAndExit();
}
TQString device = medium.deviceNode();
if (!medium.isEncrypted()) if (!medium.isEncrypted())
{ {
m_errorStr = i18n("%1 is not an encrypted media.").arg(device); m_errorStr = i18n("%1 is not an encrypted media.").arg(device);
@ -179,103 +157,192 @@ MountHelper::MountHelper() : TDEApplication()
errorAndExit(); errorAndExit();
} }
DCOPRef mediamanager("kded", "mediamanager"); // Release children devices
DCOPReply reply = mediamanager.call("lock", medium.id()); releaseHolders(medium);
DCOPReply reply = m_mediamanager.call("lock", medium.id());
TQStringVariantMap lockResult; TQStringVariantMap lockResult;
if (reply.isValid()) { if (reply.isValid()) {
reply.get(lockResult); reply.get(lockResult);
} }
if (lockResult.contains("result") && lockResult["result"].toBool()) { if (!lockResult.contains("result") || !lockResult["result"].toBool()) {
::exit(0);
}
else {
m_errorStr = lockResult.contains("errStr") ? lockResult["errStr"].toString() : i18n("Unknown lock error."); m_errorStr = lockResult.contains("errStr") ? lockResult["errStr"].toString() : i18n("Unknown lock error.");
kdDebug() << "medium lock " << m_errorStr << endl; kdDebug() << "medium lock " << m_errorStr << endl;
errorAndExit(); errorAndExit();
} }
} }
else if (args->isSet("e"))
void MountHelper::eject(const TQString &device, bool quiet)
{ {
invokeEject(device, true); #ifdef __TDE_HAVE_TDEHWLIB
// Try TDE HW library eject first...
TDEHardwareDevices *hwdevices = TDEGlobal::hardwareDevices();
TDEGenericDevice *hwdevice = hwdevices->findByDeviceNode(device);
if (hwdevice->type() == TDEGenericDeviceType::Disk) {
TDEStorageDevice *sdevice = static_cast<TDEStorageDevice*>(hwdevice);
if (sdevice->ejectDrive()) {
// Success!
::exit(0);
} }
else if (args->isSet("s")) }
#endif
// Otherwise fall back to tdeeject
TDEProcess *proc = new TDEProcess(TQT_TQOBJECT(this));
*proc << "tdeeject";
if (quiet)
{ {
// Safely remove drive *proc << "-q";
DCOPRef mediamanager("kded", "mediamanager"); }
*proc << device;
connect(proc, TQT_SIGNAL(processExited(TDEProcess*)), this, TQT_SLOT(ejectFinished(TDEProcess*)));
proc->start();
}
/* void MountHelper::releaseHolders(const Medium &medium, bool handleThis)
* We want to call mediamanager unmount before invoking eject. That's
* because unmount would provide an informative error message in case of
* failure. However, there are cases when unmount would fail
* (supermount, slackware, see bug#116209) but eject would succeed.
* Thus if unmount fails, save unmount error message and invokeEject()
* anyway. Only if both unmount and eject fail, notify the user by
* displaying the saved error message (see ejectFinished()).
*/
TQStringVariantMap unmountResult;
if (medium.isMounted())
{ {
DCOPReply reply = mediamanager.call("unmount", medium.id()); #ifdef __TDE_HAVE_TDEHWLIB
if (reply.isValid()) { if (medium.id().isEmpty())
reply.get(unmountResult); {
if (unmountResult.contains("result") && !unmountResult["result"].toBool()) { m_errorStr = i18n("Try to release holders from an unknown medium.");
m_errorStr = unmountResult.contains("errStr") ? unmountResult["errStr"].toString() : i18n("Unknown unmount error."); return;
}
// Scan the holding devices and unmount/lock them if possible
TDEHardwareDevices *hwdevices = TDEGlobal::hardwareDevices();
TDEStorageDevice *sdevice = hwdevices->findDiskByUID(medium.id());
if (sdevice)
{
TQStringList holdingDeviceList = sdevice->holdingDevices();
for (TQStringList::Iterator holdingDevIt = holdingDeviceList.begin(); holdingDevIt != holdingDeviceList.end(); ++holdingDevIt)
{
TDEGenericDevice *hwHolderDevice = hwdevices->findBySystemPath(*holdingDevIt);
if (hwHolderDevice->type() == TDEGenericDeviceType::Disk)
{
TDEStorageDevice *holderSDevice = static_cast<TDEStorageDevice*>(hwHolderDevice);
const Medium holderMedium = findMedium(holderSDevice->deviceNode());
if (!holderMedium.id().isEmpty())
{
releaseHolders(holderMedium, true);
}
} }
} }
} }
// If this is an unlocked encrypted volume and there is no error yet, we try to lock it if (handleThis)
if (unmountResult.contains("result") && unmountResult["result"].toBool() &&
medium.isEncrypted() && !medium.clearDeviceUdi().isNull())
{ {
DCOPReply reply = mediamanager.call("lock", medium.id()); // Unmount if necessary
if (reply.isValid()) { if (medium.isMountable() && medium.isMounted())
TQStringVariantMap lockResult; {
reply.get(lockResult); unmount(medium);
if (lockResult.contains("result") && !lockResult["result"].toBool()) { }
m_errorStr = lockResult.contains("errStr") ? lockResult["errStr"].toString() : i18n("Unknown lock error."); // Lock if necessary.
if (medium.isEncrypted() && !medium.isLocked())
{
lock(medium);
} }
} }
#endif
} }
void MountHelper::safeRemoval(const Medium &medium)
{
/*
* Safely remove will performs the following tasks:
* 1) release children devices (if tdehw is available)
* 2) if the medium is mounted, unmount it
* 3) if the medium is encrypted and unlocked, lock it
* 4) invoke eject to release the medium.
* If any of the above steps fails, the procedure will interrupt and an
* error message will be displayed to the user.
*
* Note: previously eject was invoked also in case of unmount failure. This
* could lead to data loss and therefore the behaviour has been changed.
* If a user really wants to eject the medium, he needs to either unmount it
* first or invoke eject manually.
*/
if (medium.id().isEmpty())
{
m_errorStr = i18n("Try to safe remove an unknown medium.");
errorAndExit();
}
// Release children devices
releaseHolders(medium);
TQStringVariantMap opResult;
TQString device = medium.deviceNode();
// Unmount if necessary
if (medium.isMountable() && medium.isMounted())
{
unmount(medium);
}
// Lock if necessary.
if (medium.isEncrypted() && !medium.isLocked())
{
lock(medium);
}
}
MountHelper::MountHelper() : TDEApplication(), m_mediamanager("kded", "mediamanager")
{
TDECmdLineArgs *args = TDECmdLineArgs::parsedArgs();
m_errorStr = TQString::null;
const Medium medium = findMedium(args->arg(0));
if (medium.id().isEmpty())
{
if (m_errorStr.isEmpty()) { if (m_errorStr.isEmpty()) {
invokeEject(device, true); m_errorStr+= i18n("%1 cannot be found.").arg(args->arg(0));
} }
else {
errorAndExit(); errorAndExit();
} }
TQString device = medium.deviceNode();
if (!medium.isMountable() && !medium.isEncrypted() && !args->isSet("e") && !args->isSet("s"))
{
m_errorStr = i18n("%1 is not a mountable or encrypted media.").arg(device);
errorAndExit();
}
if (args->isSet("m"))
{
mount(medium);
::exit(0);
} }
else else if (args->isSet("u"))
{ {
TDECmdLineArgs::usage(); unmount(medium);
::exit(0);
} }
else if (args->isSet("k"))
{
unlock(medium);
// No call to ::exit() here because this will open up the password dialog
// ::exit() is handled in the invoked code.
} }
else if (args->isSet("l"))
void MountHelper::invokeEject(const TQString &device, bool quiet)
{ {
#ifdef __TDE_HAVE_TDEHWLIB lock(medium);
// Try TDE HW library eject first...
TDEHardwareDevices *hwdevices = TDEGlobal::hardwareDevices();
TDEGenericDevice *hwdevice = hwdevices->findByDeviceNode(device);
if (hwdevice->type() == TDEGenericDeviceType::Disk) {
TDEStorageDevice *sdevice = static_cast<TDEStorageDevice*>(hwdevice);
if (sdevice->ejectDrive()) {
// Success!
::exit(0); ::exit(0);
} }
else if (args->isSet("e"))
{
eject(device, true);
::exit(0);
} }
#endif else if (args->isSet("s"))
// Then fall back to tdeeject if needed
TDEProcess *proc = new TDEProcess(TQT_TQOBJECT(this));
*proc << "tdeeject";
if (quiet)
{ {
*proc << "-q"; safeRemoval(medium);
eject(device, true);
::exit(0);
}
else
{
TDECmdLineArgs::usage();
::exit(0);
} }
*proc << device;
connect(proc, TQT_SIGNAL(processExited(TDEProcess*)), this, TQT_SLOT(ejectFinished(TDEProcess*)));
proc->start();
} }
void MountHelper::ejectFinished(TDEProcess *proc) void MountHelper::ejectFinished(TDEProcess *proc)
@ -311,9 +378,7 @@ void MountHelper::errorAndExit()
void MountHelper::slotSendPassword() void MountHelper::slotSendPassword()
{ {
DCOPRef mediamanager("kded", "mediamanager"); DCOPReply reply = m_mediamanager.call("unlock", m_mediumId, m_dialog->getPassword());
DCOPReply reply = mediamanager.call("unlock", m_mediumId, dialog->getPassword());
TQStringVariantMap unlockResult; TQStringVariantMap unlockResult;
if (reply.isValid()) { if (reply.isValid()) {
reply.get(unlockResult); reply.get(unlockResult);

@ -37,13 +37,22 @@ public:
MountHelper(); MountHelper();
private: private:
const Medium findMedium(const KURL &url);
void invokeEject(const TQString &device, bool quiet=false);
TQString m_errorStr; TQString m_errorStr;
TQString m_mediumId; TQString m_mediumId;
Dialog *dialog; Dialog *m_dialog;
DCOPRef m_mediamanager;
const Medium findMedium(const TQString &device);
void error(); void error();
void mount(const Medium &medium);
void unmount(const Medium &medium);
void unlock(const Medium &medium);
void lock(const Medium &medium);
void eject(const TQString &device, bool quiet=false);
void safeRemoval(const Medium &medium);
void releaseHolders(const Medium &medium, bool handleThis = false);
private slots: private slots:
void slotSendPassword(); void slotSendPassword();
void slotCancel(); void slotCancel();

@ -1,5 +1,5 @@
[Desktop Entry] [Desktop Entry]
X-TDE-ServiceTypes=media/bluray_mounted,media/bluray_unmounted,media/bluray_encrypted_locked,media/cdrom_mounted,media/cdrom_unmounted,media/cdrom_encrypted_locked,media/cd-r_mounted,media/cd-r_unmounted,media/cd-r_encrypted_locked,media/cd-rw_mounted,media/cd-rw_unmounted,media/cd-rw_encrypted_locked,media/dvd_mounted,media/dvd_unmounted,media/dvd_encrypted_locked,media/audiocd,media/blankcd,media/blankdvd,media/dvdvideo,media/svcd,media/vcd X-TDE-ServiceTypes=media/bluray_mounted,media/bluray_unmounted,media/bluray_encrypted_locked,media/bluray_encrypted_unlocked,media/cdrom_mounted,media/cdrom_unmounted,media/cdrom_encrypted_locked,media/cdrom_encrypted_unlocked,media/cd-r_mounted,media/cd-r_unmounted,media/cd-r_encrypted_locked,media/cd-r_encrypted_unlocked,media/cd-rw_mounted,media/cd-rw_unmounted,media/cd-rw_encrypted_locked,media/cd-rw_encrypted_unlocked,media/dvd_mounted,media/dvd_unmounted,media/dvd_encrypted_locked,media/dvd_encrypted_unlocked,media/audiocd,media/blankbluray,media/blankcd,media/blankdvd,media/dvdvideo,media/svcd,media/vcd
Actions=MediaEject; Actions=MediaEject;
X-TDE-Priority=TopLevel X-TDE-Priority=TopLevel
X-TDE-MediaNotifierHide=true X-TDE-MediaNotifierHide=true

@ -1,5 +1,5 @@
[Desktop Entry] [Desktop Entry]
X-TDE-ServiceTypes=media/removable_encrypted_unlocked,media/hdd_encrypted_unlocked,media/cdrom_encrypted_unlocked,media/cd-rw_encrypted_unlocked,media/dvd_encrypted_unlocked X-TDE-ServiceTypes=media/bluray_encrypted_unlocked,media/cdrom_encrypted_unlocked,media/cd-r_encrypted_unlocked,media/cd-rw_encrypted_unlocked,media/dvd_encrypted_unlocked,media/hdd_encrypted_unlocked,media/removable_encrypted_unlocked
Actions=MediaLock; Actions=MediaLock;
X-TDE-Priority=TopLevel X-TDE-Priority=TopLevel
X-TDE-MediaNotifierHide=true X-TDE-MediaNotifierHide=true

@ -1,5 +1,5 @@
[Desktop Entry] [Desktop Entry]
X-TDE-ServiceTypes=media/bluray_unmounted,media/cdrom_unmounted,media/cd-r_unmounted,media/cd-rw_unmounted,media/dvd_unmounted,media/floppy5_unmounted,media/floppy_unmounted,media/hdd_unmounted,media/nfs_unmounted,media/removable_unmounted,media/smb_unmounted,media/zip_unmounted,media/camera_unmounted X-TDE-ServiceTypes=media/bluray_unmounted,media/camera_unmounted,media/cdrom_unmounted,media/cd-r_unmounted,media/cd-rw_unmounted,media/dvd_unmounted,media/floppy5_unmounted,media/floppy_unmounted,media/hdd_unmounted,media/nfs_unmounted,media/removable_unmounted,media/smb_unmounted,media/zip_unmounted,media/audiocd,media/blurayvideo,media/dvdvideo,media/svcd,media/vcd
Actions=MediaMount; Actions=MediaMount;
X-TDE-Priority=TopLevel X-TDE-Priority=TopLevel
X-TDE-MediaNotifierHide=true X-TDE-MediaNotifierHide=true

@ -1,5 +1,5 @@
[Desktop Entry] [Desktop Entry]
X-TDE-ServiceTypes=media/removable_mounted,media/removable_unmounted,media/camera_mounted,media/camera_unmounted X-TDE-ServiceTypes=media/bluray_mounted,media/bluray_unmounted,media/bluray_encrypted_locked,media/bluray_encrypted_unlocked,media/cdrom_mounted,media/cdrom_unmounted,media/cdrom_encrypted_locked,media/cdrom_encrypted_unlocked,media/cd-r_mounted,media/cd-r_unmounted,media/cd-r_encrypted_locked,media/cd-r_encrypted_unlocked,media/cd-rw_mounted,media/cd-rw_unmounted,media/cd-rw_encrypted_locked,media/cd-rw_encrypted_unlocked,media/dvd_mounted,media/dvd_unmounted,media/dvd_encrypted_locked,media/dvd_encrypted_unlocked,media/audiocd,media/blankbluray,media/blankcd,media/blankdvd,media/dvdvideo,media/svcd,media/vcd,media/camera_mounted,media/camera_unmounted,media/removable_mounted,media/removable_unmounted,media/removable_encrypted_locked,media/removable_encrypted_unlocked
Actions=MediaSafelyRemove; Actions=MediaSafelyRemove;
X-TDE-Priority=TopLevel X-TDE-Priority=TopLevel
X-TDE-MediaNotifierHide=true X-TDE-MediaNotifierHide=true

@ -1,5 +1,6 @@
[Desktop Entry] [Desktop Entry]
X-TDE-ServiceTypes=media/removable_encrypted_locked,media/hdd_encrypted_locked,media/bluray_encrypted_locked,media/cdrom_encrypted_locked,media/cd-r_encrypted_locked,media/cd-rw_encrypted_locked,media/dvd_encrypted_locked X-TDE-ServiceTypes=media/bluray_encrypted_locked,media/cdrom_encrypted_locked,media/cd-r_encrypted_locked,media/cd-rw_encrypted_locked,media/dvd_encrypted_locked,media/hdd_encrypted_locked,media/removable_encrypted_locked
Actions=MediaLock;
Actions=MediaUnlock; Actions=MediaUnlock;
X-TDE-Priority=TopLevel X-TDE-Priority=TopLevel
X-TDE-MediaNotifierHide=true X-TDE-MediaNotifierHide=true

@ -1,5 +1,5 @@
[Desktop Entry] [Desktop Entry]
X-TDE-ServiceTypes=media/removable_mounted,media/bluray_mounted,media/cdrom_mounted,media/cd-r_mounted,media/cd-rw_mounted,media/dvd_mounted,media/floppy5_mounted,media/floppy_mounted,media/hdd_mounted,media/nfs_mounted,media/smb_mounted,media/zip_mounted,media/blurayvideo,media/dvdvideo,media/svcd,media/vcd,media/camera_mounted X-TDE-ServiceTypes=media/bluray_mounted,media/camera_mounted,media/cdrom_mounted,media/cd-r_mounted,media/cd-rw_mounted,media/dvd_mounted,media/floppy5_mounted,media/floppy_mounted,media/hdd_mounted,media/nfs_mounted,media/removable_mounted,media/smb_mounted,media/zip_mounted,media/audiocd,media/blurayvideo,media/dvdvideo,media/svcd,media/vcd
Actions=MediaUnmount; Actions=MediaUnmount;
X-TDE-Priority=TopLevel X-TDE-Priority=TopLevel
X-TDE-MediaNotifierHide=true X-TDE-MediaNotifierHide=true

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-10-16 13:49+0800\n" "POT-Creation-Date: 2020-10-17 08:51+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -379,7 +379,7 @@ msgid "Safely Remove"
msgstr "" msgstr ""
#. Name #. Name
#: media/services/media_unlock.desktop:8 #: media/services/media_unlock.desktop:9
msgid "Unlock" msgid "Unlock"
msgstr "" msgstr ""

Loading…
Cancel
Save