-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathclass-trackserver-mapmytracks.php
260 lines (231 loc) · 7.37 KB
/
class-trackserver-mapmytracks.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
<?php
if ( ! defined( 'TRACKSERVER_PLUGIN_DIR' ) ) {
die( 'No, sorry.' );
}
require_once TRACKSERVER_PLUGIN_DIR . 'class-trackserver-track.php';
require_once TRACKSERVER_PLUGIN_DIR . 'class-trackserver-location.php';
class Trackserver_Mapmytracks {
private $trackserver; // Reference to the calling object
private $user_id; // User ID of WP_User doing the request
private $tbl_tracks;
private $tbl_locations;
/**
* Constructor.
*
* @since 5.0
*/
public function __construct( $trackserver ) {
$this->trackserver = $trackserver;
$this->tbl_tracks = $this->trackserver->tbl_tracks;
$this->tbl_locations = $this->trackserver->tbl_locations;
}
/**
* Handle a MapMyTracks request.
*
* @since 1.0
* @since 5.0 Moved to Trackserver_Mapmytracks class
*/
public function handle_request() {
// Validate with '$return = true' so we can handle the auth failure.
$this->user_id = $this->trackserver->validate_http_basicauth( true );
if ( $this->user_id === false ) {
return $this->error_response( 'unauthorized' );
}
switch ( $_POST['request'] ) {
case 'start_activity':
$this->handle_start_activity();
break;
case 'update_activity':
$this->handle_update_activity();
break;
case 'stop_activity':
$this->handle_stop_activity();
break;
case 'upload_activity':
$this->handle_upload_activity();
break;
default:
$this->trackserver->http_terminate( 501, 'Unsupported MapMyTracks request.' );
}
}
/**
* Send a MapMyTracks error message. Reason is optional.
*
* @since 5.0
*/
private function error_response( $reason = null ) {
$data = array();
if ( ! is_null( $reason ) ) {
$data = array( 'reason' => $reason );
}
return $this->send_response( 'error', $data );
}
/**
* Format a MapMyTracks XML response.
*
* A message must have a type, and it can have additional data.
*
* @since 5.0
*/
private function send_response( $type, $data = array() ) {
$xml = new SimpleXMLElement( '<?xml version="1.0" encoding="UTF-8"?><message />' );
$xml->addChild( 'type', $type );
foreach ( $data as $key => $value ) {
$xml->addChild( $key, $value );
}
echo str_replace( array( "\r", "\n" ), '', $xml->asXML() );
}
/**
* Get the source app and version from a MapMyTracks request.
*/
private function get_source() {
$source = '';
if ( array_key_exists( 'source', $_POST ) ) {
$source .= $_POST['source'];
}
if ( array_key_exists( 'version', $_POST ) ) {
$source .= ' v' . $_POST['version'];
}
if ( $source !== '' ) {
$source .= ' / ';
}
$source .= 'MapMyTracks';
return $source;
}
/**
* Handle the 'start_activity' request for the MapMyTracks protocol.
*
* If no title / track name is received, an error is sent, otherwise track
* is saved, received points are validated, valid points are inserted and
* and the new trip ID is sent in an XML reponse.
*
* @since 1.0
* @since 5.0 Delegate DB access to Trackserver_Track instance
*/
private function handle_start_activity() {
if ( ! empty( $_POST['title'] ) ) {
$track = new Trackserver_Track( $this->trackserver, null, $this->user_id );
$track->set( 'name', $_POST['title'] );
$track->set( 'source', $this->get_source() );
if ( $track->save() ) {
list( $result, $reason ) = $this->process_points( $track->id );
if ( $result ) {
return $this->send_response( 'activity_started', array( 'activity_id' => (string) $track->id ) );
}
return $this->error_response( $reason );
}
}
return $this->error_response( 'input error' );
}
/**
* Handle 'update_activity' request for the MapMyTracks protocol.
*
* It tries to instantiate a track object from the given activity_id. If that succeeds,
* the location data is parsed and added to the track.
*
* @since 1.0
*/
private function handle_update_activity() {
$track = new Trackserver_Track( $this->trackserver, $_POST['activity_id'], $this->user_id ); // $restrict = true
if ( $track->id ) {
list( $result, $reason ) = $this->process_points( $track->id );
if ( $result ) {
return $this->send_response( 'activity_updated' );
}
return $this->error_response( $reason );
}
return $this->error_response( 'track not found' );
}
/**
* Handle 'stop_activity' request for the MapMyTracks protocol.
*
* This doesn't do anything, except return an appropriate XML message.
*
* @since 1.0
*/
private function handle_stop_activity() {
return $this->mapmytracks_response( 'activity_stopped' );
}
/**
* Handle 'upload_activity' request for the MapMyTracks protocol.
*
* It validates and processes the input as GPX data, and returns an
* appropriate XML message.
*
* @since 1.0
*/
private function handle_upload_activity() {
global $wpdb;
$_POST = stripslashes_deep( $_POST );
if ( isset( $_POST['gpx_file'] ) ) {
$xml = $this->trackserver->validate_gpx_string( $_POST['gpx_file'] );
if ( $xml ) {
$result = $this->trackserver->process_gpx( $xml, $this->user_id );
// If a description was given, put it in the comment field.
if ( isset( $_POST['description'] ) ) {
$track_ids = $result['track_ids'];
if ( count( $track_ids ) > 0 ) {
$in = '(' . implode( ',', $track_ids ) . ')';
$sql = $wpdb->prepare( 'UPDATE ' . $this->tbl_tracks . " SET comment=%s WHERE user_id=%d AND id IN $in", $_POST['description'], $this->user_id ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.PreparedSQL.InterpolatedNotPrepared
$wpdb->query( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
}
}
return $this->send_response( 'success' );
}
}
return $this->error_response( 'Invalid input' );
}
/**
* Process 'points' input from a MapMyTracks request.
*
* Parse the input from $_POST and save the points to the database. On
* errors, abort the operation and inform the caller.
*
* @since 5.0
*/
private function process_points( $track_id ) {
if ( empty( $_POST['points'] ) ) {
return array( true, '' );
}
$points = $this->parse_points( $_POST['points'] );
if ( $points === false ) {
return array( false, 'input error' );
}
if ( $this->trackserver->mapmytracks_insert_points( $points, $track_id, $this->user_id ) ) {
return array( true, '' );
}
return array( false, 'server error' );
}
/**
* Parse a string of points from a MapMyTracks request.
*
* Usinga regular expression, the string of points is parsed into
* an associative array for easier storage in the database.
*/
private function parse_points( $points ) {
// Check the points syntax. It should match groups of four items. The
// first three may contain numbers, dots, dashes and the letter 'E' (it
// appears that OruxMaps sometimes sends floats in the scientific
// notation, like '7.72E-4'). The last one is the timestamp, which may
// only contain numbers.
$pattern = '/^([\dE.-]+ [\dE.-]+ [\dE.-]+ [\d]+ ?)*$/';
$n = preg_match( $pattern, $points );
if ( $n === 1 ) {
$parsed = array();
$all = explode( ' ', $points );
for ( $i = 0; $i < count( $all ); $i += 4 ) {
if ( $all[ $i ] ) {
$parsed[] = array(
'latitude' => number_format( $all[ $i ], 6 ),
'longitude' => number_format( $all[ $i + 1 ], 6 ),
'altitude' => number_format( $all[ $i + 2 ], 6 ),
'timestamp' => $all[ $i + 3 ],
);
}
}
return $parsed;
} else {
return false; // Invalid input
}
}
} // class