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 process 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 @*/ 105 PetscErrorCode PetscBoxAuthorize(MPI_Comm comm, char access_token[], char refresh_token[], size_t tokensize) { 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(PetscStrcpy(body, "code=")); 130 PetscCall(PetscStrcat(body, buff)); 131 PetscCall(PetscStrcat(body, "&client_id=")); 132 PetscCall(PetscStrcat(body, PETSC_BOX_CLIENT_ID)); 133 PetscCall(PetscStrcat(body, "&client_secret=")); 134 PetscCall(PetscStrcat(body, PETSC_BOX_CLIENT_ST)); 135 PetscCall(PetscStrcat(body, "&grant_type=authorization_code")); 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(0); 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: `PetscURLShorten()`, `PetscBoxAuthorize()`, `PetscBoxUpload()` 172 173 @*/ 174 PetscErrorCode PetscBoxRefresh(MPI_Comm comm, const char refresh_token[], char access_token[], char new_refresh_token[], size_t tokensize) { 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(0); 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(PetscStrcpy(body, "client_id=")); 203 PetscCall(PetscStrcat(body, PETSC_BOX_CLIENT_ID)); 204 PetscCall(PetscStrcat(body, "&client_secret=")); 205 PetscCall(PetscStrcat(body, PETSC_BOX_CLIENT_ST)); 206 PetscCall(PetscStrcat(body, "&refresh_token=")); 207 PetscCall(PetscStrcat(body, refreshtoken)); 208 if (!refresh_token) PetscCall(PetscFree(refreshtoken)); 209 PetscCall(PetscStrcat(body, "&grant_type=refresh_token")); 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(0); 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: 242 . -box_refresh_token XXX - the token value 243 244 Usage Patterns: 245 With PETSc option -box_refresh_token XXX given 246 PetscBoxUpload(comm,NULL,filename); will upload file with no user interaction 247 248 Without PETSc option -box_refresh_token XXX given 249 PetscBoxUpload(comm,NULL,filename); for first use will prompt user to authorize access to Box Drive with their processor 250 251 With PETSc option -box_refresh_token XXX given 252 PetscBoxRefresh(comm,NULL,access_token,sizeof(access_token)); 253 PetscBoxUpload(comm,access_token,filename); 254 255 With refresh token entered in some way by the user 256 PetscBoxRefresh(comm,refresh_token,access_token,sizeof(access_token)); 257 PetscBoxUpload(comm,access_token,filename); 258 259 PetscBoxAuthorize(comm,access_token,refresh_token,sizeof(access_token)); 260 PetscBoxUpload(comm,access_token,filename); 261 262 Level: intermediate 263 264 .seealso: `PetscURLShorten()`, `PetscBoxAuthorize()`, `PetscBoxRefresh()` 265 266 @*/ 267 PetscErrorCode PetscBoxUpload(MPI_Comm comm, const char access_token[], const char filename[]) { 268 SSL_CTX *ctx; 269 SSL *ssl; 270 int sock; 271 char head[1024], buff[8 * 1024], *body, *title; 272 PetscMPIInt rank; 273 struct stat sb; 274 size_t len, blen, rd; 275 FILE *fd; 276 int err; 277 278 PetscFunctionBegin; 279 PetscCallMPI(MPI_Comm_rank(comm, &rank)); 280 if (rank == 0) { 281 PetscCall(PetscStrcpy(head, "Authorization: Bearer ")); 282 PetscCall(PetscStrcat(head, access_token)); 283 PetscCall(PetscStrcat(head, "\r\n")); 284 PetscCall(PetscStrcat(head, "uploadType: multipart\r\n")); 285 286 err = stat(filename, &sb); 287 PetscCheck(!err, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Unable to stat file: %s", filename); 288 len = 1024 + sb.st_size; 289 PetscCall(PetscMalloc1(len, &body)); 290 PetscCall(PetscStrcpy(body, "--foo_bar_baz\r\n" 291 "Content-Type: application/json\r\n\r\n" 292 "{")); 293 PetscCall(PetscPushJSONValue(body, "title", filename, len)); 294 PetscCall(PetscStrcat(body, ",")); 295 PetscCall(PetscPushJSONValue(body, "mimeType", "text.html", len)); 296 PetscCall(PetscStrcat(body, ",")); 297 PetscCall(PetscPushJSONValue(body, "description", "a file", len)); 298 PetscCall(PetscStrcat(body, "}\r\n\r\n" 299 "--foo_bar_baz\r\n" 300 "Content-Type: text/html\r\n\r\n")); 301 PetscCall(PetscStrlen(body, &blen)); 302 fd = fopen(filename, "r"); 303 PetscCheck(fd, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Unable to open file: %s", filename); 304 rd = fread(body + blen, sizeof(unsigned char), sb.st_size, fd); 305 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); 306 fclose(fd); 307 body[blen + rd] = 0; 308 PetscCall(PetscStrcat(body, "\r\n\r\n" 309 "--foo_bar_baz\r\n")); 310 PetscCall(PetscSSLInitializeContext(&ctx)); 311 PetscCall(PetscHTTPSConnect("www.boxapis.com", 443, ctx, &sock, &ssl)); 312 PetscCall(PetscHTTPSRequest("POST", "www.boxapis.com/upload/drive/v2/files/", head, "multipart/related; boundary=\"foo_bar_baz\"", body, ssl, buff, sizeof(buff))); 313 PetscCall(PetscFree(body)); 314 PetscCall(PetscSSLDestroyContext(ctx)); 315 close(sock); 316 PetscCall(PetscStrstr(buff, "\"title\"", &title)); 317 PetscCheck(title, PETSC_COMM_SELF, PETSC_ERR_LIB, "Upload of file %s failed", filename); 318 } 319 PetscFunctionReturn(0); 320 } 321