1 2 #include <petscsys.h> 3 #include <errno.h> 4 #include <sys/types.h> 5 #include <sys/socket.h> 6 #include <netinet/in.h> 7 #include <netinet/tcp.h> 8 #include <netdb.h> 9 #include <fcntl.h> 10 #include <signal.h> 11 #include <unistd.h> 12 #include <string.h> 13 14 #include <openssl/ssl.h> 15 #include <openssl/err.h> 16 17 static BIO *bio_err = NULL; 18 19 #define PASSWORD "password" 20 21 #if defined(PETSC_USE_CERTIFICATE) 22 static int password_cb(char *buf,int num, int rwflag,void *userdata) 23 { 24 if (num < strlen(PASSWORD)+1) return(0); 25 strcpy(buf,PASSWORD); 26 return(strlen(PASSWORD)); 27 } 28 #endif 29 30 static void sigpipe_handle(int x) 31 { 32 } 33 34 #undef __FUNCT__ 35 #define __FUNCT__ "PetscSSLInitializeContext" 36 /* 37 PetscSSLInitializeContext - Set up an SSL context suitable for initiating HTTPS requests. 38 39 If built with PETSC_USE_CERTIFICATE requires the user have created a self-signed certificate with 40 41 $ ./CA.pl -newcert (using the passphrase of password) 42 $ cat newkey.pem newcert.pem > sslclient.pem 43 44 and put the resulting file in either the current directory (with the application) or in the home directory. This seems kind of 45 silly but it was all I could figure out. 46 47 */ 48 PetscErrorCode PetscSSLInitializeContext(SSL_CTX **octx) 49 { 50 SSL_METHOD *meth; 51 SSL_CTX *ctx; 52 #if defined(PETSC_USE_CERTIFICATE) 53 char keyfile[PETSC_MAX_PATH_LEN]; 54 PetscBool exists; 55 PetscErrorCode ierr; 56 #endif 57 58 PetscFunctionBegin; 59 if (!bio_err){ 60 SSL_library_init(); 61 SSL_load_error_strings(); 62 bio_err = BIO_new_fp(stderr,BIO_NOCLOSE); 63 } 64 65 /* Set up a SIGPIPE handler */ 66 signal(SIGPIPE,sigpipe_handle); 67 68 meth = SSLv23_method(); 69 ctx = SSL_CTX_new(meth); 70 71 #if defined(PETSC_USE_CERTIFICATE) 72 /* Locate keyfile */ 73 ierr = PetscStrcpy(keyfile,"sslclient.pem");CHKERRQ(ierr); 74 ierr = PetscTestFile(keyfile,'r',&exists);CHKERRQ(ierr); 75 if (!exists) { 76 ierr = PetscGetHomeDirectory(keyfile,PETSC_MAX_PATH_LEN);CHKERRQ(ierr); 77 ierr = PetscStrcat(keyfile,"/");CHKERRQ(ierr); 78 ierr = PetscStrcat(keyfile,"sslclient.pem");CHKERRQ(ierr); 79 ierr = PetscTestFile(keyfile,'r',&exists);CHKERRQ(ierr); 80 if (!exists) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FILE_OPEN,"Unable to locate sslclient.pem file in current directory or home directory"); 81 } 82 83 /* Load our keys and certificates*/ 84 if (!(SSL_CTX_use_certificate_chain_file(ctx,keyfile))) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FILE_OPEN,"Cannot read certificate file"); 85 86 SSL_CTX_set_default_passwd_cb(ctx,password_cb); 87 if (!(SSL_CTX_use_PrivateKey_file(ctx,keyfile,SSL_FILETYPE_PEM))) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FILE_OPEN,"Cannot read key file"); 88 #endif 89 90 *octx = ctx; 91 PetscFunctionReturn(0); 92 } 93 94 #undef __FUNCT__ 95 #define __FUNCT__ "PetscSSLDestroyContext" 96 PetscErrorCode PetscSSLDestroyContext(SSL_CTX *ctx) 97 { 98 PetscFunctionBegin; 99 SSL_CTX_free(ctx); 100 PetscFunctionReturn(0); 101 } 102 103 #undef __FUNCT__ 104 #define __FUNCT__ "PetscHTTPSRequest" 105 /* 106 PetscHTTPSRequest - Send a request to an HTTPS server 107 108 Input Parameters: 109 + type - either "POST" or "GET" 110 . url - complete URL of request including https:// 111 . header - additional header information, may be NULL 112 . ctype - data type of body, for example application/json 113 . body - data to send to server 114 . ssl - obtained with PetscHTTPSConnect() 115 - buffsize - size of buffer 116 117 Output Parameter: 118 . buff - everything returned from server 119 */ 120 static PetscErrorCode PetscHTTPSRequest(const char type[],const char url[],const char header[],const char ctype[],const char body[],SSL *ssl,char buff[],size_t buffsize) 121 { 122 char *request=0; 123 char contentlength[40],contenttype[80]; 124 int r; 125 size_t request_len,len,headlen,bodylen,contentlen,urllen,typelen,contenttypelen = 0; 126 PetscErrorCode ierr; 127 PetscBool flg; 128 129 PetscFunctionBegin; 130 ierr = PetscStrbeginswith(url,"https://",&flg);CHKERRQ(ierr); 131 if (!flg) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_WRONG,"URL must begin with https://"); 132 if (header) { 133 ierr = PetscStrendswith(header,"\r\n",&flg);CHKERRQ(ierr); 134 if (!flg) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_WRONG,"header must end with \\r\\n"); 135 } 136 137 ierr = PetscStrlen(type,&typelen);CHKERRQ(ierr); 138 ierr = PetscStrlen(url,&urllen);CHKERRQ(ierr); 139 if (ctype) { 140 ierr = PetscSNPrintf(contenttype,80,"Content-Type: %s\r\n",ctype);CHKERRQ(ierr); 141 ierr = PetscStrlen(contenttype,&contenttypelen);CHKERRQ(ierr); 142 } 143 ierr = PetscStrlen(header,&headlen);CHKERRQ(ierr); 144 ierr = PetscStrlen(body,&bodylen);CHKERRQ(ierr); 145 ierr = PetscSNPrintf(contentlength,40,"Content-Length: %d\r\n\r\n",(int)bodylen);CHKERRQ(ierr); 146 ierr = PetscStrlen(contentlength,&contentlen);CHKERRQ(ierr); 147 148 /* Now construct our HTTP request */ 149 request_len = typelen + 1 + urllen + 35 + headlen + contenttypelen + contentlen + bodylen + 1; 150 ierr = PetscMalloc(request_len*sizeof(char),&request);CHKERRQ(ierr); 151 ierr = PetscStrcpy(request,type);CHKERRQ(ierr); 152 ierr = PetscStrcat(request," ");CHKERRQ(ierr); 153 ierr = PetscStrcat(request,url);CHKERRQ(ierr); 154 ierr = PetscStrcat(request," HTTP/1.1\r\nUser-Agent:PETScClient\r\n");CHKERRQ(ierr); 155 ierr = PetscStrcat(request,header);CHKERRQ(ierr); 156 if (ctype) { 157 ierr = PetscStrcat(request,contenttype);CHKERRQ(ierr); 158 } 159 ierr = PetscStrcat(request,contentlength);CHKERRQ(ierr); 160 ierr = PetscStrcat(request,body);CHKERRQ(ierr); 161 ierr = PetscStrlen(request,&request_len);CHKERRQ(ierr); 162 ierr = PetscInfo1(NULL,"HTTPS request follows: \n%s\n",request);CHKERRQ(ierr); 163 164 r = SSL_write(ssl,request,request_len); 165 switch (SSL_get_error(ssl,r)){ 166 case SSL_ERROR_NONE: 167 if (request_len != r) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Incomplete write to SSL socket"); 168 break; 169 default: 170 SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"SSL socket write problem"); 171 } 172 173 /* Now read the server's response, assuming that it's terminated by a close */ 174 r = SSL_read(ssl,buff,(int)buffsize); 175 len = r; 176 switch (SSL_get_error(ssl,r)){ 177 case SSL_ERROR_NONE: 178 break; 179 case SSL_ERROR_ZERO_RETURN: 180 SSL_shutdown(ssl); /* ignore shutdown error message */ 181 break; 182 case SSL_ERROR_SYSCALL: 183 break; 184 default: 185 SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"SSL read problem"); 186 } 187 buff[len] = 0; /* null terminate string */ 188 ierr = PetscInfo1(NULL,"HTTPS result follows: \n%s\n",buff);CHKERRQ(ierr); 189 190 SSL_free(ssl); 191 ierr = PetscFree(request);CHKERRQ(ierr); 192 PetscFunctionReturn(0); 193 } 194 195 #undef __FUNCT__ 196 #define __FUNCT__ "PetscHTTPSConnect" 197 PetscErrorCode PetscHTTPSConnect(const char host[],int port,SSL_CTX *ctx,int *sock,SSL **ssl) 198 { 199 BIO *sbio; 200 PetscErrorCode ierr; 201 202 PetscFunctionBegin; 203 /* Connect the TCP socket*/ 204 ierr = PetscOpenSocket(host,port,sock);CHKERRQ(ierr); 205 206 /* Connect the SSL socket */ 207 *ssl = SSL_new(ctx); 208 sbio = BIO_new_socket(*sock,BIO_NOCLOSE); 209 SSL_set_bio(*ssl,sbio,sbio); 210 if (SSL_connect(*ssl) <= 0) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"SSL connect error"); 211 PetscFunctionReturn(0); 212 } 213 214 /* 215 This file is not included in the respository since it contains authorization secrets 216 */ 217 #include <../src/sys/webclient/authorization.h> 218 219 #undef __FUNCT__ 220 #define __FUNCT__ "PetscGoogleDriveRefresh" 221 /*C 222 PetscGoogleDriveRefresh - Get a new authorization token for accessing Google drive from PETSc from a refresh token 223 224 225 */ 226 PetscErrorCode PetscGoogleDriveRefresh(MPI_Comm comm,const char refresh_token[],char access_token[],size_t tokensize) 227 { 228 SSL_CTX *ctx; 229 SSL *ssl; 230 int sock; 231 PetscErrorCode ierr; 232 char buff[8*1024],body[1024],*access,*ctmp; 233 PetscMPIInt rank; 234 235 PetscFunctionBegin; 236 ierr = MPI_Comm_rank(comm,&rank);CHKERRQ(ierr); 237 if (!rank) { 238 ierr = PetscSSLInitializeContext(&ctx);CHKERRQ(ierr); 239 ierr = PetscHTTPSConnect("accounts.google.com",443,ctx,&sock,&ssl);CHKERRQ(ierr); 240 ierr = PetscStrcpy(body,"&client_id=");CHKERRQ(ierr); 241 ierr = PetscStrcat(body,PETSC_CLIENT_ID);CHKERRQ(ierr); 242 ierr = PetscStrcat(body,"&client_secret=");CHKERRQ(ierr); 243 ierr = PetscStrcat(body,PETSC_CLIENT_SECRET);CHKERRQ(ierr); 244 ierr = PetscStrcat(body,"&refresh_token=");CHKERRQ(ierr); 245 ierr = PetscStrcat(body,refresh_token);CHKERRQ(ierr); 246 ierr = PetscStrcat(body,"&grant_type=refresh_token");CHKERRQ(ierr); 247 248 ierr = PetscHTTPSRequest("POST","https://accounts.google.com/o/oauth2/token",NULL,"application/x-www-form-urlencoded",body,ssl,buff,sizeof(buff));CHKERRQ(ierr); 249 ierr = PetscSSLDestroyContext(ctx);CHKERRQ(ierr); 250 close(sock); 251 252 ierr = PetscStrstr(buff,"\"access_token\" : \"",&access);CHKERRQ(ierr); 253 if (!access) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Did not receive access token from Google"); 254 access += 18; 255 ierr = PetscStrchr(access,'\"',&ctmp);CHKERRQ(ierr); 256 if (!ctmp) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Access token from Google is misformed"); 257 *ctmp = 0; 258 ierr = PetscStrncpy(access_token,access,tokensize);CHKERRQ(ierr); 259 *ctmp = '\"'; 260 } 261 PetscFunctionReturn(0); 262 } 263 264 #include <sys/stat.h> 265 266 #undef __FUNCT__ 267 #define __FUNCT__ "PetscGoogleDriveUpload" 268 /*@C 269 PetscGoogleDriveUpload - Loads a file to the google drive 270 271 Not collective, only the first process in the MPI_Comm uploads the file 272 273 Input Parameters: 274 + comm - MPI communicator 275 . access_token - obtained with PetscGoogleDriveRefresh() 276 - filename - file to upload; if you upload multiple times it will have different names each time on Google Drive 277 278 @*/ 279 PetscErrorCode PetscGoogleDriveUpload(MPI_Comm comm,const char access_token[],const char filename[]) 280 { 281 SSL_CTX *ctx; 282 SSL *ssl; 283 int sock; 284 PetscErrorCode ierr; 285 char head[1024],buff[8*1024],*body,*title; 286 PetscMPIInt rank; 287 struct stat sb; 288 size_t len,blen,rd; 289 FILE *fd; 290 291 PetscFunctionBegin; 292 ierr = MPI_Comm_rank(comm,&rank);CHKERRQ(ierr); 293 if (!rank) { 294 ierr = PetscStrcpy(head,"Authorization: Bearer ");CHKERRQ(ierr); 295 ierr = PetscStrcat(head,access_token);CHKERRQ(ierr); 296 ierr = PetscStrcat(head,"\r\n");CHKERRQ(ierr); 297 ierr = PetscStrcat(head,"uploadType: multipart\r\n");CHKERRQ(ierr); 298 299 ierr = stat(filename,&sb); 300 if (ierr) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_FILE_OPEN,"Unable to stat file: %s",filename); 301 len = 1024 + sb.st_size; 302 ierr = PetscMalloc(len*sizeof(char),&body);CHKERRQ(ierr); 303 ierr = PetscStrcpy(body,"--foo_bar_baz\r\n" 304 "Content-Type: application/json\r\n\r\n" 305 "{" 306 "\"title\": \""); 307 ierr = PetscStrcat(body,filename); 308 ierr = PetscStrcat(body,"\"," 309 "\"mimeType\": \"text.html\"," 310 "\"description\": \" a file\"" 311 "}\r\n\r\n" 312 "--foo_bar_baz\r\n" 313 "Content-Type: text/html\r\n\r\n"); 314 ierr = PetscStrlen(body,&blen);CHKERRQ(ierr); 315 fd = fopen (filename, "r"); 316 if (!fd) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_FILE_OPEN,"Unable to open file: %s",filename); 317 rd = fread (body+blen, sizeof (unsigned char), sb.st_size, fd); 318 if (rd != sb.st_size) SETERRQ3(PETSC_COMM_SELF,PETSC_ERR_FILE_OPEN,"Unable to read entire file: %s %d %d",filename,(int)rd,sb.st_size); 319 fclose(fd); 320 body[blen + rd] = 0; 321 ierr = PetscStrcat(body,"\r\n\r\n" 322 "--foo_bar_baz\r\n"); 323 ierr = PetscSSLInitializeContext(&ctx);CHKERRQ(ierr); 324 ierr = PetscHTTPSConnect("www.googleapis.com",443,ctx,&sock,&ssl);CHKERRQ(ierr); 325 ierr = PetscHTTPSRequest("POST","https://www.googleapis.com/upload/drive/v2/files/",head,"multipart/related; boundary=\"foo_bar_baz\"",body,ssl,buff,sizeof(buff));CHKERRQ(ierr); 326 ierr = PetscSSLDestroyContext(ctx);CHKERRQ(ierr); 327 close(sock); 328 ierr = PetscStrstr(buff,"\"title\"",&title);CHKERRQ(ierr); 329 if (!title) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_LIB,"Upload of file %s failed",filename); 330 } 331 PetscFunctionReturn(0); 332 } 333 334 335 #undef __FUNCT__ 336 #define __FUNCT__ "PetscGoogleDriveAuthorize" 337 /*C 338 PetscGoogleDriveAuthorize - Get authorization and refresh token for accessing Google drive from PETSc 339 340 341 */ 342 PetscErrorCode PetscGoogleDriveAuthorize(MPI_Comm comm,char access_token[],char refresh_token[],size_t tokensize) 343 { 344 SSL_CTX *ctx; 345 SSL *ssl; 346 int sock; 347 PetscErrorCode ierr; 348 char buff[8*1024],*ptr,body[1024],*access,*refresh,*ctmp; 349 PetscMPIInt rank; 350 size_t len; 351 352 PetscFunctionBegin; 353 ierr = PetscPrintf(comm,"Cut and paste the following into your browser:\n" 354 "https://accounts.google.com/o/oauth2/auth?" 355 "scope=https%%3A%%2F%%2Fwww.googleapis.com%%2Fauth%%2Fdrive.file&" 356 "redirect_uri=urn:ietf:wg:oauth:2.0:oob&" 357 "response_type=code&" 358 "client_id=" 359 PETSC_CLIENT_ID 360 "\n\n");CHKERRQ(ierr); 361 ierr = PetscPrintf(comm,"Paste the result here:");CHKERRQ(ierr); 362 ierr = MPI_Comm_rank(comm,&rank);CHKERRQ(ierr); 363 if (!rank) { 364 ptr = fgets(buff, 1024, stdin); 365 if (!ptr) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_FILE_READ, "Error reading from stdin: %d", errno); 366 ierr = PetscStrlen(buff,&len);CHKERRQ(ierr); 367 buff[len-1] = 0; /* remove carriage return at end of line */ 368 369 ierr = PetscSSLInitializeContext(&ctx);CHKERRQ(ierr); 370 ierr = PetscHTTPSConnect("accounts.google.com",443,ctx,&sock,&ssl);CHKERRQ(ierr); 371 ierr = PetscStrcpy(body,"code=");CHKERRQ(ierr); 372 ierr = PetscStrcat(body,buff);CHKERRQ(ierr); 373 ierr = PetscStrcat(body,"&client_id=");CHKERRQ(ierr); 374 ierr = PetscStrcat(body,PETSC_CLIENT_ID);CHKERRQ(ierr); 375 ierr = PetscStrcat(body,"&client_secret=");CHKERRQ(ierr); 376 ierr = PetscStrcat(body,PETSC_CLIENT_SECRET);CHKERRQ(ierr); 377 ierr = PetscStrcat(body,"&redirect_uri=urn:ietf:wg:oauth:2.0:oob&");CHKERRQ(ierr); 378 ierr = PetscStrcat(body,"grant_type=authorization_code");CHKERRQ(ierr); 379 380 ierr = PetscHTTPSRequest("POST","https://accounts.google.com/o/oauth2/token",NULL,"application/x-www-form-urlencoded",body,ssl,buff,sizeof(buff));CHKERRQ(ierr); 381 ierr = PetscSSLDestroyContext(ctx);CHKERRQ(ierr); 382 close(sock); 383 384 ierr = PetscStrstr(buff,"\"access_token\" : \"",&access);CHKERRQ(ierr); 385 if (!access) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Did not receive access token from Google"); 386 access += 18; 387 ierr = PetscStrchr(access,'\"',&ctmp);CHKERRQ(ierr); 388 if (!ctmp) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Access token from Google is misformed"); 389 *ctmp = 0; 390 ierr = PetscStrncpy(access_token,access,tokensize);CHKERRQ(ierr); 391 *ctmp = '\"'; 392 393 ierr = PetscStrstr(buff,"\"refresh_token\" : \"",&refresh);CHKERRQ(ierr); 394 if (!refresh) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Did not receive refresh token from Google"); 395 refresh += 19; 396 ierr = PetscStrchr(refresh,'\"',&ctmp);CHKERRQ(ierr); 397 if (!ctmp) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Refresh token from Google is misformed"); 398 *ctmp = 0; 399 ierr = PetscStrncpy(refresh_token,refresh,tokensize);CHKERRQ(ierr); 400 } 401 PetscFunctionReturn(0); 402 } 403 404 405 #undef __FUNCT__ 406 #define __FUNCT__ "PetscURLShorten" 407 /*@C 408 PetscURLShorten - Uses Google's service to get a short url for a long url 409 410 Input Parameters: 411 + url - long URL you want shortened 412 - lenshorturl - length of buffer to contain short URL 413 414 Output Parameter: 415 . shorturl - the shortened URL 416 417 @*/ 418 PetscErrorCode PetscURLShorten(const char url[],char shorturl[],size_t lenshorturl) 419 { 420 SSL_CTX *ctx; 421 SSL *ssl; 422 int sock; 423 PetscErrorCode ierr; 424 char buff[1024],body[512],*sub1,*sub2; 425 426 PetscFunctionBegin; 427 ierr = PetscSSLInitializeContext(&ctx);CHKERRQ(ierr); 428 ierr = PetscHTTPSConnect("www.googleapis.com",443,ctx,&sock,&ssl);CHKERRQ(ierr); 429 ierr = PetscSNPrintf(body,512,"{\"longUrl\": \"%s\"}",url);CHKERRQ(ierr); 430 ierr = PetscHTTPSRequest("POST","https://www.googleapis.com/urlshortener/v1/url",NULL,"application/json",body,ssl,buff,sizeof(buff));CHKERRQ(ierr); 431 ierr = PetscSSLDestroyContext(ctx);CHKERRQ(ierr); 432 close(sock); 433 ierr = PetscStrstr(buff,"\"id\": \"",&sub1);CHKERRQ(ierr); 434 if (sub1) { 435 sub1 += 7; 436 ierr = PetscStrstr(sub1,"\"",&sub2);CHKERRQ(ierr); 437 if (!sub2) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Google did not shorten URL"); 438 sub2[0] = 0; 439 ierr = PetscStrncpy(shorturl,sub1,lenshorturl);CHKERRQ(ierr); 440 } else SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Google did not shorten URL"); 441 PetscFunctionReturn(0); 442 } 443 444