Advertisement
Guest User

updater

a guest
Aug 8th, 2013
158
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 22.85 KB | None | 0 0
  1. package test;
  2.  
  3. import java.io.BufferedInputStream;
  4. import java.io.BufferedOutputStream;
  5. import java.io.BufferedReader;
  6. import java.io.File;
  7. import java.io.FileOutputStream;
  8. import java.io.IOException;
  9. import java.io.InputStream;
  10. import java.io.InputStreamReader;
  11. import java.net.MalformedURLException;
  12. import java.net.URL;
  13. import java.net.URLConnection;
  14. import java.util.Enumeration;
  15. import java.util.zip.ZipEntry;
  16. import java.util.zip.ZipFile;
  17.  
  18. import javax.xml.stream.XMLEventReader;
  19. import javax.xml.stream.XMLInputFactory;
  20. import javax.xml.stream.XMLStreamException;
  21. import javax.xml.stream.events.XMLEvent;
  22.  
  23. import org.bukkit.command.Command;
  24. import org.bukkit.command.CommandExecutor;
  25. import org.bukkit.command.CommandSender;
  26. import org.bukkit.command.PluginCommand;
  27. import org.bukkit.configuration.file.YamlConfiguration;
  28. import org.bukkit.entity.Player;
  29. import org.bukkit.event.EventHandler;
  30. import org.bukkit.event.EventPriority;
  31. import org.bukkit.event.Listener;
  32. import org.bukkit.event.player.PlayerCommandPreprocessEvent;
  33. import org.bukkit.event.player.PlayerJoinEvent;
  34. import org.bukkit.plugin.Plugin;
  35.  
  36. import test.Updater.UpdateType;
  37.  
  38. public class Updater implements Listener
  39. {
  40.     private Plugin plugin;
  41.     private UpdateType type;
  42.     private String versionTitle;
  43.     private String versionLink;
  44.     private long totalSize; // Holds the total size of the file
  45.     //private double downloadedSize; TODO: Holds the number of bytes downloaded
  46.     private int sizeLine; // Used for detecting file size
  47.     private int multiplier; // Used for determining when to broadcast download updates
  48.     private boolean announce; // Whether to announce file downloads
  49.     private URL url; // Connecting to RSS
  50.     private File file; // The plugin's file
  51.     private Thread thread; // Updater thread
  52.     private static final String DBOUrl = "http://dev.bukkit.org/server-mods/"; // Slugs will be appended to this to get to the project's RSS feed
  53.     private String [] noUpdateTag = {"-DEV","-PRE","-SNAPSHOT"}; // If the version number contains one of these, don't update.
  54.     private static final int BYTE_SIZE = 1024; // Used for downloading files
  55.     private String updateFolder = YamlConfiguration.loadConfiguration(new File("bukkit.yml")).getString("settings.update-folder"); // The folder that downloads will be placed in
  56.     private Updater.UpdateResult result = Updater.UpdateResult.SUCCESS; // Used for determining the outcome of the update process
  57.     // Strings for reading RSS
  58.     private static final String TITLE = "title";
  59.     private static final String LINK = "link";
  60.     private static final String ITEM = "item";
  61.     private String permission;
  62.     private static boolean isoutdated= false;
  63.  
  64.     /**
  65. * Gives the dev the result of the update process. Can be obtained by called getResult().
  66. */
  67.     public enum UpdateResult
  68.     {
  69.         /**
  70. * The updater found an update, and has readied it to be loaded the next time the server restarts/reloads.
  71. */
  72.         SUCCESS,
  73.         /**
  74. * The updater did not find an update, and nothing was downloaded.
  75. */
  76.         NO_UPDATE,
  77.         /**
  78. * The updater found an update, but was unable to download it.
  79. */
  80.         FAIL_DOWNLOAD,
  81.         /**
  82. * For some reason, the updater was unable to contact dev.bukkit.org to download the file.
  83. */
  84.         FAIL_DBO,
  85.         /**
  86. * When running the version check, the file on DBO did not contain the a version in the format 'vVersion' such as 'v1.0'.
  87. */
  88.         FAIL_NOVERSION,
  89.         /**
  90. * The slug provided by the plugin running the updater was invalid and doesn't exist on DBO.
  91. */
  92.         FAIL_BADSLUG,
  93.         /**
  94. * The updater found an update, but because of the UpdateType being set to NO_DOWNLOAD, it wasn't downloaded.
  95. */
  96.         UPDATE_AVAILABLE
  97.     }
  98.  
  99.     /**
  100. * Allows the dev to specify the type of update that will be run.
  101. */
  102.     public enum UpdateType
  103.     {
  104.         /**
  105. * Run a version check, and then if the file is out of date, download the newest version.
  106. */
  107.         DEFAULT,
  108.         /**
  109. * Don't run a version check, just find the latest update and download it.
  110. */
  111.         NO_VERSION_CHECK,
  112.         /**
  113. * Get information about the version and the download size, but don't actually download anything.
  114. */
  115.         NO_DOWNLOAD
  116.     }
  117.  
  118.     /**
  119. * Initialize the updater
  120. *
  121. * @param plugin
  122. * The plugin that is checking for an update.
  123. * @param slug
  124. * The dev.bukkit.org slug of the project (http://dev.bukkit.org/server-mods/SLUG_IS_HERE)
  125. * @param file
  126. * The file that the plugin is running from, get this by doing this.getFile() from within your main class.
  127. * @param type
  128. * Specify the type of update this will be. See {@link UpdateType}
  129. * @param permission
  130. * Permission for the player who can update and get an Update
  131. * @param announce
  132. * True if the program should announce the progress of new updates in console
  133. */
  134.     public Updater(Plugin plugin, String slug, File file, UpdateType type,String permission, boolean announce)
  135.     {
  136.         this.plugin = plugin;
  137.         this.type = type;
  138.         this.announce = announce;
  139.         this.file = file;
  140.         this.permission = permission;
  141.         plugin.getServer().getPluginManager().registerEvents(this, plugin);
  142.         try
  143.         {
  144.             // Obtain the results of the project's file feed
  145.             url = new URL(DBOUrl + slug + "/files.rss");
  146.         }
  147.         catch (MalformedURLException ex)
  148.         {
  149.             // Invalid slug
  150.             plugin.getLogger().warning("The author of this plugin (" + plugin.getDescription().getAuthors().get(0) + ") has misconfigured their Auto Update system");
  151.             plugin.getLogger().warning("The project slug given ('" + slug + "') is invalid. Please nag the author about this.");
  152.             result = Updater.UpdateResult.FAIL_BADSLUG; // Bad slug! Bad!
  153.         }
  154.         thread = new Thread(new UpdateRunnable());
  155.         thread.start();
  156.     }
  157.  
  158.  
  159.     /**
  160. * Get the result of the update process.
  161. */
  162.     public Updater.UpdateResult getResult()
  163.     {
  164.         waitForThread();
  165.         return result;
  166.     }
  167.  
  168.     /**
  169. * Get the total bytes of the file (can only be used after running a version check or a normal run).
  170. */
  171.     public long getFileSize()
  172.     {
  173.         waitForThread();
  174.         return totalSize;
  175.     }
  176.  
  177.     /**
  178. * Get the version string latest file avaliable online.
  179. */
  180.     public String getLatestVersionString()
  181.     {
  182.         waitForThread();
  183.         return versionTitle;
  184.     }
  185.  
  186.     /**
  187. * As the result of Updater output depends on the thread's completion, it is necessary to wait for the thread to finish
  188. * before alloowing anyone to check the result.
  189. */
  190.     public void waitForThread() {
  191.         if(thread.isAlive()) {
  192.             try {
  193.                 thread.join();
  194.             } catch (InterruptedException e) {
  195.                 e.printStackTrace();
  196.             }
  197.         }
  198.     }
  199.  
  200.     /**
  201. * Save an update from dev.bukkit.org into the server's update folder.
  202. */
  203.     private void saveFile(File folder, String file, String u)
  204.     {
  205.         if(!folder.exists())
  206.         {
  207.             folder.mkdir();
  208.         }
  209.         BufferedInputStream in = null;
  210.         FileOutputStream fout = null;
  211.         try
  212.         {
  213.             // Download the file
  214.             URL url = new URL(u);
  215.             int fileLength = url.openConnection().getContentLength();
  216.             in = new BufferedInputStream(url.openStream());
  217.             fout = new FileOutputStream(folder.getAbsolutePath() + "/" + file);
  218.  
  219.             byte[] data = new byte[BYTE_SIZE];
  220.             int count;
  221.             if(announce) plugin.getLogger().info("About to download a new update: " + versionTitle);
  222.             long downloaded = 0;
  223.             while ((count = in.read(data, 0, BYTE_SIZE)) != -1)
  224.             {
  225.                 downloaded += count;
  226.                 fout.write(data, 0, count);
  227.                 int percent = (int) (downloaded * 100 / fileLength);
  228.                 if(announce & (percent % 10 == 0))
  229.                 {
  230.                     plugin.getLogger().info("Downloading update: " + percent + "% of " + fileLength + " bytes.");
  231.                 }
  232.             }
  233.             //Just a quick check to make sure we didn't leave any files from last time...
  234.             for(File xFile : new File("plugins/" + updateFolder).listFiles())
  235.             {
  236.                 if(xFile.getName().endsWith(".zip"))
  237.                 {
  238.                     xFile.delete();
  239.                 }
  240.             }
  241.             // Check to see if it's a zip file, if it is, unzip it.
  242.             File dFile = new File(folder.getAbsolutePath() + "/" + file);
  243.             if(dFile.getName().endsWith(".zip"))
  244.             {
  245.                 // Unzip
  246.                 unzip(dFile.getCanonicalPath());
  247.             }
  248.             if(announce) plugin.getLogger().info("Finished updating.");
  249.         }
  250.         catch (Exception ex)
  251.         {
  252.             plugin.getLogger().warning("The auto-updater tried to download a new update, but was unsuccessful.");
  253.             result = Updater.UpdateResult.FAIL_DOWNLOAD;
  254.         }
  255.         finally
  256.         {
  257.             try
  258.             {
  259.                 if (in != null)
  260.                 {
  261.                     in.close();
  262.                 }
  263.                 if (fout != null)
  264.                 {
  265.                     fout.close();
  266.                 }
  267.             }
  268.             catch (Exception ex)
  269.             {
  270.             }
  271.         }
  272.     }
  273.  
  274.     /**
  275. * Part of Zip-File-Extractor, modified by H31IX for use with Bukkit
  276. */
  277.     private void unzip(String file)
  278.     {
  279.         try
  280.         {
  281.             File fSourceZip = new File(file);
  282.             String zipPath = file.substring(0, file.length()-4);
  283.             ZipFile zipFile = new ZipFile(fSourceZip);
  284.             Enumeration<? extends ZipEntry> e = zipFile.entries();
  285.             while(e.hasMoreElements())
  286.             {
  287.                 ZipEntry entry = (ZipEntry)e.nextElement();
  288.                 File destinationFilePath = new File(zipPath,entry.getName());
  289.                 destinationFilePath.getParentFile().mkdirs();
  290.                 if(entry.isDirectory())
  291.                 {
  292.                     continue;
  293.                 }
  294.                 else
  295.                 {
  296.                     BufferedInputStream bis = new BufferedInputStream(zipFile.getInputStream(entry));
  297.                     int b;
  298.                     byte buffer[] = new byte[BYTE_SIZE];
  299.                     FileOutputStream fos = new FileOutputStream(destinationFilePath);
  300.                     BufferedOutputStream bos = new BufferedOutputStream(fos, BYTE_SIZE);
  301.                     while((b = bis.read(buffer, 0, BYTE_SIZE)) != -1)
  302.                     {
  303.                         bos.write(buffer, 0, b);
  304.                     }
  305.                     bos.flush();
  306.                     bos.close();
  307.                     bis.close();
  308.                     String name = destinationFilePath.getName();
  309.                     if(name.endsWith(".jar") && pluginFile(name))
  310.                     {
  311.                         destinationFilePath.renameTo(new File("plugins/" + updateFolder + "/" + name));
  312.                     }
  313.                 }
  314.                 entry = null;
  315.                 destinationFilePath = null;
  316.             }
  317.             e = null;
  318.             zipFile.close();
  319.             zipFile = null;
  320.             // Move any plugin data folders that were included to the right place, Bukkit won't do this for us.
  321.             for(File dFile : new File(zipPath).listFiles())
  322.             {
  323.                 if(dFile.isDirectory())
  324.                 {
  325.                     if(pluginFile(dFile.getName()))
  326.                     {
  327.                         File oFile = new File("plugins/" + dFile.getName()); // Get current dir
  328.                         File [] contents = oFile.listFiles(); // List of existing files in the current dir
  329.                         for(File cFile : dFile.listFiles()) // Loop through all the files in the new dir
  330.                         {
  331.                             boolean found = false;
  332.                             for(File xFile : contents) // Loop through contents to see if it exists
  333.                             {
  334.                                 if(xFile.getName().equals(cFile.getName()))
  335.                                 {
  336.                                     found = true;
  337.                                     break;
  338.                                 }
  339.                             }
  340.                             if(!found)
  341.                             {
  342.                                 // Move the new file into the current dir
  343.                                 cFile.renameTo(new File(oFile.getCanonicalFile() + "/" + cFile.getName()));
  344.                             }
  345.                             else
  346.                             {
  347.                                 // This file already exists, so we don't need it anymore.
  348.                                 cFile.delete();
  349.                             }
  350.                         }
  351.                     }
  352.                 }
  353.                 dFile.delete();
  354.             }
  355.             new File(zipPath).delete();
  356.             fSourceZip.delete();
  357.         }
  358.         catch(IOException ex)
  359.         {
  360.             ex.printStackTrace();
  361.             plugin.getLogger().warning("The auto-updater tried to unzip a new update file, but was unsuccessful.");
  362.             result = Updater.UpdateResult.FAIL_DOWNLOAD;
  363.         }
  364.         new File(file).delete();
  365.     }
  366.  
  367.     /**
  368. * Check if the name of a jar is one of the plugins currently installed, used for extracting the correct files out of a zip.
  369. */
  370.     public boolean pluginFile(String name)
  371.     {
  372.         for(File file : new File("plugins").listFiles())
  373.         {
  374.             if(file.getName().equals(name))
  375.             {
  376.                 return true;
  377.             }
  378.         }
  379.         return false;
  380.     }
  381.  
  382.     /**
  383. * Obtain the direct download file url from the file's page.
  384. */
  385.     private String getFile(String link)
  386.     {
  387.         String download = null;
  388.         try
  389.         {
  390.             // Open a connection to the page
  391.             URL url = new URL(link);
  392.             URLConnection urlConn = url.openConnection();
  393.             InputStreamReader inStream = new InputStreamReader(urlConn.getInputStream());
  394.             BufferedReader buff = new BufferedReader(inStream);
  395.  
  396.             int counter = 0;
  397.             String line;
  398.             while((line = buff.readLine()) != null)
  399.             {
  400.                 counter++;
  401.                 // Search for the download link
  402.                 if(line.contains("<li class=\"user-action user-action-download\">"))
  403.                 {
  404.                     // Get the raw link
  405.                     download = line.split("<a href=\"")[1].split("\">Download</a>")[0];
  406.                 }
  407.                 // Search for size
  408.                 else if (line.contains("<dt>Size</dt>"))
  409.                 {
  410.                     sizeLine = counter+1;
  411.                 }
  412.                 else if(counter == sizeLine)
  413.                 {
  414.                     String size = line.replaceAll("<dd>", "").replaceAll("</dd>", "");
  415.                     multiplier = size.contains("MiB") ? 1048576 : 1024;
  416.                     size = size.replace(" KiB", "").replace(" MiB", "");
  417.                     totalSize = (long)(Double.parseDouble(size)*multiplier);
  418.                 }
  419.             }
  420.             urlConn = null;
  421.             inStream = null;
  422.             buff.close();
  423.             buff = null;
  424.         }
  425.         catch (Exception ex)
  426.         {
  427.             ex.printStackTrace();
  428.             plugin.getLogger().warning("The auto-updater tried to contact dev.bukkit.org, but was unsuccessful.");
  429.             result = Updater.UpdateResult.FAIL_DBO;
  430.             return null;
  431.         }
  432.         return download;
  433.     }
  434.  
  435.     /**
  436. * Check to see if the program should continue by evaluation whether the plugin is already updated, or shouldn't be updated
  437. */
  438.     private boolean versionCheck(String title)
  439.     {
  440.         if(type != UpdateType.NO_VERSION_CHECK)
  441.         {
  442.             String version = plugin.getDescription().getVersion();
  443.             if(title.split(" v").length == 2)
  444.             {
  445.                 String remoteVersion = title.split(" v")[1].split(" ")[0]; // Get the newest file's version number
  446.                 int remVer = -1,curVer=0;
  447.                 try
  448.                 {
  449.                     remVer = calVer(remoteVersion);
  450.                     curVer = calVer(version);
  451.                 }
  452.                 catch(NumberFormatException nfe)
  453.                 {
  454.                     remVer=-1;
  455.                 }
  456.                 if(hasTag(version)||version.equalsIgnoreCase(remoteVersion)||curVer>=remVer)
  457.                 {
  458.                     // We already have the latest version, or this build is tagged for no-update
  459.                     result = Updater.UpdateResult.NO_UPDATE;
  460.                     return false;
  461.                 }
  462.             }
  463.             else
  464.             {
  465.                 // The file's name did not contain the string 'vVersion'
  466.                 plugin.getLogger().warning("The author of this plugin (" + plugin.getDescription().getAuthors().get(0) + ") has misconfigured their Auto Update system");
  467.                 plugin.getLogger().warning("Files uploaded to BukkitDev should contain the version number, seperated from the name by a 'v', such as PluginName v1.0");
  468.                 plugin.getLogger().warning("Please notify the author of this error.");
  469.                 result = Updater.UpdateResult.FAIL_NOVERSION;
  470.                 return false;
  471.             }
  472.         }
  473.         return true;
  474.     }
  475.     /**
  476. * Used to calculate the version string as an Integer
  477. */
  478.     private Integer calVer(String s) throws NumberFormatException
  479.     {
  480.         if(s.contains("."))
  481.         {
  482.             StringBuilder sb = new StringBuilder();
  483.             for (int i = 0; i <s.length(); i++)
  484.             {
  485.                 Character c = s.charAt(i);
  486.                 if (Character.isLetterOrDigit(c))
  487.                 {
  488.                     sb.append(c);
  489.                 }
  490.             }
  491.             return Integer.parseInt(sb.toString());
  492.         }
  493.         return Integer.parseInt(s);
  494.     }
  495.     /**
  496. * Evaluate whether the version number is marked showing that it should not be updated by this program
  497. */
  498.     private boolean hasTag(String version)
  499.     {
  500.         for(String string : noUpdateTag)
  501.         {
  502.             if(version.contains(string))
  503.             {
  504.                 return true;
  505.             }
  506.         }
  507.         return false;
  508.     }
  509.  
  510.     /**
  511. * Part of RSS Reader by Vogella, modified by H31IX for use with Bukkit
  512. */
  513.     private boolean readFeed()
  514.     {
  515.         try
  516.         {
  517.             // Set header values intial to the empty string
  518.             String title = "";
  519.             String link = "";
  520.             // First create a new XMLInputFactory
  521.             XMLInputFactory inputFactory = XMLInputFactory.newInstance();
  522.             // Setup a new eventReader
  523.             InputStream in = read();
  524.             if(in != null)
  525.             {
  526.                 XMLEventReader eventReader = inputFactory.createXMLEventReader(in);
  527.                 // Read the XML document
  528.                 while (eventReader.hasNext())
  529.                 {
  530.                     XMLEvent event = eventReader.nextEvent();
  531.                     if (event.isStartElement())
  532.                     {
  533.                         if (event.asStartElement().getName().getLocalPart().equals(TITLE))
  534.                         {
  535.                             event = eventReader.nextEvent();
  536.                             title = event.asCharacters().getData();
  537.                             continue;
  538.                         }
  539.                         if (event.asStartElement().getName().getLocalPart().equals(LINK))
  540.                         {
  541.                             event = eventReader.nextEvent();
  542.                             link = event.asCharacters().getData();
  543.                             continue;
  544.                         }
  545.                     }
  546.                     else if (event.isEndElement())
  547.                     {
  548.                         if (event.asEndElement().getName().getLocalPart().equals(ITEM))
  549.                         {
  550.                             // Store the title and link of the first entry we get - the first file on the list is all we need
  551.                             versionTitle = title;
  552.                             versionLink = link;
  553.                             // All done, we don't need to know about older files.
  554.                             break;
  555.                         }
  556.                     }
  557.                 }
  558.                 return true;
  559.             }
  560.             else
  561.             {
  562.                 return false;
  563.             }
  564.         }
  565.         catch (XMLStreamException e)
  566.         {
  567.             plugin.getLogger().warning("Could not reach dev.bukkit.org for update checking. Is it offline?");
  568.             return false;
  569.         }
  570.     }
  571.  
  572.     /**
  573. * Open the RSS feed
  574. */
  575.     private InputStream read()
  576.     {
  577.         try
  578.         {
  579.             return url.openStream();
  580.         }
  581.         catch (IOException e)
  582.         {
  583.             plugin.getLogger().warning("Could not reach BukkitDev file stream for update checking. Is dev.bukkit.org offline?");
  584.             return null;
  585.         }
  586.     }
  587.  
  588.     private class UpdateRunnable implements Runnable {
  589.  
  590.         public void run() {
  591.             if(url != null)
  592.             {
  593.                 // Obtain the results of the project's file feed
  594.                 if(readFeed())
  595.                 {
  596.                     if(versionCheck(versionTitle))
  597.                     {
  598.                         String fileLink = getFile(versionLink);
  599.                         if(fileLink != null && type != UpdateType.NO_DOWNLOAD)
  600.                         {
  601.                             String name = file.getName();
  602.                             // If it's a zip file, it shouldn't be downloaded as the plugin's name
  603.                             if(fileLink.endsWith(".zip"))
  604.                             {
  605.                                 String [] split = fileLink.split("/");
  606.                                 name = split[split.length-1];
  607.                             }
  608.                             saveFile(new File("plugins/" + updateFolder), name, fileLink);
  609.                         }
  610.                         else
  611.                         {
  612.                             result = UpdateResult.UPDATE_AVAILABLE;
  613.                         }
  614.                         isoutdated = true;
  615.                     }
  616.                     else
  617.                     {
  618.                         isoutdated = false;
  619.                     }
  620.                 }
  621.             }
  622.         }
  623.     }
  624.     @EventHandler
  625.     public void onAdminJoin(PlayerJoinEvent e)
  626.     {
  627.         final Player p = e.getPlayer();
  628.         if(isoutdated && (p.isOp() || p.hasPermission(permission)))
  629.         {
  630.             p.sendMessage("An update is available: "+ getLatestVersionString() +"("+ getFileSize()+"Bytes)");
  631.            
  632.         }
  633.         System.out.println("awsdasd");
  634.     }
  635.    
  636. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement