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