1 /*
2 * Sybase PhoneGap HTTPS proxy
3 *
4 * https-proxy.js
5 * This file will not be regenerated, so it is possible to modify it, but it
6 * is not recommended.
7 *
8 * Copyright (c) 2013 Sybase Inc. All rights reserved.
9 */
10
11 /**
12 * Holds PhoneGap HTTP(S) proxy JavaScript
13 * @namespace
14 */
15 (function (window, undefined) {
16 if (!window.HttpsConnection) {
17 window.HttpsConnection = {};
18 }
19
20 var HttpsConnection = window.HttpsConnection;
21
22 /**
23 * Constant definitions for registration methods
24 */
25
26 /**
27 * Constant indicating the operation failed with unknown error. Used in {@link anonymous.sendRequestErrorCBParameter}
28 * @memberOf HttpsConnection
29 * @type number
30 */
31 HttpsConnection.ERR_UNKNOWN = -1;
32 /**
33 * Constant indicating the operation has invalid parameter. Used in {@link anonymous.sendRequestErrorCBParameter}
34 * @memberOf HttpsConnection
35 * @type number
36 */
37 HttpsConnection.ERR_INVALID_PARAMETER_VALUE = -2;
38 /**
39 * Constant indicating the operation failed because of missing parameter. Used in {@link anonymous.sendRequestErrorCBParameter}
40 * @memberOf HttpsConnection
41 * @type number
42 */
43 HttpsConnection.ERR_MISSING_PARAMETER = -3;
44 /**
45 * Constant indicating there is no such cordova action for the current service. Used in {@link anonymous.sendRequestErrorCBParameter}
46 * @memberOf HttpsConnection
47 * @type number
48 */
49 HttpsConnection.ERR_NO_SUCH_ACTION = -100;
50 /**
51 * Constant indicating certificate from file keystore is not supported on current platform. Used in {@link anonymous.sendRequestErrorCBParameter}
52 * @memberOf HttpsConnection
53 * @type number
54 */
55 HttpsConnection.ERR_FILE_CERTIFICATE_SOURCE_UNSUPPORTED = -101;
56 /**
57 * Constant indicating certificate from system keystore is not supported on current platform. Used in {@link anonymous.sendRequestErrorCBParameter}
58 * @memberOf HttpsConnection
59 * @type number
60 */
61 HttpsConnection.ERR_SYSTEM_CERTIFICATE_SOURCE_UNSUPPORTED = -102;
62 /**
63 * Constant indicating certificate from Afaria server is not supported on current platform. Used in {@link anonymous.sendRequestErrorCBParameter}
64 * @memberOf HttpsConnection
65 * @type number
66 */
67 HttpsConnection.ERR_AFARIA_CERTIFICATE_SOURCE_UNSUPPORTED = -103;
68 /**
69 * Constant indicating the certificate with given alias could not be found. Used in {@link anonymous.sendRequestErrorCBParameter}
70 * @memberOf HttpsConnection
71 * @type number
72 */
73 HttpsConnection.ERR_CERTIFICATE_ALIAS_NOT_FOUND = -104;
74 /**
75 * Constant indicating the certificate file could not be found. Used in {@link anonymous.sendRequestErrorCBParameter}
76 * @memberOf HttpsConnection
77 * @type number
78 */
79 HttpsConnection.ERR_CERTIFICATE_FILE_NOT_EXIST = -105;
80 /**
81 * Constant indicating incorrect certificate file format. Used in {@link anonymous.sendRequestErrorCBParameter}
82 * @memberOf HttpsConnection
83 * @type number
84 */
85 HttpsConnection.ERR_CERTIFICATE_INVALID_FILE_FORMAT = -106;
86 /**
87 * Constant indicating failed in getting certificate. Used in {@link anonymous.sendRequestErrorCBParameter}
88 * @memberOf HttpsConnection
89 * @type number
90 */
91 HttpsConnection.ERR_GET_CERTIFICATE_FAILED = -107;
92 /**
93 * Constant indicating the provided certificate failed validation on server side. Used in {@link anonymous.sendRequestErrorCBParameter}
94 * @memberOf HttpsConnection
95 * @type number
96 */
97 HttpsConnection.ERR_CLIENT_CERTIFICATE_VALIDATION = -108;
98 /**
99 * Constant indicating the server certificate failed validation on client side. Used in {@link anonymous.sendRequestErrorCB}
100 * @memberOf HttpsConnection
101 * @type number
102 */
103 HttpsConnection.ERR_SERVER_CERTIFICATE_VALIDATION = -109;
104 /**
105 * Constant indicating the server request failed. Used in {@link anonymous.sendRequestErrorCB}
106 * @memberOf HttpsConnection
107 * @type number
108 */
109 HttpsConnection.ERR_SERVER_REQUEST_FAILED = -110;
110 /**
111 * Constant indicating timeout error while connecting to the server. Used in {@link anonymous.sendRequestErrorCB}
112 * @memberOf HttpsConnection
113 * @type number
114 */
115 HttpsConnection.ERR_HTTP_TIMEOUT = -120;
116
117 /**
118 * Create certificate source description object for certificates from a keystore file.
119 * <b> Not supported on Blackberry platform </b>
120 * @class
121 * @public
122 * @memberOf HttpsConnection
123 * @param {string} Path Path of the keystore file. For iOS client, it first tries to load the
124 * relative file path from application's Documents folder; if it fails, then tries
125 * to load the file path from application's main bundle. In addition, before trying
126 * to load the certificate from file system, iOS client first checks whether the
127 * specified certificate key already exists in the key store, if so, it just loads
128 * the existing certificate from key store, instead of loading the certificate from
129 * file system.
130 * @param {string} Password Password of the keystore.
131 * @param {string} CertificateKey An unique key that will be used to locate the certificate.
132 */
133 HttpsConnection.CertificateFromFile = function (Path, Password, CertificateKey) {
134 this.Source = "FILE";
135 this.Path = Path;
136 this.Password = Password;
137 this.CertificateKey = CertificateKey;
138 };
139
140 /**
141 * Create certificate source description object for certificates from Afaria.
142 * @class
143 * @memberOf HttpsConnection
144 * @public
145 * @param {string} CN Common Name (CN) for CA/SCEP protocol. For iOS, the retrieved certificate is
146 * stored in the key store with the common name as the certificate key, the
147 * following requests for the same common name will just load the saved certificate
148 * from key store, instead of sending a new request to Afaria server.
149 * @param {string} [ChallengeCode] Challenge code for CA/SCEP protocol.
150 */
151 HttpsConnection.CertificateFromAfaria = function (CN, ChallengeCode) {
152 this.Source = "AFARIA";
153 this.CN = CN;
154 this.ChallengeCode = ChallengeCode;
155 };
156
157 /**
158 * Create certificate source description object for certificates from system keystore (Keystore in BB, Keychain in iOS and Android).
159 * The certificateKey is not used on the BB platform. BB will prompt the user to select a certificate if a certificate was not already
160 * used for the server connection.
161 * @class
162 * @memberOf HttpsConnection
163 * @public
164 * @param {string} CertificateKey An unique key that will be used to locate the certificate. Not used in BB platform.
165 */
166 HttpsConnection.CertificateFromStore = function (CertificateKey) {
167 this.Source = "SYSTEM";
168 this.CertificateKey = CertificateKey;
169 };
170
171 HttpsConnection.MSG_MISSING_PARAMETER = "Missing a required parameter: ";
172 HttpsConnection.MSG_INVALID_PARAMETER_VALUE = "Invalid Parameter Value for parameter: ";
173
174 /**
175 * @private
176 */
177 HttpsConnection.validateCertSource = function(certSource, errorCB) {
178 if (!certSource) {
179 // The certificate is not present, so just ignore it.
180 return true;
181 }
182
183 // errorCB required.
184 // First check this one. We may need it to return errors
185 if (errorCB && (typeof errorCB !== "function")) {
186 console.log("HttpsConnection Error: errorCB is not a function");
187 return false;
188 }
189
190 try {
191 // First check whether it is an object
192 if (typeof certSource !== "object") {
193 errorCB({errorCode : HttpsConnection.ERR_INVALID_PARAMETER_VALUE, description : HttpsConnection.MSG_INVALID_PARAMETER_VALUE + "certSource"});
194 return false;
195 }
196
197 if (certSource.Source === "FILE") {
198 if (!certSource.Path) {
199 errorCB({errorCode : HttpsConnection.ERR_INVALID_PARAMETER_VALUE, description : HttpsConnection.MSG_MISSING_PARAMETER + "keystore path"});
200 return false;
201 }
202
203 if (typeof certSource.Path !== "string") {
204 errorCB({errorCode : HttpsConnection.ERR_INVALID_PARAMETER_VALUE, description : HttpsConnection.MSG_INVALID_PARAMETER_VALUE + "keystore path"});
205 return false;
206 }
207
208 if (!certSource.Password) {
209 errorCB({errorCode : HttpsConnection.ERR_INVALID_PARAMETER_VALUE, description : HttpsConnection.MSG_MISSING_PARAMETER + "keystore password"});
210 return false;
211 }
212
213 if (typeof certSource.Password !== "string") {
214 errorCB({errorCode : HttpsConnection.ERR_INVALID_PARAMETER_VALUE, description : HttpsConnection.MSG_INVALID_PARAMETER_VALUE + "keystore password"});
215 return false;
216 }
217
218 if (!certSource.CertificateKey) {
219 errorCB({errorCode : HttpsConnection.ERR_INVALID_PARAMETER_VALUE, description : HttpsConnection.MSG_MISSING_PARAMETER + "certificate key"});
220 return false;
221 }
222
223 if (typeof certSource.CertificateKey !== "string") {
224 errorCB({errorCode : HttpsConnection.ERR_INVALID_PARAMETER_VALUE, description : HttpsConnection.MSG_INVALID_PARAMETER_VALUE + "certificate key"});
225 return false;
226 }
227 } else if (certSource.Source === "SYSTEM") {
228 if (!certSource.CertificateKey) {
229 errorCB({errorCode : HttpsConnection.ERR_INVALID_PARAMETER_VALUE, description : HttpsConnection.MSG_MISSING_PARAMETER + "certificate key"});
230 return false;
231 }
232
233 if (typeof certSource.CertificateKey !== "string") {
234 errorCB({errorCode : HttpsConnection.ERR_INVALID_PARAMETER_VALUE, description : HttpsConnection.MSG_INVALID_PARAMETER_VALUE + "certificate key"});
235 return false;
236 }
237 } else if (certSource.Source === "AFARIA") {
238 if (!certSource.CN) {
239 errorCB({errorCode : HttpsConnection.ERR_INVALID_PARAMETER_VALUE, description : HttpsConnection.MSG_MISSING_PARAMETER + "common name"});
240 return false;
241 }
242
243 if (typeof certSource.CN !== "string") {
244 errorCB({errorCode : HttpsConnection.ERR_INVALID_PARAMETER_VALUE, description : HttpsConnection.MSG_INVALID_PARAMETER_VALUE + "common name"});
245 return false;
246 }
247
248 if (!certSource.ChallengeCode) {
249 errorCB({errorCode : HttpsConnection.ERR_INVALID_PARAMETER_VALUE, description : HttpsConnection.MSG_MISSING_PARAMETER + "Afaria challenge code"});
250 return false;
251 }
252
253 if (typeof certSource.ChallengeCode !== "string") {
254 errorCB({errorCode : HttpsConnection.ERR_INVALID_PARAMETER_VALUE, description : HttpsConnection.MSG_INVALID_PARAMETER_VALUE + "Afaria challenge code"});
255 return false;
256 }
257 } else {
258 errorCB({errorCode : HttpsConnection.ERR_INVALID_PARAMETER_VALUE, description : HttpsConnection.MSG_INVALID_PARAMETER_VALUE + "certSource"});
259 return false;
260 }
261
262 return true;
263 } catch (ex) {
264 errorCB({errorCode : HttpsConnection.ERR_INVALID_PARAMETER_VALUE, description : HttpsConnection.MSG_INVALID_PARAMETER_VALUE + "certSource"});
265 }
266 };
267
268 /**
269 * Send a HTTP(S) request to a remote server.
270 * @memberOf HttpsConnection
271 * @public
272 * @param {string} method Standard HTTP request method name.
273 * @param {string} url The http url with format http(s)://[user:password]@hostname[:port]/path.
274 * @param {Object} header HTTP header to be sent to server. This is an Object. Can be null.
275 * @param {string} requestBody Data to be sent to server with the request. It’s a string value. Can be null.
276 * @param {anonymous.sendRequestSuccessCB} successCB Callback method upon success.
277 * @param {anonymous.sendRequestErrorCB} errorCB Callback method upon failure.
278 * @param {string} [user] User ID for basic authentication.
279 * @param {string} [password] User password for basic authentication.
280 * @param {number} [timeout] Timeout setting in seconds.
281 * @param {Object} [certSource] Certificate description object. It can be one of {@link HttpsConnection.CertificateFromFile},
282 * {@link HttpsConnection.CertificateFromStore}, or {@link HttpsConnection.CertificateFromAfaria}.
283 * @return {anonymous.abort} A JavaScript function object to cancel the operation.
284 * @example
285 * // To send a post request to server, call the method
286 * HttpsConnection.sendRequest("POST", "http://www.google.com", null, "THIS IS THE BODY", function (data) {
287 * alert("Status: " + JSON.stringify(data.status));
288 * alert("Headers: " + JSON.stringify(data.headers));
289 * alert("Response: " + JSON.stringify(data.response));
290 * }, function (data) {
291 * alert("Failed: " + JSON.stringify(data));}};
292 * // To send a post request to server with headers, call the method
293 * HttpsConnection.sendRequest("POST", url, {HeaderName : "Header value"}, "THIS IS THE BODY", successCB, errorCB);
294 * // To send a post request to server with basic authentication, call the method
295 * HttpsConnection.sendRequest("POST", url, headers, "THIS IS THE BODY", successCB, errorCB, "username", "password");
296 * // To send a post request to server with mutual authentication, call the method
297 * HttpsConnection.sendRequest("POST", "https://hostname", headers, "THIS IS THE BODY", successCB, errorCB, null,
298 * null, 0, new CertificateFromFile("/mnt/sdcard/my.keystore", "password", "mykey"));
299 */
300 HttpsConnection.sendRequest = function (method, url, header, requestBody, successCB, errorCB, user, password, timeout, certSource){
301
302 // errorCB required.
303 // First check this one. We may need it to return errors
304 if (!errorCB || (typeof errorCB !== "function")) {
305 console.log("HttpsConnection Error: errorCB is not a function");
306 // if error callback is invalid, throw an exception to notify the caller
307 throw new Error("HttpsConnection Error: errorCB is not a function");
308 return;
309 }
310
311 // method required
312 if (!method) {
313 console.log("HttpsConnection Error: method is required");
314 errorCB({errorCode : HttpsConnection.ERR_MISSING_PARAMETER, description : HttpsConnection.MSG_MISSING_PARAMETER + "method"});
315 return;
316 }
317
318 // We only support GET, POST, HEAD, PUT, DELETE method
319 if (method !== "GET" && method !== "POST" && method !== "HEAD" && method !== "PUT" && method !== "DELETE") {
320 console.log("Invalid Parameter Value for parameter: " + method);
321 errorCB({errorCode : HttpsConnection.ERR_INVALID_PARAMETER_VALUE, description : HttpsConnection.MSG_INVALID_PARAMETER_VALUE + "method"});
322 return;
323 }
324
325 // url required
326 if (!url) {
327 console.log("HttpsConnection Error: url is required");
328 errorCB({errorCode : HttpsConnection.ERR_MISSING_PARAMETER, description : HttpsConnection.MSG_MISSING_PARAMETER + "url"});
329 return;
330 }
331
332 // successCB required
333 if (!successCB) {
334 console.log("HttpsConnection Error: successCB is required");
335 errorCB({errorCode : HttpsConnection.ERR_MISSING_PARAMETER, description : HttpsConnection.MSG_MISSING_PARAMETER + "successCB"});
336 return;
337 }
338
339 if (typeof successCB !== "function") {
340 console.log("HttpsConnection Error: successCB is not a function");
341 errorCB({errorCode : HttpsConnection.ERR_INVALID_PARAMETER_VALUE, description : HttpsConnection.MSG_INVALID_PARAMETER_VALUE + "successCB"});
342 return;
343 }
344
345 if (user && typeof user !== "string") {
346 errorCB({errorCode : HttpsConnection.ERR_INVALID_PARAMETER_VALUE, description : HttpsConnection.MSG_INVALID_PARAMETER_VALUE + "user"});
347 return;
348 }
349
350 if (password && typeof password !== "string") {
351 errorCB({errorCode : HttpsConnection.ERR_INVALID_PARAMETER_VALUE, description : HttpsConnection.MSG_INVALID_PARAMETER_VALUE + "password"});
352 return;
353 }
354
355 if (timeout && typeof timeout !== "number") {
356 errorCB({errorCode : HttpsConnection.ERR_INVALID_PARAMETER_VALUE, description : HttpsConnection.MSG_INVALID_PARAMETER_VALUE + "timeout"});
357 return;
358 }
359
360 if (!HttpsConnection.validateCertSource(certSource, errorCB)) {
361 return;
362 }
363
364 try {
365 var client = new HttpsConnection.Client(method, url, header, requestBody, successCB, errorCB, user, password, timeout, certSource);
366 return client.send();
367 } catch (ex){
368 errorCB({errorCode : HttpsConnection.ERR_UNKNOWN, description : ex.message});
369 }
370 };
371
372 /**
373 * Send a HTTP(S) GET request to a remote server.
374 * @memberOf HttpsConnection
375 * @public
376 * @param {string} url The http url with format http(s)://[user:password]@hostname[:port]/path.
377 * @param {Object} header HTTP header to be sent to server. This is an Object. Can be null.
378 * @param {string} requestBody Data to be sent to server with the request. It’s a string value. Can be null.
379 * @param {anonymous.sendRequestSuccessCB} successCB Callback method upon success.
380 * @param {anonymous.sendRequestErrorCB} [errorCB] Callback method upon failure.
381 * @param {string} [user] User ID for basic authentication.
382 * @param {string} [password] User password for basic authentication.
383 * @param {number} [timeout] Timeout setting in seconds.
384 * @param {Object} [certSource] Certificate description object. It can be one of {@link HttpsConnection.CertificateFromFile},
385 * {@link HttpsConnection.CertificateFromStore}, or {@link HttpsConnection.CertificateFromAfaria}.
386 * @return {anonymous.abort} A JavaScript function object to cancel the operation.
387 * @example
388 * // To send a get request to server, call the method
389 * HttpsConnection.get("http://www.google.com", null, function (data) {
390 * alert("Status: " + JSON.stringify(data.status));
391 * alert("Headers: " + JSON.stringify(data.headers));
392 * if (data.responseText){
393 * alert("Response: " + JSON.stringify(data.responseText));
394 * }
395 * },
396 * function (error) {
397 * alert("Failed: " + JSON.stringify(error));
398 * }};
399 * // To send a get request to server with headers, call the method
400 * HttpsConnection.get(url, {HeaderName : "Header value"}, successCB, errorCB);
401 * // To send a get request to server with basic authentication, call the method
402 * HttpsConnection.get(url, headers, successCB, errorCB, "username", "password");
403 * // To send a get request to server with mutual authentication, call the method
404 * HttpsConnection.get("https://hostname", headers, successCB, errorCB, null, null, 0,
405 * new CertificateFromFile("/mnt/sdcard/my.p12", "password", "mykey"));
406 */
407 HttpsConnection.get = function (url, header, successCB, errorCB, user, password, timeout, certSource){
408 return HttpsConnection.sendRequest("GET", url, header, null, successCB, errorCB, user, password, timeout, certSource);
409 };
410
411 /**
412 * Delete cached certificate from keychain. iOS client will always try the cached certificate first if it is available before requesting the certificate from
413 * afaria server or loading the certificate from file system. In case the cached certificate is no longer valid, use this method to delete it from keychain
414 * <b> Only supported by iOS platform </b>
415 * @memberOf HttpsConnection
416 * @public
417 * @param {anonymous.sendRequestSuccessCB} successCB Callback method upon success.
418 * @param {anonymous.sendRequestErrorCB} [errorCB] Callback method upon failure.
419 * @param {string} certificateKey The key of the certificate to be deleted.
420 */
421 HttpsConnection.deleteCertificateFromStore = function (successCB, errorCB, certificateKey) {
422 cordova.exec(successCB, errorCB, "HttpsProxy", "deleteCertificateFromStore", [certificateKey]);
423 };
424
425
426 /**
427 * @private
428 */
429 HttpsConnection.Client = function ( method, url, header, requestBody, successCB, errorCB, user, password, timeout, certSource )
430 {
431 //ios plugin parameter does not support object type, convert Header and CertSource to JSON string
432 if (device.platform === "iOS" || (device.platform && device.platform.indexOf("iP") === 0 ))
433 {
434 if (header) {
435 header = JSON.stringify(header);
436 }
437 if (certSource) {
438 certSource = JSON.stringify(certSource);
439 }
440 }
441
442 this.Method = method;
443 this.Url = url;
444 this.Header = header;
445 this.RequestBody = requestBody;
446 this.SuccessCB = successCB;
447 this.ErrorCB = errorCB;
448 this.User = user;
449 this.Password = password;
450 this.Timeout = timeout;
451 this.CertSource = certSource;
452 this.IsAbort = false;
453
454 this.abort = function ()
455 {
456 this.IsAbort = true;
457 };
458
459 this.send = function ()
460 {
461 var args = [this.Method, this.Url, this.Header, this.RequestBody, this.User, this.Password, this.Timeout, this.CertSource];
462
463 var me = this;
464
465 var successCallBack = function(data)
466 {
467 if (me.IsAbort === true)
468 {
469 return;
470 }
471
472 successCB(data);
473 };
474
475 var errorCallBack = function(data)
476 {
477 if (me.IsAbort === true)
478 {
479 return;
480 }
481
482 errorCB(data);
483 };
484
485 cordova.exec(successCallBack, errorCallBack, "HttpsProxy", "sendRequest", args);
486
487 return this.abort;
488 };
489 };
490
491 })(this);
492
493 /**
494 * Used to group anonymous objects and callback functions used as method parameters. Methods and fields in this
495 * namespace cannot be instantiated. Used for API docs generation only.
496 * @namespace
497 */
498 anonymous = (typeof anonymous === "undefined" || !anonymous) ? {} : anonymous; // SUP 'namespace'
499
500 /**
501 * Callback function that will be invoked HttpsConnection.get()/sendRequest() succeeded.
502 *
503 * @name anonymous.sendRequestSuccessCB
504 *
505 * @param {anonymous.sendRequestSuccessCBParameter} data The response data object.
506 * @function
507 */
508
509 /**
510 * Callback function that will be invoked HttpsConnection.get()/sendRequest() failed.
511 *
512 * @name anonymous.sendRequestErrorCB
513 * @param {anonymous.sendRequestErrorCBParameter} data The error object.
514 * @function
515 */
516
517 /**
518 * Object used in {@link anonymous.sendRequestSuccessCB} function.
519 * @class
520 * @name anonymous.sendRequestSuccessCBParameter
521 * @property {number} status The HTTP status code
522 * @property {object} headers An object that contains headerKey = value pairs.
523 * @property {string} [responseText] The text response. This parameter is present only if the response is a text response.
524 * @property {string} [responseBase64] Base64 encoded representation of the binary response. This parameter is included only
525 * if the response is a binary response.
526 * @property {object} [clientError] An optional object that contains the authentication error. It is an object of {@link anonymous.sendRequestErrorCBParameter}.
527 */
528
529 /**
530 * Object used in {@link anonymous.sendRequestErrorCB} function.
531 * @class
532 * @name anonymous.sendRequestErrorCBParameter
533 * @property {number} errorCode Predefined error code
534 * @property {string} description The description of the error
535 * @property {number} [nativeErrorCode] The native error code reported from Afaria, device, etc (optional)
536 */
537
538
539 /**
540 * JavaScript function to abort the HTTP(S) request
541 *
542 * @name anonymous.abort
543 *
544 * @function
545 */
546