001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.io;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.io.IOException;
007import java.io.InputStream;
008import java.net.HttpURLConnection;
009import java.net.MalformedURLException;
010import java.net.URL;
011import java.util.List;
012
013import org.openstreetmap.josm.Main;
014import org.openstreetmap.josm.data.gpx.GpxData;
015import org.openstreetmap.josm.data.notes.Note;
016import org.openstreetmap.josm.data.osm.DataSet;
017import org.openstreetmap.josm.gui.progress.ProgressMonitor;
018import org.openstreetmap.josm.tools.HttpClient;
019
020/**
021 * This DataReader reads directly from the REST API of the osm server.
022 *
023 * It supports plain text transfer as well as gzip or deflate encoded transfers;
024 * if compressed transfers are unwanted, set property osm-server.use-compression
025 * to false.
026 *
027 * @author imi
028 */
029public abstract class OsmServerReader extends OsmConnection {
030    private final OsmApi api = OsmApi.getOsmApi();
031    private boolean doAuthenticate;
032    protected boolean gpxParsedProperly;
033
034    /**
035     * Open a connection to the given url and return a reader on the input stream
036     * from that connection. In case of user cancel, return <code>null</code>.
037     * Relative URL's are directed to API base URL.
038     * @param urlStr The url to connect to.
039     * @param progressMonitor progress monitoring and abort handler
040     * @return A reader reading the input stream (servers answer) or <code>null</code>.
041     * @throws OsmTransferException if data transfer errors occur
042     */
043    protected InputStream getInputStream(String urlStr, ProgressMonitor progressMonitor) throws OsmTransferException  {
044        return getInputStream(urlStr, progressMonitor, null);
045    }
046
047    /**
048     * Open a connection to the given url and return a reader on the input stream
049     * from that connection. In case of user cancel, return <code>null</code>.
050     * Relative URL's are directed to API base URL.
051     * @param urlStr The url to connect to.
052     * @param progressMonitor progress monitoring and abort handler
053     * @param reason The reason to show on console. Can be {@code null} if no reason is given
054     * @return A reader reading the input stream (servers answer) or <code>null</code>.
055     * @throws OsmTransferException if data transfer errors occur
056     */
057    protected InputStream getInputStream(String urlStr, ProgressMonitor progressMonitor, String reason) throws OsmTransferException  {
058        try {
059            api.initialize(progressMonitor);
060            String url = urlStr.startsWith("http") ? urlStr : (getBaseUrl() + urlStr);
061            return getInputStreamRaw(url, progressMonitor, reason);
062        } finally {
063            progressMonitor.invalidate();
064        }
065    }
066
067    /**
068     * Return the base URL for relative URL requests
069     * @return base url of API
070     */
071    protected String getBaseUrl() {
072        return api.getBaseUrl();
073    }
074
075    /**
076     * Open a connection to the given url and return a reader on the input stream
077     * from that connection. In case of user cancel, return <code>null</code>.
078     * @param urlStr The exact url to connect to.
079     * @param progressMonitor progress monitoring and abort handler
080     * @return An reader reading the input stream (servers answer) or <code>null</code>.
081     * @throws OsmTransferException if data transfer errors occur
082     */
083    protected InputStream getInputStreamRaw(String urlStr, ProgressMonitor progressMonitor) throws OsmTransferException {
084        return getInputStreamRaw(urlStr, progressMonitor, null);
085    }
086
087    /**
088     * Open a connection to the given url and return a reader on the input stream
089     * from that connection. In case of user cancel, return <code>null</code>.
090     * @param urlStr The exact url to connect to.
091     * @param progressMonitor progress monitoring and abort handler
092     * @param reason The reason to show on console. Can be {@code null} if no reason is given
093     * @return An reader reading the input stream (servers answer) or <code>null</code>.
094     * @throws OsmTransferException if data transfer errors occur
095     */
096    protected InputStream getInputStreamRaw(String urlStr, ProgressMonitor progressMonitor, String reason) throws OsmTransferException {
097        return getInputStreamRaw(urlStr, progressMonitor, reason, false);
098    }
099
100    /**
101     * Open a connection to the given url and return a reader on the input stream
102     * from that connection. In case of user cancel, return <code>null</code>.
103     * @param urlStr The exact url to connect to.
104     * @param progressMonitor progress monitoring and abort handler
105     * @param reason The reason to show on console. Can be {@code null} if no reason is given
106     * @param uncompressAccordingToContentDisposition Whether to inspect the HTTP header {@code Content-Disposition}
107     *                                                for {@code filename} and uncompress a gzip/bzip2 stream.
108     * @return An reader reading the input stream (servers answer) or <code>null</code>.
109     * @throws OsmTransferException if data transfer errors occur
110     */
111    @SuppressWarnings("resource")
112    protected InputStream getInputStreamRaw(String urlStr, ProgressMonitor progressMonitor, String reason,
113            boolean uncompressAccordingToContentDisposition) throws OsmTransferException {
114        try {
115            OnlineResource.JOSM_WEBSITE.checkOfflineAccess(urlStr, Main.getJOSMWebsite());
116            OnlineResource.OSM_API.checkOfflineAccess(urlStr, OsmApi.getOsmApi().getServerUrl());
117
118            URL url = null;
119            try {
120                url = new URL(urlStr.replace(" ", "%20"));
121            } catch (MalformedURLException e) {
122                throw new OsmTransferException(e);
123            }
124
125            if ("file".equals(url.getProtocol())) {
126                try {
127                    return url.openStream();
128                } catch (IOException e) {
129                    throw new OsmTransferException(e);
130                }
131            }
132
133            final HttpClient client = HttpClient.create(url);
134            activeConnection = client;
135            client.setReasonForRequest(reason);
136            adaptRequest(client);
137            if (doAuthenticate) {
138                addAuth(client);
139            }
140            if (cancel)
141                throw new OsmTransferCanceledException("Operation canceled");
142
143            final HttpClient.Response response;
144            try {
145                response = client.connect(progressMonitor);
146            } catch (IOException e) {
147                Main.error(e);
148                OsmTransferException ote = new OsmTransferException(
149                        tr("Could not connect to the OSM server. Please check your internet connection."), e);
150                ote.setUrl(url.toString());
151                throw ote;
152            }
153            try {
154                if (response.getResponseCode() == HttpURLConnection.HTTP_UNAUTHORIZED)
155                    throw new OsmApiException(HttpURLConnection.HTTP_UNAUTHORIZED, null, null);
156
157                if (response.getResponseCode() == HttpURLConnection.HTTP_PROXY_AUTH)
158                    throw new OsmTransferCanceledException("Proxy Authentication Required");
159
160                if (response.getResponseCode() != HttpURLConnection.HTTP_OK) {
161                    String errorHeader = response.getHeaderField("Error");
162                    String errorBody;
163                    try {
164                        errorBody = response.fetchContent();
165                    } catch (IOException e) {
166                        errorBody = tr("Reading error text failed.");
167                    }
168                    throw new OsmApiException(response.getResponseCode(), errorHeader, errorBody, url.toString());
169                }
170
171                response.uncompressAccordingToContentDisposition(uncompressAccordingToContentDisposition);
172                return response.getContent();
173            } catch (OsmTransferException e) {
174                throw e;
175            } catch (IOException e) {
176                throw new OsmTransferException(e);
177            }
178        } finally {
179            progressMonitor.invalidate();
180        }
181    }
182
183    /**
184     * Allows subclasses to modify the request.
185     * @param request the prepared request
186     * @since 9308
187     */
188    protected void adaptRequest(HttpClient request) {
189    }
190
191    /**
192     * Download OSM files from somewhere
193     * @param progressMonitor The progress monitor
194     * @return The corresponding dataset
195     * @throws OsmTransferException if any error occurs
196     */
197    public abstract DataSet parseOsm(final ProgressMonitor progressMonitor) throws OsmTransferException;
198
199    /**
200     * Download OSM Change files from somewhere
201     * @param progressMonitor The progress monitor
202     * @return The corresponding dataset
203     * @throws OsmTransferException if any error occurs
204     */
205    public DataSet parseOsmChange(final ProgressMonitor progressMonitor) throws OsmTransferException {
206        return null;
207    }
208
209    /**
210     * Download BZip2-compressed OSM Change files from somewhere
211     * @param progressMonitor The progress monitor
212     * @return The corresponding dataset
213     * @throws OsmTransferException if any error occurs
214     */
215    public DataSet parseOsmChangeBzip2(final ProgressMonitor progressMonitor) throws OsmTransferException {
216        return null;
217    }
218
219    /**
220     * Download GZip-compressed OSM Change files from somewhere
221     * @param progressMonitor The progress monitor
222     * @return The corresponding dataset
223     * @throws OsmTransferException if any error occurs
224     */
225    public DataSet parseOsmChangeGzip(final ProgressMonitor progressMonitor) throws OsmTransferException {
226        return null;
227    }
228
229    /**
230     * Retrieve raw gps waypoints from the server API.
231     * @param progressMonitor The progress monitor
232     * @return The corresponding GPX tracks
233     * @throws OsmTransferException if any error occurs
234     */
235    public GpxData parseRawGps(final ProgressMonitor progressMonitor) throws OsmTransferException {
236        return null;
237    }
238
239    /**
240     * Retrieve BZip2-compressed GPX files from somewhere.
241     * @param progressMonitor The progress monitor
242     * @return The corresponding GPX tracks
243     * @throws OsmTransferException if any error occurs
244     * @since 6244
245     */
246    public GpxData parseRawGpsBzip2(final ProgressMonitor progressMonitor) throws OsmTransferException {
247        return null;
248    }
249
250    /**
251     * Download BZip2-compressed OSM files from somewhere
252     * @param progressMonitor The progress monitor
253     * @return The corresponding dataset
254     * @throws OsmTransferException if any error occurs
255     */
256    public DataSet parseOsmBzip2(final ProgressMonitor progressMonitor) throws OsmTransferException {
257        return null;
258    }
259
260    /**
261     * Download GZip-compressed OSM files from somewhere
262     * @param progressMonitor The progress monitor
263     * @return The corresponding dataset
264     * @throws OsmTransferException if any error occurs
265     */
266    public DataSet parseOsmGzip(final ProgressMonitor progressMonitor) throws OsmTransferException {
267        return null;
268    }
269
270    /**
271     * Download Zip-compressed OSM files from somewhere
272     * @param progressMonitor The progress monitor
273     * @return The corresponding dataset
274     * @throws OsmTransferException if any error occurs
275     * @since 6882
276     */
277    public DataSet parseOsmZip(final ProgressMonitor progressMonitor) throws OsmTransferException {
278        return null;
279    }
280
281    /**
282     * Returns true if this reader is adding authentication credentials to the read
283     * request sent to the server.
284     *
285     * @return true if this reader is adding authentication credentials to the read
286     * request sent to the server
287     */
288    public boolean isDoAuthenticate() {
289        return doAuthenticate;
290    }
291
292    /**
293     * Sets whether this reader adds authentication credentials to the read
294     * request sent to the server.
295     *
296     * @param doAuthenticate  true if  this reader adds authentication credentials to the read
297     * request sent to the server
298     */
299    public void setDoAuthenticate(boolean doAuthenticate) {
300        this.doAuthenticate = doAuthenticate;
301    }
302
303    /**
304     * Determines if the GPX data has been parsed properly.
305     * @return true if the GPX data has been parsed properly, false otherwise
306     * @see GpxReader#parse
307     */
308    public final boolean isGpxParsedProperly() {
309        return gpxParsedProperly;
310    }
311
312    /**
313     * Downloads notes from the API, given API limit parameters
314     *
315     * @param noteLimit How many notes to download.
316     * @param daysClosed Return notes closed this many days in the past. -1 means all notes, ever. 0 means only unresolved notes.
317     * @param progressMonitor Progress monitor for user feedback
318     * @return List of notes returned by the API
319     * @throws OsmTransferException if any errors happen
320     */
321    public List<Note> parseNotes(int noteLimit, int daysClosed, ProgressMonitor progressMonitor) throws OsmTransferException {
322        return null;
323    }
324
325    /**
326     * Downloads notes from a given raw URL. The URL is assumed to be complete and no API limits are added
327     *
328     * @param progressMonitor progress monitor
329     * @return A list of notes parsed from the URL
330     * @throws OsmTransferException if any error occurs during dialog with OSM API
331     */
332    public List<Note> parseRawNotes(final ProgressMonitor progressMonitor) throws OsmTransferException {
333        return null;
334    }
335
336    /**
337     * Download notes from a URL that contains a bzip2 compressed notes dump file
338     * @param progressMonitor progress monitor
339     * @return A list of notes parsed from the URL
340     * @throws OsmTransferException if any error occurs during dialog with OSM API
341     */
342    public List<Note> parseRawNotesBzip2(final ProgressMonitor progressMonitor) throws OsmTransferException {
343        return null;
344    }
345}