View Javadoc

1   /*
2    *  XNap - A P2P framework and client.
3    *
4    *  See the file AUTHORS for copyright information.
5    *
6    *  This program is free software; you can redistribute it and/or modify
7    *  it under the terms of the GNU General Public License as published by
8    *  the Free Software Foundation.
9    *
10   *  This program is distributed in the hope that it will be useful,
11   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   *  GNU General Public License for more details.
14   *
15   *  You should have received a copy of the GNU General Public License
16   *  along with this program; if not, write to the Free Software
17   *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18   */
19  
20  package org.xnap.gui.plugin;
21  
22  import java.awt.Component;
23  import java.io.File;
24  import java.io.IOException;
25  
26  import javax.swing.JOptionPane;
27  
28  import org.apache.log4j.Logger;
29  import org.xnap.XNap;
30  import org.xnap.gui.Dialogs;
31  import org.xnap.gui.pkg.PackageInstallerDialog;
32  import org.xnap.loader.XNapLoader;
33  import org.xnap.pkg.PackageCommit;
34  import org.xnap.pkg.PackageInfo;
35  import org.xnap.pkg.ParseException;
36  import org.xnap.pkg.UnsatisfiedDependenciesException;
37  import org.xnap.pkg.XNapPackageManager;
38  import org.xnap.plugin.PluginInfo;
39  import org.xnap.plugin.PluginManager;
40  import org.xnap.plugin.PluginVerifier;
41  import org.xnap.util.FileHelper;
42  import org.xnap.util.Preferences;
43  import org.xnap.util.StringHelper;
44  
45  public class PluginInstallerDialog extends PackageInstallerDialog {
46  
47      //--- Constant(s) ---
48  
49      //--- Data field(s) ---
50  	private static Logger logger = Logger.getLogger(PluginInstallerDialog.class);
51  	
52      //--- Constructor(s) ---
53  
54      privatePluginInstallerDialog(PackageInfo[] packages)/package-summary.html">ong> PluginInstallerDialog(PackageInfo[] packages) 
55      {
56  		super(packages);
57      }
58  
59      //--- Method(s) ---
60  
61  	public void close()
62  	{
63  		if (!isRunning()) {
64  			dispose();
65  
66  			PackageInfo pendingCore = null;
67  
68  			boolean showRestart = false;
69  			boolean rewriteLoader = false;
70  			PackageInfo[] infos = getInstaller().getPackageInfos();
71  			for (int i = 0; i < infos.length; i++) {
72  				if (infos[i].isInstalled()) {
73  					if (infos[i].isPlugin()) {
74  						showRestart |= addPlugin(infos[i]);
75  					}
76  					else if (infos[i].isPatch()) {
77  						rewriteLoader |= addPatch(infos[i]);
78  					}
79  					else if (infos[i].isCore()) {
80  						pendingCore = infos[i];
81  					}
82  				}
83  			}
84  
85  			if (pendingCore != null) {
86  				addBase(pendingCore);
87  			}
88  			else if (rewriteLoader) {
89  				// PluginManager.getCoreClassPath > ~/.xnap/loader
90  			}
91  
92  			if (showRestart) {
93  				Dialogs.info(this,
94  							 XNap.tr("One or more plugins could not be enabled, yet. You need to restart XNap."),
95  							 XNap.tr("New Plugin Installed"));
96  				Preferences.getInstance().setUpdatePluginsOnStartup(true);
97  			}
98  		}
99  
100 		// save list of packages
101 		XNapPackageManager.getInstance().write();
102 		PluginManager.getInstance().write();
103 	}
104 
105 
106 	private void addBase(PackageInfo p)
107 	{		
108 		PluginInfo info = new PluginInfo(p.getProperties());
109 		if (resolve(info)) {
110 			PluginManager.getInstance().add(info);
111 			String[] classPath = info.getClassPath();
112 			try {
113 				String loaderPathText
114 					= "# XNap startup classpath\n"
115 					+ "Loader-Version: " + XNapLoader.VERSION + "\n"
116 					+ StringHelper.toString(classPath, "\n");
117 				FileHelper.writeText
118 					(new File(XNapLoader.LOADER_FILENAME), loaderPathText);
119 			}
120 			catch (IOException e) {
121 				Dialogs.error
122 					(this, XNap.tr("Could not write loader information: {0}.", 
123 								   e.getLocalizedMessage()));
124 				return;
125 			}
126 			Dialogs.info
127 				(this,
128 				 XNap.tr("You need to restart XNap."),
129 				 XNap.tr("New Base Package Installed"));
130 		}
131 	}
132 
133 	private boolean addPatch(PackageInfo p)
134 	{		
135 		//PluginInfo info = new PluginInfo(p.getProperties());
136 		
137 		// check if version matches
138 		// set class path
139 		// add to plugin manager
140 		return true;
141 	}
142 
143 	private boolean addPlugin(PackageInfo p)
144 	{		
145 		PluginInfo info = new PluginInfo(p.getProperties());
146 		if (resolve(info)) {
147 			if (PluginManager.getInstance().add(info)) {
148 				int response = JOptionPane.showConfirmDialog
149 					(this, 
150 					 XNap.tr("Would you like to enable the {0} plugin?",
151 							 info.getName()),
152 					 XNap.tr("New Plugin Installed"),
153 					 JOptionPane.YES_NO_OPTION);
154 				if (response == JOptionPane.YES_OPTION) {
155 					PluginHelper.enablePlugin(this, info);
156 				}
157 			}
158 			else {
159 				// the plugin was installed but already loaded
160 				return true;
161 			}
162 		}
163 		return false;
164 	}
165 
166 	public static boolean commit(Component c)
167 	{
168 		try {
169 			PackageCommit commit 
170 				= new PackageCommit(XNapPackageManager.getInstance());
171 
172 			if (!commit.isEmpty()) {
173 				PluginInstallerDialog 
174 					dialog = new PluginInstallerDialog(commit.getInstall());
175 				dialog.show(c);
176 			}
177 
178 			return true;
179 		}
180 		catch (ParseException e) {
181 			Dialogs.error(c, e.getLocalizedMessage());
182 		}
183 		catch (UnsatisfiedDependenciesException e) {
184 			String message
185 				= XNap.tr("Unsatisfied dependency: Missing {0}.", 
186 						  e.getLocalizedMessage());
187 			Dialogs.error(c, message);
188 		}
189 
190 		return false;
191 	}
192 
193 	public boolean resolve(PluginInfo info)
194 	{
195 		try {
196 			info.setClassPath(PluginManager.resolveClassPath(info));
197 		}
198 		catch (Exception e) {
199 			Dialogs.error
200 				(this, XNap.tr("Error while resolving class path for {0}: {1}.",
201 							   info.getName(),
202 							   e.getLocalizedMessage()));
203 			return false;
204 		}
205 
206 		// FIX: disabled for now
207  		//if (!validate(info)) {
208  		//	return false;
209  		//}
210 
211 		return true;
212 	}
213 
214 	public boolean validate(PluginInfo info)
215 	{
216 		try {
217 			PluginVerifier pv = new PluginVerifier(info);
218 			if (!pv.isValid()) {
219 				// someone has added/modified signed code
220 				logger.error(info.getName()+" contains invalid jars!");
221 				Dialogs.error(this, XNap.tr("Security Error: {0} will be disabled because it has a broken signature", info.getName()));
222 				return false;
223 			}
224 			// plugin is valid
225 			logger.info(info.getName()+(pv.isSigned()?" is signed by "+pv.getSigners():"is not signed"));
226 			if (!pv.isTrustedByXNap()) {
227 				logger.warn(info.getName()+" contains untrusted code!");
228 				//return false;
229 			}
230 		}
231 		catch (IOException e) {
232 			logger.error("Plugin validation failed", e);
233 			return false;
234 		}
235 
236 		return true;
237 	}
238 
239     // --- Inner Class(es) ---
240 }