1 2 #include <petscwebclient.h> 3 PETSC_PRAGMA_DIAGNOSTIC_IGNORED_BEGIN("-Wdeprecated-declarations") 4 5 /* 6 These variables identify the code as a PETSc application to Box. 7 8 See - https://stackoverflow.com/questions/4616553/using-oauth-in-free-open-source-software 9 Users can get their own application IDs - goto https://developer.box.com 10 11 */ 12 #define PETSC_BOX_CLIENT_ID "sse42nygt4zqgrdwi0luv79q1u1f0xza" 13 #define PETSC_BOX_CLIENT_ST "A0Dy4KgOYLB2JIYZqpbze4EzjeIiX5k4" 14 15 #if defined(PETSC_HAVE_SAWS) 16 #include <mongoose.h> 17 18 static volatile char *result = NULL; 19 20 static int PetscBoxWebServer_Private(struct mg_connection *conn) 21 { 22 const struct mg_request_info *request_info = mg_get_request_info(conn); 23 result = (char *)request_info->query_string; 24 return 1; /* Mongoose will now not handle the request */ 25 } 26 27 /* 28 Box can only return an authorization code to a Webserver, hence we need to start one up and wait for 29 the authorization code to arrive from Box 30 */ 31 static PetscErrorCode PetscBoxStartWebServer_Private(void) 32 { 33 int optionsLen = 5; 34 const char *options[optionsLen]; 35 struct mg_callbacks callbacks; 36 struct mg_context *ctx; 37 char keyfile[PETSC_MAX_PATH_LEN]; 38 PetscBool exists; 39 40 PetscFunctionBegin; 41 options[0] = "listening_ports"; 42 options[1] = "8081s"; 43 44 PetscCall(PetscStrncpy(keyfile, "sslclient.pem", sizeof(keyfile))); 45 PetscCall(PetscTestFile(keyfile, 'r', &exists)); 46 if (!exists) { 47 PetscCall(PetscGetHomeDirectory(keyfile, PETSC_MAX_PATH_LEN)); 48 PetscCall(PetscStrlcat(keyfile, "/", sizeof(keyfile))); 49 PetscCall(PetscStrlcat(keyfile, "sslclient.pem", sizeof(keyfile))); 50 PetscCall(PetscTestFile(keyfile, 'r', &exists)); 51 PetscCheck(exists, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Unable to locate sslclient.pem file in current directory or home directory"); 52 } 53 54 options[2] = "ssl_certificate"; 55 options[3] = keyfile; 56 options[4] = NULL; 57 58 /* Prepare callbacks structure. We have only one callback, the rest are NULL. */ 59 PetscCall(PetscMemzero(&callbacks, sizeof(callbacks))); 60 callbacks.begin_request = PetscBoxWebServer_Private; 61 ctx = mg_start(&callbacks, NULL, options); 62 PetscCheck(ctx, PETSC_COMM_SELF, PETSC_ERR_LIB, "Unable to start up webserver"); 63 while (!result) { }; 64 PetscFunctionReturn(PETSC_SUCCESS); 65 } 66 67 #if defined(PETSC_HAVE_UNISTD_H) 68 #include <unistd.h> 69 #endif 70 71 /*@C 72 PetscBoxAuthorize - Get authorization and refresh token for accessing Box drive from PETSc 73 74 Not collective, only the first rank in `MPI_Comm` does anything 75 76 Input Parameters: 77 + comm - the MPI communicator 78 - tokensize - size of the token arrays 79 80 Output Parameters: 81 + access_token - can be used with `PetscBoxUpload()` for this one session 82 - refresh_token - can be used for ever to obtain new access_tokens with `PetscBoxRefresh()`, guard this like a password 83 it gives access to your Box Drive 84 85 Notes: 86 This call requires stdout and stdin access from process 0 on the MPI communicator 87 88 You can run src/sys/webclient/tutorials/boxobtainrefreshtoken to get a refresh token and then in the future pass it to 89 PETSc programs with -box_refresh_token XXX 90 91 This requires PETSc be installed using --with-saws or --download-saws 92 93 Requires the user have created a self-signed ssl certificate with 94 95 $ saws/CA.pl -newcert (using the passphrase of password) 96 $ cat newkey.pem newcert.pem > sslclient.pem 97 98 and put the resulting file in either the current directory (with the application) or in the home directory. This seems kind of 99 silly but it was all I could figure out. 100 101 Level: intermediate 102 103 .seealso: `PetscBoxRefresh()`, `PetscBoxUpload()`, `PetscURLShorten()` 104 @*/ 105 PetscErrorCode PetscBoxAuthorize(MPI_Comm comm, char access_token[], char refresh_token[], size_t tokensize) 106 { 107 SSL_CTX *ctx; 108 SSL *ssl; 109 int sock; 110 char buff[8 * 1024], body[1024]; 111 PetscMPIInt rank; 112 PetscBool flg, found; 113 114 PetscFunctionBegin; 115 PetscCallMPI(MPI_Comm_rank(comm, &rank)); 116 if (rank == 0) { 117 PetscCheck(isatty(fileno(PETSC_STDOUT)), PETSC_COMM_SELF, PETSC_ERR_USER, "Requires users input/output"); 118 PetscCall(PetscPrintf(comm, "Cut and paste the following into your browser:\n\n" 119 "https://www.box.com/api/oauth2/authorize?" 120 "response_type=code&" 121 "client_id=" PETSC_BOX_CLIENT_ID "&state=PETScState" 122 "\n\n")); 123 PetscCall(PetscBoxStartWebServer_Private()); 124 PetscCall(PetscStrbeginswith((const char *)result, "state=PETScState&code=", &flg)); 125 PetscCheck(flg, PETSC_COMM_SELF, PETSC_ERR_LIB, "Did not get expected string from Box got %s", result); 126 PetscCall(PetscStrncpy(buff, (const char *)result + 22, sizeof(buff))); 127 128 PetscCall(PetscSSLInitializeContext(&ctx)); 129 PetscCall(PetscHTTPSConnect("www.box.com", 443, ctx, &sock, &ssl)); 130 PetscCall(PetscStrncpy(body, "code=", sizeof(body))); 131 PetscCall(PetscStrlcat(body, buff, sizeof(body))); 132 PetscCall(PetscStrlcat(body, "&client_id=", sizeof(body))); 133 PetscCall(PetscStrlcat(body, PETSC_BOX_CLIENT_ID, sizeof(body))); 134 PetscCall(PetscStrlcat(body, "&client_secret=", sizeof(body))); 135 PetscCall(PetscStrlcat(body, PETSC_BOX_CLIENT_ST, sizeof(body))); 136 PetscCall(PetscStrlcat(body, "&grant_type=authorization_code", sizeof(body))); 137 138 PetscCall(PetscHTTPSRequest("POST", "www.box.com/api/oauth2/token", NULL, "application/x-www-form-urlencoded", body, ssl, buff, sizeof(buff))); 139 PetscCall(PetscSSLDestroyContext(ctx)); 140 close(sock); 141 142 PetscCall(PetscPullJSONValue(buff, "access_token", access_token, tokensize, &found)); 143 PetscCheck(found, PETSC_COMM_SELF, PETSC_ERR_LIB, "Box did not return access token"); 144 PetscCall(PetscPullJSONValue(buff, "refresh_token", refresh_token, tokensize, &found)); 145 PetscCheck(found, PETSC_COMM_SELF, PETSC_ERR_LIB, "Box did not return refresh token"); 146 147 PetscCall(PetscPrintf(comm, "Here is your Box refresh token, save it in a save place, in the future you can run PETSc\n")); 148 PetscCall(PetscPrintf(comm, "programs with the option -box_refresh_token %s\n", refresh_token)); 149 PetscCall(PetscPrintf(comm, "to access Box Drive automatically\n")); 150 } 151 PetscFunctionReturn(PETSC_SUCCESS); 152 } 153 #endif 154 155 /*@C 156 PetscBoxRefresh - Get a new authorization token for accessing Box drive from PETSc from a refresh token 157 158 Not collective, only the first process in the `MPI_Comm` does anything 159 160 Input Parameters: 161 + comm - MPI communicator 162 . refresh token - obtained with `PetscBoxAuthorize()`, if NULL PETSc will first look for one in the options data 163 if not found it will call `PetscBoxAuthorize()` 164 - tokensize - size of the output string access_token 165 166 Output Parameters: 167 + access_token - token that can be passed to `PetscBoxUpload()` 168 - new_refresh_token - the old refresh token is no longer valid, not this is different than Google where the same refresh_token is used forever 169 170 Level: intermediate 171 172 .seealso: `PetscURLShorten()`, `PetscBoxAuthorize()`, `PetscBoxUpload()` 173 @*/ 174 PetscErrorCode PetscBoxRefresh(MPI_Comm comm, const char refresh_token[], char access_token[], char new_refresh_token[], size_t tokensize) 175 { 176 SSL_CTX *ctx; 177 SSL *ssl; 178 int sock; 179 char buff[8 * 1024], body[1024]; 180 PetscMPIInt rank; 181 char *refreshtoken = (char *)refresh_token; 182 PetscBool found; 183 184 PetscFunctionBegin; 185 PetscCallMPI(MPI_Comm_rank(comm, &rank)); 186 if (rank == 0) { 187 if (!refresh_token) { 188 PetscBool set; 189 PetscCall(PetscMalloc1(512, &refreshtoken)); 190 PetscCall(PetscOptionsGetString(NULL, NULL, "-box_refresh_token", refreshtoken, sizeof(refreshtoken), &set)); 191 #if defined(PETSC_HAVE_SAWS) 192 if (!set) { 193 PetscCall(PetscBoxAuthorize(comm, access_token, new_refresh_token, 512 * sizeof(char))); 194 PetscCall(PetscFree(refreshtoken)); 195 PetscFunctionReturn(PETSC_SUCCESS); 196 } 197 #else 198 PetscCheck(set, PETSC_COMM_SELF, PETSC_ERR_LIB, "Must provide refresh token with -box_refresh_token XXX"); 199 #endif 200 } 201 PetscCall(PetscSSLInitializeContext(&ctx)); 202 PetscCall(PetscHTTPSConnect("www.box.com", 443, ctx, &sock, &ssl)); 203 PetscCall(PetscStrncpy(body, "client_id=", sizeof(body))); 204 PetscCall(PetscStrlcat(body, PETSC_BOX_CLIENT_ID, sizeof(body))); 205 PetscCall(PetscStrlcat(body, "&client_secret=", sizeof(body))); 206 PetscCall(PetscStrlcat(body, PETSC_BOX_CLIENT_ST, sizeof(body))); 207 PetscCall(PetscStrlcat(body, "&refresh_token=", sizeof(body))); 208 PetscCall(PetscStrlcat(body, refreshtoken, sizeof(body))); 209 if (!refresh_token) PetscCall(PetscFree(refreshtoken)); 210 PetscCall(PetscStrlcat(body, "&grant_type=refresh_token", sizeof(body))); 211 212 PetscCall(PetscHTTPSRequest("POST", "www.box.com/api/oauth2/token", NULL, "application/x-www-form-urlencoded", body, ssl, buff, sizeof(buff))); 213 PetscCall(PetscSSLDestroyContext(ctx)); 214 close(sock); 215 216 PetscCall(PetscPullJSONValue(buff, "access_token", access_token, tokensize, &found)); 217 PetscCheck(found, PETSC_COMM_SELF, PETSC_ERR_LIB, "Box did not return access token"); 218 PetscCall(PetscPullJSONValue(buff, "refresh_token", new_refresh_token, tokensize, &found)); 219 PetscCheck(found, PETSC_COMM_SELF, PETSC_ERR_LIB, "Box did not return refresh token"); 220 221 PetscCall(PetscPrintf(comm, "Here is your new Box refresh token, save it in a save place, in the future you can run PETSc\n")); 222 PetscCall(PetscPrintf(comm, "programs with the option -box_refresh_token %s\n", new_refresh_token)); 223 PetscCall(PetscPrintf(comm, "to access Box Drive automatically\n")); 224 } 225 PetscFunctionReturn(PETSC_SUCCESS); 226 } 227 228 #include <sys/stat.h> 229 230 /*@C 231 PetscBoxUpload - Loads a file to the Box Drive 232 233 This routine has not yet been written; it is just copied from Google Drive 234 235 Not collective, only the first process in the `MPI_Comm` uploads the file 236 237 Input Parameters: 238 + comm - MPI communicator 239 . access_token - obtained with `PetscBoxRefresh()`, pass NULL to have PETSc generate one 240 - filename - file to upload; if you upload multiple times it will have different names each time on Box Drive 241 242 Options Database Key: 243 . -box_refresh_token XXX - the token value 244 245 Usage Patterns: 246 .vb 247 With PETSc option -box_refresh_token XXX given 248 PetscBoxUpload(comm,NULL,filename); will upload file with no user interaction 249 250 Without PETSc option -box_refresh_token XXX given 251 PetscBoxUpload(comm,NULL,filename); for first use will prompt user to authorize access to Box Drive with their processor 252 253 With PETSc option -box_refresh_token XXX given 254 PetscBoxRefresh(comm,NULL,access_token,sizeof(access_token)); 255 PetscBoxUpload(comm,access_token,filename); 256 257 With refresh token entered in some way by the user 258 PetscBoxRefresh(comm,refresh_token,access_token,sizeof(access_token)); 259 PetscBoxUpload(comm,access_token,filename); 260 261 PetscBoxAuthorize(comm,access_token,refresh_token,sizeof(access_token)); 262 PetscBoxUpload(comm,access_token,filename); 263 .ve 264 265 Level: intermediate 266 267 .seealso: `PetscURLShorten()`, `PetscBoxAuthorize()`, `PetscBoxRefresh()` 268 @*/ 269 PetscErrorCode PetscBoxUpload(MPI_Comm comm, const char access_token[], const char filename[]) 270 { 271 SSL_CTX *ctx; 272 SSL *ssl; 273 int sock; 274 char head[1024], buff[8 * 1024], *body, *title; 275 PetscMPIInt rank; 276 struct stat sb; 277 size_t len, blen, rd; 278 FILE *fd; 279 int err; 280 281 PetscFunctionBegin; 282 PetscCallMPI(MPI_Comm_rank(comm, &rank)); 283 if (rank == 0) { 284 PetscCall(PetscStrncpy(head, "Authorization: Bearer ", sizeof(head))); 285 PetscCall(PetscStrlcat(head, access_token, sizeof(head))); 286 PetscCall(PetscStrlcat(head, "\r\n", sizeof(head))); 287 PetscCall(PetscStrlcat(head, "uploadType: multipart\r\n", sizeof(head))); 288 289 err = stat(filename, &sb); 290 PetscCheck(!err, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Unable to stat file: %s", filename); 291 len = 1024 + sb.st_size; 292 PetscCall(PetscMalloc1(len, &body)); 293 PetscCall(PetscStrncpy(body, 294 "--foo_bar_baz\r\n" 295 "Content-Type: application/json\r\n\r\n" 296 "{", 297 len)); 298 PetscCall(PetscPushJSONValue(body, "title", filename, len)); 299 PetscCall(PetscStrlcat(body, ",", len)); 300 PetscCall(PetscPushJSONValue(body, "mimeType", "text.html", len)); 301 PetscCall(PetscStrlcat(body, ",", len)); 302 PetscCall(PetscPushJSONValue(body, "description", "a file", len)); 303 PetscCall(PetscStrlcat(body, 304 "}\r\n\r\n" 305 "--foo_bar_baz\r\n" 306 "Content-Type: text/html\r\n\r\n", 307 len)); 308 PetscCall(PetscStrlen(body, &blen)); 309 fd = fopen(filename, "r"); 310 PetscCheck(fd, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Unable to open file: %s", filename); 311 rd = fread(body + blen, sizeof(unsigned char), sb.st_size, fd); 312 PetscCheck(rd == (size_t)sb.st_size, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Unable to read entire file: %s %d %d", filename, (int)rd, (int)sb.st_size); 313 fclose(fd); 314 body[blen + rd] = 0; 315 PetscCall(PetscStrlcat(body, 316 "\r\n\r\n" 317 "--foo_bar_baz\r\n", 318 len)); 319 PetscCall(PetscSSLInitializeContext(&ctx)); 320 PetscCall(PetscHTTPSConnect("www.boxapis.com", 443, ctx, &sock, &ssl)); 321 PetscCall(PetscHTTPSRequest("POST", "www.boxapis.com/upload/drive/v2/files/", head, "multipart/related; boundary=\"foo_bar_baz\"", body, ssl, buff, sizeof(buff))); 322 PetscCall(PetscFree(body)); 323 PetscCall(PetscSSLDestroyContext(ctx)); 324 close(sock); 325 PetscCall(PetscStrstr(buff, "\"title\"", &title)); 326 PetscCheck(title, PETSC_COMM_SELF, PETSC_ERR_LIB, "Upload of file %s failed", filename); 327 } 328 PetscFunctionReturn(PETSC_SUCCESS); 329 } 330