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 = fetchResponseText(response);
163                    throw new OsmApiException(response.getResponseCode(), errorHeader, errorBody, url.toString());
164                }
165
166                response.uncompressAccordingToContentDisposition(uncompressAccordingToContentDisposition);
167                return response.getContent();
168            } catch (OsmTransferException e) {
169                throw e;
170            } catch (IOException e) {
171                throw new OsmTransferException(e);
172            }
173        } finally {
174            progressMonitor.invalidate();
175        }
176    }
177
178    private static String fetchResponseText(final HttpClient.Response response) {
179        try {
180            return response.fetchContent();
181        } catch (IOException e) {
182            Main.error(e);
183            return tr("Reading error text failed.");
184        }
185    }
186
187    /**
188     * Allows subclasses to modify the request.
189     * @param request the prepared request
190     * @since 9308
191     */
192    protected void adaptRequest(HttpClient request) {
193    }
194
195    /**
196     * Download OSM files from somewhere
197     * @param progressMonitor The progress monitor
198     * @return The corresponding dataset
199     * @throws OsmTransferException if any error occurs
200     */
201    public abstract DataSet parseOsm(final ProgressMonitor progressMonitor) throws OsmTransferException;
202
203    /**
204     * Download OSM Change files from somewhere
205     * @param progressMonitor The progress monitor
206     * @return The corresponding dataset
207     * @throws OsmTransferException if any error occurs
208     */
209    public DataSet parseOsmChange(final ProgressMonitor progressMonitor) throws OsmTransferException {
210        return null;
211    }
212
213    /**
214     * Download BZip2-compressed OSM Change files from somewhere
215     * @param progressMonitor The progress monitor
216     * @return The corresponding dataset
217     * @throws OsmTransferException if any error occurs
218     */
219    public DataSet parseOsmChangeBzip2(final ProgressMonitor progressMonitor) throws OsmTransferException {
220        return null;
221    }
222
223    /**
224     * Download GZip-compressed OSM Change files from somewhere
225     * @param progressMonitor The progress monitor
226     * @return The corresponding dataset
227     * @throws OsmTransferException if any error occurs
228     */
229    public DataSet parseOsmChangeGzip(final ProgressMonitor progressMonitor) throws OsmTransferException {
230        return null;
231    }
232
233    /**
234     * Retrieve raw gps waypoints from the server API.
235     * @param progressMonitor The progress monitor
236     * @return The corresponding GPX tracks
237     * @throws OsmTransferException if any error occurs
238     */
239    public GpxData parseRawGps(final ProgressMonitor progressMonitor) throws OsmTransferException {
240        return null;
241    }
242
243    /**
244     * Retrieve BZip2-compressed GPX files from somewhere.
245     * @param progressMonitor The progress monitor
246     * @return The corresponding GPX tracks
247     * @throws OsmTransferException if any error occurs
248     * @since 6244
249     */
250    public GpxData parseRawGpsBzip2(final ProgressMonitor progressMonitor) throws OsmTransferException {
251        return null;
252    }
253
254    /**
255     * Download BZip2-compressed OSM files from somewhere
256     * @param progressMonitor The progress monitor
257     * @return The corresponding dataset
258     * @throws OsmTransferException if any error occurs
259     */
260    public DataSet parseOsmBzip2(final ProgressMonitor progressMonitor) throws OsmTransferException {
261        return null;
262    }
263
264    /**
265     * Download GZip-compressed OSM files from somewhere
266     * @param progressMonitor The progress monitor
267     * @return The corresponding dataset
268     * @throws OsmTransferException if any error occurs
269     */
270    public DataSet parseOsmGzip(final ProgressMonitor progressMonitor) throws OsmTransferException {
271        return null;
272    }
273
274    /**
275     * Download Zip-compressed OSM files from somewhere
276     * @param progressMonitor The progress monitor
277     * @return The corresponding dataset
278     * @throws OsmTransferException if any error occurs
279     * @since 6882
280     */
281    public DataSet parseOsmZip(final ProgressMonitor progressMonitor) throws OsmTransferException {
282        return null;
283    }
284
285    /**
286     * Returns true if this reader is adding authentication credentials to the read
287     * request sent to the server.
288     *
289     * @return true if this reader is adding authentication credentials to the read
290     * request sent to the server
291     */
292    public boolean isDoAuthenticate() {
293        return doAuthenticate;
294    }
295
296    /**
297     * Sets whether this reader adds authentication credentials to the read
298     * request sent to the server.
299     *
300     * @param doAuthenticate  true if  this reader adds authentication credentials to the read
301     * request sent to the server
302     */
303    public void setDoAuthenticate(boolean doAuthenticate) {
304        this.doAuthenticate = doAuthenticate;
305    }
306
307    /**
308     * Determines if the GPX data has been parsed properly.
309     * @return true if the GPX data has been parsed properly, false otherwise
310     * @see GpxReader#parse
311     */
312    public final boolean isGpxParsedProperly() {
313        return gpxParsedProperly;
314    }
315
316    /**
317     * Downloads notes from the API, given API limit parameters
318     *
319     * @param noteLimit How many notes to download.
320     * @param daysClosed Return notes closed this many days in the past. -1 means all notes, ever. 0 means only unresolved notes.
321     * @param progressMonitor Progress monitor for user feedback
322     * @return List of notes returned by the API
323     * @throws OsmTransferException if any errors happen
324     */
325    public List<Note> parseNotes(int noteLimit, int daysClosed, ProgressMonitor progressMonitor) throws OsmTransferException {
326        return null;
327    }
328
329    /**
330     * Downloads notes from a given raw URL. The URL is assumed to be complete and no API limits are added
331     *
332     * @param progressMonitor progress monitor
333     * @return A list of notes parsed from the URL
334     * @throws OsmTransferException if any error occurs during dialog with OSM API
335     */
336    public List<Note> parseRawNotes(final ProgressMonitor progressMonitor) throws OsmTransferException {
337        return null;
338    }
339
340    /**
341     * Download notes from a URL that contains a bzip2 compressed notes dump file
342     * @param progressMonitor progress monitor
343     * @return A list of notes parsed from the URL
344     * @throws OsmTransferException if any error occurs during dialog with OSM API
345     */
346    public List<Note> parseRawNotesBzip2(final ProgressMonitor progressMonitor) throws OsmTransferException {
347        return null;
348    }
349}