1 /* ***** BEGIN LICENSE BLOCK ***** 2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 3 * 4 * The contents of this file are subject to the Mozilla Public License Version 5 * 1.1 (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * http://www.mozilla.org/MPL/ 8 * 9 * Software distributed under the License is distributed on an "AS IS" basis, 10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 11 * for the specific language governing rights and limitations under the 12 * License. 13 * 14 * The Original Code is gContactSync. 15 * 16 * The Initial Developer of the Original Code is 17 * Josh Geenen <gcontactsync@pirules.org>. 18 * Portions created by the Initial Developer are Copyright (C) 2008-2009 19 * the Initial Developer. All Rights Reserved. 20 * 21 * Contributor(s): 22 * 23 * Alternatively, the contents of this file may be used under the terms of 24 * either the GNU General Public License Version 2 or later (the "GPL"), or 25 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), 26 * in which case the provisions of the GPL or the LGPL are applicable instead 27 * of those above. If you wish to allow use of your version of this file only 28 * under the terms of either the GPL or the LGPL, and not to allow others to 29 * use your version of this file under the terms of the MPL, indicate your 30 * decision by deleting the provisions above and replace them with the notice 31 * and other provisions required by the GPL or the LGPL. If you do not delete 32 * the provisions above, a recipient may use your version of this file under 33 * the terms of any one of the MPL, the GPL or the LGPL. 34 * 35 * ***** END LICENSE BLOCK ***** */ 36 37 if (!com) var com = {}; // A generic wrapper variable 38 // A wrapper for all GCS functions and variables 39 if (!com.gContactSync) com.gContactSync = {}; 40 41 /** 42 * Sets up an HTTP request to send to Google. 43 * After calling this constructor and setting up any additional data, call the 44 * send method. 45 * 46 * @param aType {string} The type of request. Must be one of the following 47 * authenticate, getAll, get, update, add, delete, 48 * getGroups 49 * @param aAuth {string} The authorization token. 50 * @param aUrl {string} The url for the request, if unique for the type of 51 * request. Not required for authenticate, getAll, 52 * getGroups, and add. 53 * @param aBody {string} The body of the request. 54 * @param aUsername {string} Optional. Replaces "default" in the URL. 55 * @param aOther {string} Additional parameter to use when needed. 56 * Currently this is only used for GET requests for 57 * obtaining contacts in a specified group (pass the 58 * Group ID in that case) 59 * @constructor 60 * @class 61 * @extends com.gContactSync.HttpRequest 62 */ 63 com.gContactSync.GHttpRequest = function gCS_GHttpRequest(aType, aAuth, aUrl, aBody, aUsername, aOther) { 64 com.gContactSync.HttpRequest.call(this); // call the superclass' constructor 65 this.mBody = aBody; 66 // all urls in gdata use SSL. If a URL is supplied, make sure it uses SSL 67 if (aUrl && aUrl.indexOf("https://") < 0) 68 aUrl = aUrl.replace("http://", "https://"); 69 switch (aType) { 70 case "AUTH_SUB_SESSION": 71 case "authsubsession": 72 this.mContentType = this.CONTENT_TYPES.URL_ENC; 73 this.mUrl = com.gContactSync.gdata.AUTH_SUB_SESSION_URL; 74 this.mType = com.gContactSync.gdata.AUTH_SUB_SESSION_TYPE; 75 break; 76 case "AUTHENTICATE": 77 case "authenticate": 78 this.mContentType = this.CONTENT_TYPES.URL_ENC; 79 this.mUrl = com.gContactSync.gdata.AUTH_URL; 80 this.mType = com.gContactSync.gdata.AUTH_REQUEST_TYPE; 81 break; 82 case "GETALL": 83 case "getAll": 84 this.mContentType = this.CONTENT_TYPES.ATOM; 85 this.mUrl = com.gContactSync.gdata.contacts.GET_ALL_URL + 86 com.gContactSync.Preferences.mSyncPrefs.maxContacts 87 .value; 88 this.mType = com.gContactSync.gdata.contacts.requestTypes.GET_ALL; 89 this.addHeaderItem("Authorization", aAuth); 90 break; 91 case "getFromGroup": 92 this.mContentType = this.CONTENT_TYPES.ATOM; 93 this.mUrl = com.gContactSync.gdata.contacts.GET_ALL_URL + 94 com.gContactSync.Preferences.mSyncPrefs.maxContacts.value + 95 "&group=" + encodeURIComponent(aOther); 96 this.mType = com.gContactSync.gdata.contacts.requestTypes.GET_ALL; 97 this.addHeaderItem("Authorization", aAuth); 98 break; 99 case "GETGROUPS": 100 case "getGroups": 101 this.mContentType = this.CONTENT_TYPES.ATOM; 102 this.mUrl = com.gContactSync.gdata.contacts.GROUPS_URL; 103 this.mType = com.gContactSync.gdata.contacts.requestTypes.GET; 104 this.addHeaderItem("Authorization", aAuth); 105 break; 106 case "GET": 107 case "get": 108 this.mContentType = this.CONTENT_TYPES.ATOM; 109 this.mUrl = aUrl; // the URL is unique and needs to be passed in 110 this.mType = com.gContactSync.gdata.contacts.requestTypes.GET; 111 this.addHeaderItem("Authorization", aAuth); 112 break; 113 case "UPDATE": 114 case "update": 115 case "updategroup": 116 this.mContentType = this.CONTENT_TYPES.ATOM; 117 this.mUrl = aUrl; 118 this.mType = "POST"; // for firewalls that block PUT requests 119 this.addContentOverride(com.gContactSync.gdata.contacts.requestTypes.UPDATE); 120 this.addHeaderItem("Authorization", aAuth); 121 break; 122 case "ADD": 123 case "add": 124 this.mContentType = this.CONTENT_TYPES.ATOM; 125 this.mUrl = com.gContactSync.gdata.contacts.ADD_URL; 126 this.mType = com.gContactSync.gdata.contacts.requestTypes.ADD; 127 this.addHeaderItem("Authorization", aAuth); 128 break; 129 case "addGroup": 130 this.mContentType = this.CONTENT_TYPES.ATOM; 131 this.mUrl = com.gContactSync.gdata.contacts.ADD_GROUP_URL; 132 this.mType = com.gContactSync.gdata.contacts.requestTypes.ADD; 133 this.addHeaderItem("Authorization", aAuth); 134 break; 135 case "DELETE": 136 case "delete": 137 this.mContentType = this.CONTENT_TYPES.URL_ENC; 138 this.mUrl = aUrl; 139 this.mType = "POST"; // for firewalls that block DELETE 140 this.addContentOverride(com.gContactSync.gdata.contacts.requestTypes.DELETE); 141 this.addHeaderItem("Content-length", 0); // required or there will be an error 142 this.addHeaderItem("Authorization", aAuth); 143 break; 144 default: 145 // if the input doesn't match one of the above throw an error 146 throw "Invalid aType parameter supplied to the " + 147 "com.gContactSync.GHttpRequest constructor" + 148 com.gContactSync.StringBundle.getStr("pleaseReport"); 149 } 150 // use version 3 of the contacts api 151 this.addHeaderItem("GData-Version", "3"); 152 // handle Token Expired errors 153 this.mOn401 = com.gContactSync.handle401; 154 if (!this.mUrl) 155 throw "Error - no URL was found for the HTTP Request"; 156 if (aUsername && this.mUrl) 157 this.mUrl = this.mUrl.replace("default", 158 encodeURIComponent(com.gContactSync.fixUsername(aUsername))); 159 }; 160 161 // get the superclass' prototype 162 com.gContactSync.GHttpRequest.prototype = new com.gContactSync.HttpRequest(); 163 164 /** 165 * Handles 'Token Expired' errors. 166 * If a sync is in progress: 167 * - Get the username 168 * - Remove the auth token 169 * - Alert the user 170 * - Prompt for the password 171 * - Get a new auth token to replace the old one 172 * - Restart the sync 173 */ 174 com.gContactSync.handle401 = function gCS_handle401(httpRequest) { 175 com.gContactSync.LOGGER.LOG("***Found an expired token***"); 176 // If there is a synchronization in process 177 if (com.gContactSync.Preferences.mSyncPrefs.synchronizing.value) { 178 // Get the current username 179 var username = com.gContactSync.Sync.mCurrentUsername; 180 // Remove the auth token if it wasn't already 181 if (com.gContactSync.LoginManager.mAuthTokens[username]) { 182 com.gContactSync.LOGGER.VERBOSE_LOG(" * Removing old auth token"); 183 com.gContactSync.LoginManager.removeAuthToken(username); 184 } 185 com.gContactSync.alertWarning(com.gContactSync.StringBundle.getStr("tokenExpiredMsg")); 186 // Prompt for the username and password 187 var prompt = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] 188 .getService(Components.interfaces.nsIPromptService) 189 .promptUsernameAndPassword, 190 password = {}; 191 // set the username 192 username = { value: username }; 193 com.gContactSync.LOGGER.VERBOSE_LOG(" * Showing a username/password prompt"); 194 // opens a username/password prompt 195 var ok = prompt(window, com.gContactSync.StringBundle.getStr("loginTitle"), 196 com.gContactSync.StringBundle.getStr("loginText"), username, 197 password, null, {value: false}); 198 if (!ok) { 199 com.gContactSync.LOGGER.VERBOSE_LOG(" * User canceled the prompt"); 200 com.gContactSync.Sync.finish(com.gContactSync.StringBundle.getStr("tokenExpired"), false); 201 return false; 202 } 203 // This is a primitive way of validating an e-mail address, but Google takes 204 // care of the rest. It seems to allow getting an auth token w/ only the 205 // username, but returns an error when trying to do anything w/ that token 206 // so this makes sure it is a full e-mail address. 207 if (username.value.indexOf("@") < 1) { 208 com.gContactSync.alertError(com.gContactSync.StringBundle.getStr("invalidEmail")); 209 return com.gContactSync.handle401(); 210 } 211 // fix the username before authenticating 212 username.value = com.gContactSync.fixUsername(username.value); 213 var body = com.gContactSync.gdata.makeAuthBody(username.value, password.value); 214 var httpReq = new com.gContactSync.GHttpRequest("authenticate", null, null, body); 215 // if it succeeds and Google returns the auth token, store it and then start 216 // a new sync 217 httpReq.mOnSuccess = function fix401Success(httpReq) { 218 com.gContactSync.LOGGER.VERBOSE_LOG(com.gContactSync 219 .serializeFromText(httpReq.responseText)); 220 com.gContactSync.finish401(username.value, 221 httpReq.responseText.split("\n")[2]); 222 }; 223 // if it fails, alert the user and prompt them to try again 224 httpReq.mOnError = function fix401Error(httpReq) { 225 com.gContactSync.alertError(com.gContactSync.StringBundle.getStr('authErr')); 226 com.gContactSync.LOGGER.LOG_ERROR('Authentication Error - ' + 227 httpReq.status, 228 httpReq.responseText); 229 com.gContactSync.handle401(); 230 }; 231 // if the user is offline, alert them and quit 232 httpReq.mOnOffline = function fix401Offline(httpReq) { 233 com.gContactSync.alertError(com.gContactSync.StringBundle.getStr('offlineErr')); 234 com.gContactSync.LOGGER.LOG_ERROR(com.gContactSync.StringBundle.getStr('offlineErr')); 235 }; 236 httpReq.send(); 237 } 238 }; 239 240 /** 241 * Called after the re-authentication HTTP request is sent after a 401 error 242 * @param aUsername {string} The account's username. 243 * @param aAuthToken {string} An authentication token for the account. 244 */ 245 com.gContactSync.finish401 = function gCS_finish401(aUsername, aAuthToken) { 246 com.gContactSync.LOGGER.VERBOSE_LOG(" * finish401 called: " + aUsername + 247 " - " + aAuthToken); 248 if (aUsername && aAuthToken) { 249 var token = 'GoogleLogin ' + aAuthToken; 250 com.gContactSync.LoginManager.addAuthToken(aUsername, token); 251 com.gContactSync.Sync.mCurrentAuthToken = token; 252 if (com.gContactSync.Preferences.mSyncPrefs.syncGroups.value || 253 com.gContactSync.Preferences.mSyncPrefs.myContacts) 254 com.gContactSync.Sync.getGroups(); 255 else 256 com.gContactSync.Sync.getContacts(); 257 } 258 }; 259