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