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