Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

DMDircCheckStrategy.java 8.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. /*
  2. * Copyright (c) 2006-2015 DMDirc Developers
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a copy
  5. * of this software and associated documentation files (the "Software"), to deal
  6. * in the Software without restriction, including without limitation the rights
  7. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. * copies of the Software, and to permit persons to whom the Software is
  9. * furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice shall be included in
  12. * all copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  20. * SOFTWARE.
  21. */
  22. package com.dmdirc.updater.checking;
  23. import com.dmdirc.config.ConfigBinding;
  24. import com.dmdirc.config.GlobalConfig;
  25. import com.dmdirc.interfaces.config.AggregateConfigProvider;
  26. import com.dmdirc.updater.UpdateChannel;
  27. import com.dmdirc.updater.UpdateComponent;
  28. import com.dmdirc.updater.Version;
  29. import com.dmdirc.util.LogUtils;
  30. import com.dmdirc.util.io.Downloader;
  31. import org.slf4j.Logger;
  32. import org.slf4j.LoggerFactory;
  33. import javax.inject.Inject;
  34. import java.io.IOException;
  35. import java.net.MalformedURLException;
  36. import java.net.URL;
  37. import java.util.Collection;
  38. import java.util.HashMap;
  39. import java.util.List;
  40. import java.util.Map;
  41. /**
  42. * A strategy which sends a request to the DMDirc update service for information.
  43. */
  44. public class DMDircCheckStrategy implements UpdateCheckStrategy {
  45. private static final Logger LOG = LoggerFactory.getLogger(DMDircCheckStrategy.class);
  46. /** The URL to request to check for updates. */
  47. private static final String UPDATE_URL = "https://updates.dmdirc.com/";
  48. /** The update channel to check for updates on. */
  49. private UpdateChannel channel;
  50. /** Downloader to download files. */
  51. private final Downloader downloader;
  52. /**
  53. * Creates a new instance of {@link DMDircCheckStrategy}.
  54. *
  55. * @param configProvider The provider to use to retrieve update channel information.
  56. * @param downloader Used to download files
  57. */
  58. @Inject
  59. public DMDircCheckStrategy(@GlobalConfig final AggregateConfigProvider configProvider,
  60. final Downloader downloader) {
  61. configProvider.getBinder().bind(this, DMDircCheckStrategy.class);
  62. this.downloader = downloader;
  63. }
  64. /**
  65. * Sets the channel which will be used by the {@link DMDircCheckStrategy}.
  66. *
  67. * @param channel The new channel to use
  68. */
  69. @ConfigBinding(domain = "updater", key = "channel")
  70. public void setChannel(final String channel) {
  71. LOG.info("Changing channel to {}", channel);
  72. try {
  73. this.channel = UpdateChannel.valueOf(channel.toUpperCase());
  74. } catch (IllegalArgumentException ex) {
  75. LOG.warn("Unknown channel {}", channel, ex);
  76. }
  77. }
  78. @Override
  79. public Map<UpdateComponent, UpdateCheckResult> checkForUpdates(
  80. final Collection<UpdateComponent> components) {
  81. final Map<UpdateComponent, UpdateCheckResult> res = new HashMap<>();
  82. final Map<String, UpdateComponent> names = getComponentsByName(components);
  83. try {
  84. final List<String> response = downloader.getPage(UPDATE_URL, getPayload(components));
  85. LOG.trace("Response from update server: {}", response);
  86. for (String line : response) {
  87. final UpdateComponent component = names.get(getComponent(line));
  88. if (component == null) {
  89. LOG.warn("Unable to extract component from line: {}", line);
  90. continue;
  91. }
  92. final UpdateCheckResult result = parseResponse(component, line);
  93. if (result != null) {
  94. res.put(component, result);
  95. }
  96. }
  97. } catch (IOException ex) {
  98. LOG.warn("I/O exception when checking for updates", ex);
  99. }
  100. return res;
  101. }
  102. /**
  103. * Builds the data payload which will be sent to the update server. Specifically, iterates over
  104. * each component and appends their name, the channel name, and the component's version number.
  105. *
  106. * @param components The components to be added to the payload
  107. *
  108. * @return A string which can be posted to the DMDirc update server
  109. */
  110. private String getPayload(final Collection<UpdateComponent> components) {
  111. final StringBuilder data = new StringBuilder("data=");
  112. for (UpdateComponent component : components) {
  113. LOG.trace("Adding payload info for component {} (version {})", component.getName(),
  114. component.getVersion());
  115. data.append(component.getName());
  116. data.append(',');
  117. data.append(channel.name());
  118. data.append(',');
  119. data.append(component.getVersion());
  120. data.append(';');
  121. }
  122. LOG.debug("Constructed update payload: {}", data);
  123. return data.toString();
  124. }
  125. /**
  126. * Extracts the name of the component a given response line contains.
  127. *
  128. * @param line The line to be parsed
  129. *
  130. * @return The name of the component extracted from the given line
  131. */
  132. private String getComponent(final String line) {
  133. final String[] parts = line.split(" ");
  134. if (parts.length >= 2 && "outofdate".equals(parts[0])) {
  135. return parts[1];
  136. }
  137. return parts.length >= 3 ? parts[2] : null;
  138. }
  139. /**
  140. * Checks the specified line to determine the message from the update server.
  141. *
  142. * @param component The component the line refers to
  143. * @param line The line to be checked
  144. */
  145. private UpdateCheckResult parseResponse(final UpdateComponent component,
  146. final String line) {
  147. final String[] parts = line.split(" ");
  148. switch (parts[0]) {
  149. case "outofdate":
  150. return parseOutOfDateResponse(component, parts);
  151. case "uptodate":
  152. return new BaseCheckResult(component);
  153. case "error":
  154. LOG.warn("Error received from update server: {}", line);
  155. break;
  156. default:
  157. LOG.error("Unknown update line received from server: {}", line);
  158. break;
  159. }
  160. return null;
  161. }
  162. /**
  163. * Parses an "outofdate" response from the server. Extracts the URL, remote version and remote
  164. * friendly version into a {@link BaseDownloadableResult}.
  165. *
  166. * @param parts The tokenised parts of the response line
  167. *
  168. * @return A corresponding {@link UpdateCheckResult} or null on failure
  169. */
  170. private UpdateCheckResult parseOutOfDateResponse(
  171. final UpdateComponent component, final String[] parts) {
  172. try {
  173. return new BaseDownloadableResult(component, new URL(parts[5]),
  174. parts[4], new Version(parts[3]));
  175. } catch (MalformedURLException ex) {
  176. LOG.error(LogUtils.APP_ERROR, "Unable to construct URL for update. Parts: {}", parts,
  177. ex);
  178. return null;
  179. }
  180. }
  181. /**
  182. * Builds a mapping of components' names to their actual component objects.
  183. *
  184. * @param components A collection of components to be mapped
  185. *
  186. * @return A corresponding Map containing a single entry for each component, which the
  187. * component's name as a key and the component itself as a value.
  188. */
  189. private Map<String, UpdateComponent> getComponentsByName(
  190. final Collection<UpdateComponent> components) {
  191. final Map<String, UpdateComponent> res = new HashMap<>();
  192. for (UpdateComponent component : components) {
  193. res.put(component.getName(), component);
  194. }
  195. return res;
  196. }
  197. }