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