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.pkg;
21  
22  import java.io.File;
23  import java.io.FileOutputStream;
24  import java.io.IOException;
25  import java.io.InputStream;
26  import java.io.OutputStream;
27  import java.net.URL;
28  import java.util.Enumeration;
29  import java.util.zip.ZipEntry;
30  import java.util.zip.ZipFile;
31  
32  import org.apache.log4j.Logger;
33  import org.xnap.XNap;
34  import org.xnap.loader.XNapClassLoader;
35  import org.xnap.net.NetHelper;
36  import org.xnap.util.FileHelper;
37  import org.xnap.util.Formatter;
38  import org.xnap.util.StringHelper;
39  
40  public class PackageInstaller {
41  
42      //--- Constant(s) ---
43  
44      //--- Data field(s) ---
45  
46      private static Logger logger = Logger.getLogger(PackageInstaller.class);
47  
48      privatePackageInfo[] packages/package-summary.html">ong> PackageInfo[] packages;
49      private DownloadRunner dlRunner;
50  
51  	private PackageInstallerListener listener;
52  	private boolean successful = false;
53  
54      //--- Constructor(s) ---
55  
56      publicPackageInstaller(PackageInfo[] packages,/package-summary.html">ong> PackageInstaller(PackageInfo[] packages,
57  							PackageInstallerListener listener) 
58      {
59  		this.packages = packages;
60  		this.listener = listener;
61  
62  		int totalSize = 0;
63  		for (int i = 0; i < packages.length; i++) {
64  			totalSize += packages[i].getSize();
65  		}
66  		listener.setTotalMinimum(0);
67  		listener.setTotalValue(0);
68  		listener.setTotalMaximum(totalSize);
69  		listener.setText(XNap.tr("Need to download {0} in {1} packages.", 
70  								 Formatter.formatSize(totalSize),
71  								 new Integer(packages.length)));
72  
73      }
74  
75      //--- Method(s) ---
76  
77  	public PackageInfo[] getPackageInfos()
78  	{
79  		returng> packages;
80  	}
81  
82  	public boolean isSuccessful()
83  	{
84  		return successful;
85  	}
86      
87  	public void start()
88  	{
89  		dlRunner = new DownloadRunner();
90  		Thread t = new Thread(dlRunner, "PluginDownload");
91  		t.start();
92  	}
93  
94      // --- Inner Class(es) ---
95  
96      private class DownloadRunner implements Runnable
97      {
98  
99  		private long totalTransferred = 0;
100 
101 		public File createTempFile(String filename) throws IOException
102 		{
103 			File pathes[] = XNapClassLoader.getSearchPath();
104 			for (int i = 0; i < pathes.length; i++) {
105 				File f = new File(pathes[i], filename);
106 				logger.debug("trying " + f.getAbsolutePath());
107 				if (f.exists() && f.canWrite()) {
108 					return f;
109 				}
110 				else if ((f.getParentFile().exists() 
111 						  || f.getParentFile().mkdirs())
112 						 && f.createNewFile()) {
113 					return f;
114 				}
115 			}
116 
117 			return null;
118 		}
119 
120 		public File download(InputStream in, String filename) 
121 			throws IOException
122 		{
123 			File tempFile = createTempFile(filename + ".part");
124 			if (tempFile == null) {
125 				throw new IOException(XNap.tr("Could not create file."));
126 			}
127 
128 			File target = new File(tempFile.getParentFile(), filename);
129 			if (target.exists() && !target.delete()) {
130 				throw new IOException
131 					(XNap.tr("A file with this name already exists."));
132 			}
133 
134 			OutputStream out = null;
135 			try {
136 				out = new FileOutputStream(tempFile.getAbsolutePath(), false);
137 		
138 				listener.setText(XNap.tr("Downloading {0}", filename));
139 
140 				long transferred = 0;
141 				int c = 0;
142 				byte[] b = new byte[500];
143 				while (c != -1 && !listener.isCancelled()) {
144 					c = in.read(b);
145 					if (c != -1) {
146 						out.write(b, 0, c);
147 
148 						transferred += c;
149 						totalTransferred += c;
150 						
151 						listener.setValue((int)transferred);
152 						listener.setTotalValue((int)totalTransferred);
153 					}
154 				}
155 				out.close();
156 			}
157 			catch (IOException e) {
158 				if (out != null) {
159 					try {
160 						out.close();
161 						tempFile.delete();
162 					}
163 					catch (IOException e2) {
164 					}
165 				}
166 				throw e;
167 			}
168 
169 			if (!tempFile.renameTo(target)) {
170 				throw new IOException(XNap.tr("Could not rename file."));
171 			}
172 
173 			return target;
174 		}
175 
176 		public File downloadPackage(PackageInfo info)
177 		{
178 			String filename = info.getDownloadFilename();
179 			if (filename != null) {
180 				File file = new File(filename);
181 				if (file.exists() && file.length() == info.getSize()) {
182 					return file;
183 				}
184 			}
185 
186 			String[] urls = info.getDownloadURLs();
187 
188 			if (/*(info.isPlugin() || info.isBase())
189 				  && */(urls.length > 0 
190 					&& (urls[0].indexOf("sf.net") > 0
191 						|| urls[0].indexOf("sourceforge.net") > 0))) {
192 				
193 				// request sf download url to make use of download counters
194 				try {
195 					URL url = new URL("http://prdownloads.sourceforge.net/xnap/" 
196 									  + info.getFilename() + "?use_mirror=unc");
197 					InputStream in = url.openStream();
198 					try {
199 						in.read();
200 					}
201 					finally {
202 						in.close();
203 					}					
204 				}
205 				catch (IOException e) {
206 				}
207 			}
208 
209 			for (int i = 0; i < urls.length; i++) {
210 				String location = urls[i] + info.getFilename();
211 
212 				listener.setText(XNap.tr("Getting {0}", location));
213 				listener.setMinimum(0);
214 				listener.setValue(0);
215 				listener.setMaximum((int)info.getSize());
216 
217 				long value = totalTransferred;
218 				try {				
219 					URL url = new URL(location);
220 
221 					// make sure filename does not contain a '/'
222 					filename = StringHelper.lastToken(info.getFilename(), "/");
223 					if (filename.length() == 0) {
224 						filename = info.getFilename();
225 					}
226 
227 					return download(url.openStream(), filename);
228 				}
229 				catch (IOException e) {
230 					// reset total value
231 					listener.setTotalValue((int)value);
232 					logger.info("Download failed", e);
233 				}
234 			}
235 			
236 			// download failed from all locations
237 			return null;
238 		}
239 
240 		public String unzip(File file) throws IOException
241 		{
242 			listener.setText(XNap.tr("Unzipping {0}", file.getName()));
243 
244 			String controlPath = null;
245 			File base = file.getParentFile();
246 			ZipFile zip = new ZipFile(file);
247 
248 			int i = 0;
249 			for (Enumeration it = zip.entries(); 
250 				 it.hasMoreElements(); i++) {
251 				ZipEntry entry = (ZipEntry)it.nextElement();
252 				File target = new File(base.getAbsolutePath()
253 									   + File.separatorChar + entry.getName());
254 				if (entry.isDirectory()) {
255 					logger.debug("creating directory: "
256 								 + target.getAbsolutePath());
257 					target.mkdirs();
258 				}
259 				else {
260 					logger.debug("unzipping file: "
261 								 + target.getAbsolutePath());
262 
263 					InputStream in = zip.getInputStream(entry);
264 					try {
265 						FileHelper.copy(in, new FileOutputStream(target));
266 					}
267 					finally {
268 						try {
269 							in.close();
270 						}
271 						catch (IOException e) {
272 						}
273 					}
274 
275 					if (target.getName().equals("control")) {
276 						controlPath = target.getParentFile().getAbsolutePath();
277 					}
278 				}
279 			}
280 			file.delete();
281 
282 			return controlPath;
283 		}
284 
285 		public void run()
286 		{
287 			try {
288 				for (int i = 0; i < packages.length 
289 						 && !listener.isCancelled(); i++) {
290 
291 					File target = downloadPackage(packages[i]);
292 					if (target != null) {
293 						try {
294 							String path = unzip(target);
295 							if (path == null) {
296 								throw new IOException
297 									(XNap.tr("Invalid package, control file is missing."));
298 							}
299 						
300 							packages[i].getProperties().setProperty
301 								("Control-Path", path);
302 						}
303 						catch (IOException e) {
304 							logger.debug("Error unziping plugin", e);
305 							listener.setText(NetHelper.getErrorMessage(e));
306 							return;
307 						}
308 
309 						// FIX: check md5 sum
310 
311 						// remember successful download, acctually the
312 						// package is already unzipped
313 						packages[i].setDownloadFilename
314 							(target.getAbsolutePath());
315 					}
316 					else {
317 						listener.setText
318 							(XNap.tr("Unable to download package {0}.",
319 									 packages[i].getName()));
320 						return;
321 					}
322 				}
323 				
324 				successful = !listener.isCancelled();
325 				if (successful) {
326 					for (int i = 0; i < packages.length; i++) {
327 						packages[i].setInstalled(true);
328 						File file 
329 							= new File(packages[i].getDownloadFilename());
330 						file.delete();
331 					}
332 					listener.setText(XNap.tr("Finished successfully."));
333 				}
334 				else {
335 					listener.setText(XNap.tr("Cancelled by user."));
336 				}
337 
338 				listener.setPercent(1.0);
339 				listener.setTotalPercent(1.0);
340 			}
341 			finally {
342 				listener.done();
343 			}
344 		}
345     }
346 }