Skip to content

Commit

Permalink
[#1548] Install plugins from remote repository
Browse files Browse the repository at this point in the history
Signed-off-by: Hiroshi Miura <miurahr@linux.com>
  • Loading branch information
miurahr committed Apr 13, 2022
1 parent f3b2dae commit d7c0c0a
Show file tree
Hide file tree
Showing 15 changed files with 1,185 additions and 149 deletions.
40 changes: 40 additions & 0 deletions docs_devel/HowToCreateFilterPlugin.md
Original file line number Diff line number Diff line change
Expand Up @@ -347,3 +347,43 @@ If you really need to debug in the context of a running OmegaT instance (for som
more relevant), you can 'run' `org.omegat.Main`. Make sure all dependent 3rd party libraries are in the classpath.
Since you did not compile a .jar file, you have to make sure there is a correct META-INF/MANIFEST.MF file
(which is missing if you rely on e.g. maven-jar plugin to generate it for you)

# register your project to plugins database

To register your plugin to plugins database and support remote installation, you need to register your plugin manifest
to database project.
You can visit database repository at
https://github.com/omegat-org/omegat-plugins

There is following directory structure;

manifests/
omegat-browser/
MANIFEST.MF
omegat-plugins-epwing/
MANIFEST.MF
omegat-plugins-A/
MANIFEST.MF
omegat-B/
MANIFEST.MF

You can fork project and add directory that have your plugin name, and add plugin MANIFEST.MF, then
send Pull-Request to the project.
These MANIFESTs are bundled into single release file which users OmegaT download.
It is automatically generated through continous integration feature when your Pull-Request merged.

You can check a resulted file at web page https://github.com/omegat-org/omegat-plugins/releases/
and the file URL is
https://github.com/omegat-org/omegat-plugins/releases/download/continuous-release/plugins.MF

You need to add additional information to allow OmegaT to download and validate the plugin jar.

| Attribute | Manifest entry |
|-------------------|--------------------------------------------------|
| Download URL | Plugin-Download-Url |
| SHA256Sum | Plugin-Sha256Sum |
| Jar Filename | Plugin-Jar-Filename |

Download URL should be an URL of your plugin jar file. OmegaT will validate downloaded binary
with SHA256Sum specified. OmegaT will store the plugin into users plugin directory with a filename
specified by Plugin-Jar-Filename parameter.
29 changes: 19 additions & 10 deletions src/org/omegat/Bundle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2880,16 +2880,6 @@ PREFERENCES_BUTTON_RESET=Restore &Defaults
PREFERENCES_WARNING_NEEDS_RESTART=Restart OmegaT to apply these changes
PREFERENCES_WARNING_NEEDS_RELOAD=Reload the current project to apply these changes

PREFS_TITLE_PLUGINS=Plugins
PREFS_PLUGINS_BROWSE_ONLINE=&Browse Online
PREFS_PLUGINS_AVAILABLE_ONLINE=There are many plugins available for OmegaT online.
PREFS_PLUGINS_LIST=Installed plugins:
PREFS_PLUGINS_COL_CLASS=Class
PREFS_PLUGINS_COL_NAME=Name
PREFS_PLUGINS_COL_VERSION=Version
PREFS_PLUGINS_COL_AUTHOR=Author
PREFS_PLUGINS_COL_DESCRIPTION=Description

PREFS_TITLE_MACHINE_TRANSLATION=Machine Translation
PREFS_MT_CONFIGURE_BUTTON=Con&figure
PREFS_MT_AUTO_FETCH=&Automatically fetch translations
Expand Down Expand Up @@ -3029,3 +3019,22 @@ GPG_EXTERNAL_SIGNER_SKIP_NOT_ACCESSIBLE_PATH=Looking for a GPG executable: path
GPG_EXTERNAL_SIGNER_SIGNING_CANCELED=User canceled signing (no passphrase provided)
GPG_EXTERNAL_SIGNER_GPG_NOT_FOUND=No GPG executable found; set it in the Git preferences in section "Committing", or define it in the git config gpg.program
GPG_EXTERNAL_SIGNER_NO_SIGNATURE=External GPG did not return a valid signature; got: {0}

# org.omegat.gui.dialogs.PluginInstallerDialog
PREFS_TITLE_PLUGINS=Plugins
PREFS_PLUGINS_INSTALL_FROM_DISK=Install from disk
PREFS_PLUGINS_TITLE_CONFIRM_INSTALLATION=Confirm instllation of plugin file
PREFS_PLUGINS_CONFIRM_UPGRADE=Upgrade plugin {0} from {1} to {2}
PREFS_PLUGINS_CONFIRM_INSTALL=Install plugin {0} {1}
PREFS_PLUGINS_INSTALLATION_FAILED=Plugin installation failed.
PREFS_PLUGINS_DETAILS_explanation=Show plugin details
PREFS_PLUGINS_COL_STAT=Status
PREFS_PLUGINS_COL_CATEGORY=Category
PREFS_PLUGINS_COL_NAME=Name
PREFS_PLUGINS_COL_VERSION=Version
PREFS_PLUGINS_UPGRADE=Upgrade
PREFS_PLUGINS_INSTALL=Install
PREFS_PLUGINS_BUNDLED=Bundled
PREFS_PLUGINS_UPTODATE=Up to date
PREFS_PLUGINS_UPGRADABLE=Upgradable
PREFS_PLUGINS_NEW=New
71 changes: 67 additions & 4 deletions src/org/omegat/core/data/PluginInformation.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

package org.omegat.core.data;

import java.io.File;
import java.net.URL;
import java.util.Properties;
import java.util.jar.Attributes;
Expand All @@ -43,6 +44,8 @@ public class PluginInformation {
public enum Status {
INSTALLED,
BUNDLED,
UPGRADABLE,
UNINSTALLED,
}

private final String className;
Expand All @@ -54,10 +57,16 @@ public enum Status {
private final String link;
private final URL url;
private final Status status;
// for manage and install
private final String remoteJarFileUrl;
private final String jarFilename;
private final String sha256Sum;


/* The class is recommend to build from builder. */
private PluginInformation(String className, String name, String version, String author, String description,
PluginUtils.PluginType category, String link, URL url, Status status) {
PluginUtils.PluginType category, String link, URL url, Status status,
String remoteJarFileUrl, String jarFilename, String sha256Sum) {
this.className = className;
this.name = name;
this.version = version;
Expand All @@ -67,6 +76,9 @@ private PluginInformation(String className, String name, String version, String
this.link = link;
this.url = url;
this.status = status;
this.remoteJarFileUrl = remoteJarFileUrl;
this.jarFilename = jarFilename;
this.sha256Sum = sha256Sum;
}

/**
Expand Down Expand Up @@ -132,6 +144,26 @@ public final boolean isBundled() {
return status == Status.BUNDLED;
}

public Status getStatus() {
return status;
}

public File getJarFile() {
return new File(url.getPath().substring(5, url.getPath().indexOf("!")));
}

public String getRemoteJarFileUrl() {
return remoteJarFileUrl;
}

public String getJarFilename() {
return jarFilename;
}

public String getSha256Sum() {
return sha256Sum;
}

/**
* @return string expression of PluginInformation class.
*/
Expand Down Expand Up @@ -191,7 +223,6 @@ public boolean equals(Object obj) {
return true;
}


/**
* Builder class.
*/
Expand All @@ -209,13 +240,24 @@ public static final class Builder {
private static final String BUNDLE_VERSION = "Bundle-Version";
private static final String BUNDLE_NAME = "Bundle-Name";
private static final String BUILT_BY = "Built-By";
private static final String PLUGIN_JAR_URL = "Plugin-Download-Url";
private static final String PLUGIN_JAR_FILENAME = "Plugin-Jar-Filename";
private static final String PLUGIN_SHA256SUM = "Plugin-Sha256Sum";


/**
* Disable default constructor.
*/
private Builder() {
}

public static PluginInformation copy(final PluginInformation info, final Status status) {
return new PluginInformation(info.getClassName(), info.getName(), info.getVersion(),
info.getAuthor(), info.getDescription(), info.getCategory(), info.getLink(),
info.getUrl(), status, info.getRemoteJarFileUrl(), info.getJarFilename(),
info.getSha256Sum());
}

/**
* Build PluginInformation from Manifest attributes.
* @param className Plugin class name.
Expand All @@ -231,10 +273,31 @@ public static PluginInformation fromManifest(final String className, final Manif
if (attrs == null) {
attrs = manifest.getMainAttributes();
}
String remoteJarFileUrl = attrs.getValue(PLUGIN_JAR_URL);
return new PluginInformation(className, findName(className, attrs),
findVersion(attrs, mainAttrs), findAuthor(mainAttrs), attrs.getValue(PLUGIN_DESCRIPTION),
PluginUtils.PluginType.getTypeByValue(findCategoryKey(attrs)),
attrs.getValue(PLUGIN_LINK), mu, status);
attrs.getValue(PLUGIN_LINK), mu, status, remoteJarFileUrl,
getJarFilename(attrs, remoteJarFileUrl), attrs.getValue(PLUGIN_SHA256SUM));
}

private static String getJarFilename(Attributes attrs, String remoteJarFileUrl) {
String attrsName = attrs.getValue(PLUGIN_JAR_FILENAME);
if (attrsName != null) {
return attrsName;
}
if (attrs.getValue(PLUGIN_JAR_URL) != null) {
int from = remoteJarFileUrl.lastIndexOf("/");
int to = remoteJarFileUrl.indexOf("?");
if (from != -1) {
if (to == -1) {
return remoteJarFileUrl.substring(from + 1);
} else {
return remoteJarFileUrl.substring(from + 1, to);
}
}
}
return null;
}

/**
Expand All @@ -251,7 +314,7 @@ public static PluginInformation fromProperties(String className, Properties prop
return new PluginInformation(className, findName(className, null),
null, null, null,
PluginUtils.PluginType.getTypeByValue(key),
null, mu, status);
null, mu, status, null, null, null);
}

private static String findCategoryKey(Attributes attrs) {
Expand Down
115 changes: 115 additions & 0 deletions src/org/omegat/core/threads/PluginDownloadThread.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/**************************************************************************
OmegaT - Computer Assisted Translation (CAT) tool
with fuzzy matching, translation memory, keyword search,
glossaries, and translation leveraging into updated projects.
Copyright (C) 2021 Hiroshi Miura
Home page: http://www.omegat.org/
Support center: https://omegat.org/support
This file is part of OmegaT.
OmegaT is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OmegaT is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
**************************************************************************/

package org.omegat.core.threads;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Formatter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;

import org.omegat.util.PluginInstaller;
import org.omegat.util.HttpConnectionUtils;
import org.omegat.util.Log;

public class PluginDownloadThread extends LongProcessThread {

private final URL url;
private final String archive;
private final String checksum;
private final HashMap<String, String> headers = new HashMap<>();
private static final Set<String> mimetype = new HashSet<>();

static {
mimetype.add("application/octet-stream");
mimetype.add("application/java-archive");
}

public PluginDownloadThread(URL url, String sha256Sum, String filename) throws UnsupportedEncodingException {
this.url = url;
this.checksum = sha256Sum;
this.archive = filename;
}

@Override
public void run() {
try {
Path temporaryDir = Files.createTempDirectory("omegat");
File temporaryFilePath = new File(temporaryDir.toFile(), archive);
temporaryDir.toFile().deleteOnExit();
Log.log("Start downloading from " + url.toString());
boolean result = HttpConnectionUtils.downloadBinaryFile(url, headers, mimetype, temporaryFilePath);
if (!result) {
Log.log("Failed to download plugin file.");
} else if (!checksum.equals(calculateSha256(temporaryFilePath))) {
Log.log("Checksum error of plugin file.");
} else {
PluginInstaller.install(temporaryFilePath, true);
}
} catch (IOException | NoSuchAlgorithmException e) {
e.printStackTrace();
}
}

private String calculateSha256(final File targetFilePath) throws NoSuchAlgorithmException {
MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
sha256.reset();

byte[] buffer = new byte[8192];
try (InputStream in = new BufferedInputStream(new FileInputStream(targetFilePath))) {
while (true) {
int len = in.read(buffer);
if (len < 0) {
break;
}
sha256.update(buffer, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
}

// out as hex
Formatter formatter = new Formatter();
try {
for (byte b : sha256.digest()) {
formatter.format("%02x", b);
}
return formatter.toString();
} finally {
formatter.close();
}
}
}
1 change: 1 addition & 0 deletions src/org/omegat/filters2/master/PluginUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ public enum PluginType {
GLOSSARY("glossary"),
DICTIONARY("dictionary"),
MISCELLANEOUS("miscellaneous"),
THEME("theme"),
UNKNOWN("Undefined");

private final String typeValue;
Expand Down
Loading

0 comments on commit d7c0c0a

Please sign in to comment.