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 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 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(PetscStrcpy(keyfile, "sslclient.pem")); 44 PetscCall(PetscTestFile(keyfile, 'r', &exists)); 45 if (!exists) { 46 PetscCall(PetscGetHomeDirectory(keyfile, PETSC_MAX_PATH_LEN)); 47 PetscCall(PetscStrcat(keyfile, "/")); 48 PetscCall(PetscStrcat(keyfile, "sslclient.pem")); 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(0); 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()`, guard this like a password 82 it gives access to your Box Drive 83 84 Notes: 85 This call requires stdout and stdin access from process 0 on the MPI communicator 86 87 You can run src/sys/webclient/tutorials/boxobtainrefreshtoken to get a refresh token and then in the future pass it to 88 PETSc programs with -box_refresh_token XXX 89 90 This requires PETSc be installed using --with-saws or --download-saws 91 92 Requires the user have created a self-signed ssl certificate with 93 94 $ saws/CA.pl -newcert (using the passphrase of password) 95 $ cat newkey.pem newcert.pem > sslclient.pem 96 97 and put the resulting file in either the current directory (with the application) or in the home directory. This seems kind of 98 silly but it was all I could figure out. 99 100 Level: intermediate 101 102 .seealso: `PetscBoxRefresh()`, `PetscBoxUpload()`, `PetscURLShorten()` 103 @*/ 104 PetscErrorCode PetscBoxAuthorize(MPI_Comm comm, char access_token[], char refresh_token[], size_t tokensize) { 105 SSL_CTX *ctx; 106 SSL *ssl; 107 int sock; 108 char buff[8 * 1024], body[1024]; 109 PetscMPIInt rank; 110 PetscBool flg, found; 111 112 PetscFunctionBegin; 113 PetscCallMPI(MPI_Comm_rank(comm, &rank)); 114 if (rank == 0) { 115 PetscCheck(isatty(fileno(PETSC_STDOUT)), PETSC_COMM_SELF, PETSC_ERR_USER, "Requires users input/output"); 116 PetscCall(PetscPrintf(comm, "Cut and paste the following into your browser:\n\n" 117 "https://www.box.com/api/oauth2/authorize?" 118 "response_type=code&" 119 "client_id=" PETSC_BOX_CLIENT_ID "&state=PETScState" 120 "\n\n")); 121 PetscCall(PetscBoxStartWebServer_Private()); 122 PetscCall(PetscStrbeginswith((const char *)result, "state=PETScState&code=", &flg)); 123 PetscCheck(flg, PETSC_COMM_SELF, PETSC_ERR_LIB, "Did not get expected string from Box got %s", result); 124 PetscCall(PetscStrncpy(buff, (const char *)result + 22, sizeof(buff))); 125 126 PetscCall(PetscSSLInitializeContext(&ctx)); 127 PetscCall(PetscHTTPSConnect("www.box.com", 443, ctx, &sock, &ssl)); 128 PetscCall(PetscStrcpy(body, "code=")); 129 PetscCall(PetscStrcat(body, buff)); 130 PetscCall(PetscStrcat(body, "&client_id=")); 131 PetscCall(PetscStrcat(body, PETSC_BOX_CLIENT_ID)); 132 PetscCall(PetscStrcat(body, "&client_secret=")); 133 PetscCall(PetscStrcat(body, PETSC_BOX_CLIENT_ST)); 134 PetscCall(PetscStrcat(body, "&grant_type=authorization_code")); 135 136 PetscCall(PetscHTTPSRequest("POST", "www.box.com/api/oauth2/token", NULL, "application/x-www-form-urlencoded", body, ssl, buff, sizeof(buff))); 137 PetscCall(PetscSSLDestroyContext(ctx)); 138 close(sock); 139 140 PetscCall(PetscPullJSONValue(buff, "access_token", access_token, tokensize, &found)); 141 PetscCheck(found, PETSC_COMM_SELF, PETSC_ERR_LIB, "Box did not return access token"); 142 PetscCall(PetscPullJSONValue(buff, "refresh_token", refresh_token, tokensize, &found)); 143 PetscCheck(found, PETSC_COMM_SELF, PETSC_ERR_LIB, "Box did not return refresh token"); 144 145 PetscCall(PetscPrintf(comm, "Here is your Box refresh token, save it in a save place, in the future you can run PETSc\n")); 146 PetscCall(PetscPrintf(comm, "programs with the option -box_refresh_token %s\n", refresh_token)); 147 PetscCall(PetscPrintf(comm, "to access Box Drive automatically\n")); 148 } 149 PetscFunctionReturn(0); 150 } 151 #endif 152 153 /*@C 154 PetscBoxRefresh - Get a new authorization token for accessing Box drive from PETSc from a refresh token 155 156 Not collective, only the first process in the `MPI_Comm` does anything 157 158 Input Parameters: 159 + comm - MPI communicator 160 . refresh token - obtained with `PetscBoxAuthorize()`, if NULL PETSc will first look for one in the options data 161 if not found it will call `PetscBoxAuthorize()` 162 - tokensize - size of the output string access_token 163 164 Output Parameters: 165 + access_token - token that can be passed to `PetscBoxUpload()` 166 - 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 167 168 Level: intermediate 169 170 .seealso: `PetscURLShorten()`, `PetscBoxAuthorize()`, `PetscBoxUpload()` 171 @*/ 172 PetscErrorCode PetscBoxRefresh(MPI_Comm comm, const char refresh_token[], char access_token[], char new_refresh_token[], size_t tokensize) { 173 SSL_CTX *ctx; 174 SSL *ssl; 175 int sock; 176 char buff[8 * 1024], body[1024]; 177 PetscMPIInt rank; 178 char *refreshtoken = (char *)refresh_token; 179 PetscBool found; 180 181 PetscFunctionBegin; 182 PetscCallMPI(MPI_Comm_rank(comm, &rank)); 183 if (rank == 0) { 184 if (!refresh_token) { 185 PetscBool set; 186 PetscCall(PetscMalloc1(512, &refreshtoken)); 187 PetscCall(PetscOptionsGetString(NULL, NULL, "-box_refresh_token", refreshtoken, sizeof(refreshtoken), &set)); 188 #if defined(PETSC_HAVE_SAWS) 189 if (!set) { 190 PetscCall(PetscBoxAuthorize(comm, access_token, new_refresh_token, 512 * sizeof(char))); 191 PetscCall(PetscFree(refreshtoken)); 192 PetscFunctionReturn(0); 193 } 194 #else 195 PetscCheck(set, PETSC_COMM_SELF, PETSC_ERR_LIB, "Must provide refresh token with -box_refresh_token XXX"); 196 #endif 197 } 198 PetscCall(PetscSSLInitializeContext(&ctx)); 199 PetscCall(PetscHTTPSConnect("www.box.com", 443, ctx, &sock, &ssl)); 200 PetscCall(PetscStrcpy(body, "client_id=")); 201 PetscCall(PetscStrcat(body, PETSC_BOX_CLIENT_ID)); 202 PetscCall(PetscStrcat(body, "&client_secret=")); 203 PetscCall(PetscStrcat(body, PETSC_BOX_CLIENT_ST)); 204 PetscCall(PetscStrcat(body, "&refresh_token=")); 205 PetscCall(PetscStrcat(body, refreshtoken)); 206 if (!refresh_token) PetscCall(PetscFree(refreshtoken)); 207 PetscCall(PetscStrcat(body, "&grant_type=refresh_token")); 208 209 PetscCall(PetscHTTPSRequest("POST", "www.box.com/api/oauth2/token", NULL, "application/x-www-form-urlencoded", body, ssl, buff, sizeof(buff))); 210 PetscCall(PetscSSLDestroyContext(ctx)); 211 close(sock); 212 213 PetscCall(PetscPullJSONValue(buff, "access_token", access_token, tokensize, &found)); 214 PetscCheck(found, PETSC_COMM_SELF, PETSC_ERR_LIB, "Box did not return access token"); 215 PetscCall(PetscPullJSONValue(buff, "refresh_token", new_refresh_token, tokensize, &found)); 216 PetscCheck(found, PETSC_COMM_SELF, PETSC_ERR_LIB, "Box did not return refresh token"); 217 218 PetscCall(PetscPrintf(comm, "Here is your new Box refresh token, save it in a save place, in the future you can run PETSc\n")); 219 PetscCall(PetscPrintf(comm, "programs with the option -box_refresh_token %s\n", new_refresh_token)); 220 PetscCall(PetscPrintf(comm, "to access Box Drive automatically\n")); 221 } 222 PetscFunctionReturn(0); 223 } 224 225 #include <sys/stat.h> 226 227 /*@C 228 PetscBoxUpload - Loads a file to the Box Drive 229 230 This routine has not yet been written; it is just copied from Google Drive 231 232 Not collective, only the first process in the `MPI_Comm` uploads the file 233 234 Input Parameters: 235 + comm - MPI communicator 236 . access_token - obtained with `PetscBoxRefresh()`, pass NULL to have PETSc generate one 237 - filename - file to upload; if you upload multiple times it will have different names each time on Box Drive 238 239 Options Database Key: 240 . -box_refresh_token XXX - the token value 241 242 Usage Patterns: 243 .vb 244 With PETSc option -box_refresh_token XXX given 245 PetscBoxUpload(comm,NULL,filename); will upload file with no user interaction 246 247 Without PETSc option -box_refresh_token XXX given 248 PetscBoxUpload(comm,NULL,filename); for first use will prompt user to authorize access to Box Drive with their processor 249 250 With PETSc option -box_refresh_token XXX given 251 PetscBoxRefresh(comm,NULL,access_token,sizeof(access_token)); 252 PetscBoxUpload(comm,access_token,filename); 253 254 With refresh token entered in some way by the user 255 PetscBoxRefresh(comm,refresh_token,access_token,sizeof(access_token)); 256 PetscBoxUpload(comm,access_token,filename); 257 258 PetscBoxAuthorize(comm,access_token,refresh_token,sizeof(access_token)); 259 PetscBoxUpload(comm,access_token,filename); 260 .ve 261 262 Level: intermediate 263 264 .seealso: `PetscURLShorten()`, `PetscBoxAuthorize()`, `PetscBoxRefresh()` 265 @*/ 266 PetscErrorCode PetscBoxUpload(MPI_Comm comm, const char access_token[], const char filename[]) { 267 SSL_CTX *ctx; 268 SSL *ssl; 269 int sock; 270 char head[1024], buff[8 * 1024], *body, *title; 271 PetscMPIInt rank; 272 struct stat sb; 273 size_t len, blen, rd; 274 FILE *fd; 275 int err; 276 277 PetscFunctionBegin; 278 PetscCallMPI(MPI_Comm_rank(comm, &rank)); 279 if (rank == 0) { 280 PetscCall(PetscStrcpy(head, "Authorization: Bearer ")); 281 PetscCall(PetscStrcat(head, access_token)); 282 PetscCall(PetscStrcat(head, "\r\n")); 283 PetscCall(PetscStrcat(head, "uploadType: multipart\r\n")); 284 285 err = stat(filename, &sb); 286 PetscCheck(!err, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Unable to stat file: %s", filename); 287 len = 1024 + sb.st_size; 288 PetscCall(PetscMalloc1(len, &body)); 289 PetscCall(PetscStrcpy(body, "--foo_bar_baz\r\n" 290 "Content-Type: application/json\r\n\r\n" 291 "{")); 292 PetscCall(PetscPushJSONValue(body, "title", filename, len)); 293 PetscCall(PetscStrcat(body, ",")); 294 PetscCall(PetscPushJSONValue(body, "mimeType", "text.html", len)); 295 PetscCall(PetscStrcat(body, ",")); 296 PetscCall(PetscPushJSONValue(body, "description", "a file", len)); 297 PetscCall(PetscStrcat(body, "}\r\n\r\n" 298 "--foo_bar_baz\r\n" 299 "Content-Type: text/html\r\n\r\n")); 300 PetscCall(PetscStrlen(body, &blen)); 301 fd = fopen(filename, "r"); 302 PetscCheck(fd, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Unable to open file: %s", filename); 303 rd = fread(body + blen, sizeof(unsigned char), sb.st_size, fd); 304 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); 305 fclose(fd); 306 body[blen + rd] = 0; 307 PetscCall(PetscStrcat(body, "\r\n\r\n" 308 "--foo_bar_baz\r\n")); 309 PetscCall(PetscSSLInitializeContext(&ctx)); 310 PetscCall(PetscHTTPSConnect("www.boxapis.com", 443, ctx, &sock, &ssl)); 311 PetscCall(PetscHTTPSRequest("POST", "www.boxapis.com/upload/drive/v2/files/", head, "multipart/related; boundary=\"foo_bar_baz\"", body, ssl, buff, sizeof(buff))); 312 PetscCall(PetscFree(body)); 313 PetscCall(PetscSSLDestroyContext(ctx)); 314 close(sock); 315 PetscCall(PetscStrstr(buff, "\"title\"", &title)); 316 PetscCheck(title, PETSC_COMM_SELF, PETSC_ERR_LIB, "Upload of file %s failed", filename); 317 } 318 PetscFunctionReturn(0); 319 } 320