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