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 = PetscMalloc1(request_len,&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 Not collective, only the first process in the MPI_Comm does anything 225 226 Input Parameters: 227 + comm - MPI communicator 228 . refresh token - obtained with PetscGoogleDriveAuthorize(), if NULL PETSc will first look for one in the options data 229 if not found it will call PetscGoogleDriveAuthorize() 230 - tokensize - size of the output string access_token 231 232 Output Parameter: 233 . access_token - token that can be passed to PetscGoogleDriveUpload() 234 235 .seealso: PetscURLShorten(), PetscGoogleDriveAuthorize(), PetscGoogleDriveUpload() 236 237 @*/ 238 PetscErrorCode PetscGoogleDriveRefresh(MPI_Comm comm,const char refresh_token[],char access_token[],size_t tokensize) 239 { 240 SSL_CTX *ctx; 241 SSL *ssl; 242 int sock; 243 PetscErrorCode ierr; 244 char buff[8*1024],body[1024],*access,*ctmp; 245 PetscMPIInt rank; 246 char *refreshtoken = (char*)refresh_token; 247 248 PetscFunctionBegin; 249 ierr = MPI_Comm_rank(comm,&rank);CHKERRQ(ierr); 250 if (!rank) { 251 if (!refresh_token) { 252 PetscBool set; 253 ierr = PetscMalloc1(512,&refreshtoken);CHKERRQ(ierr); 254 ierr = PetscOptionsGetString(NULL,"-refresh_token",refreshtoken,512,&set);CHKERRQ(ierr); 255 if (!set) { 256 ierr = PetscGoogleDriveAuthorize(comm,access_token,refreshtoken,512*sizeof(char));CHKERRQ(ierr); 257 ierr = PetscFree(refreshtoken);CHKERRQ(ierr); 258 PetscFunctionReturn(0); 259 } 260 } 261 ierr = PetscSSLInitializeContext(&ctx);CHKERRQ(ierr); 262 ierr = PetscHTTPSConnect("accounts.google.com",443,ctx,&sock,&ssl);CHKERRQ(ierr); 263 ierr = PetscStrcpy(body,"&client_id=");CHKERRQ(ierr); 264 ierr = PetscStrcat(body,PETSC_CLIENT_ID);CHKERRQ(ierr); 265 ierr = PetscStrcat(body,"&client_secret=");CHKERRQ(ierr); 266 ierr = PetscStrcat(body,PETSC_CLIENT_SECRET);CHKERRQ(ierr); 267 ierr = PetscStrcat(body,"&refresh_token=");CHKERRQ(ierr); 268 ierr = PetscStrcat(body,refreshtoken);CHKERRQ(ierr); 269 if (!refresh_token) {ierr = PetscFree(refreshtoken);CHKERRQ(ierr);} 270 ierr = PetscStrcat(body,"&grant_type=refresh_token");CHKERRQ(ierr); 271 272 ierr = PetscHTTPSRequest("POST","https://accounts.google.com/o/oauth2/token",NULL,"application/x-www-form-urlencoded",body,ssl,buff,sizeof(buff));CHKERRQ(ierr); 273 ierr = PetscSSLDestroyContext(ctx);CHKERRQ(ierr); 274 close(sock); 275 276 ierr = PetscStrstr(buff,"\"access_token\" : \"",&access);CHKERRQ(ierr); 277 if (!access) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Did not receive access token from Google"); 278 access += 18; 279 ierr = PetscStrchr(access,'\"',&ctmp);CHKERRQ(ierr); 280 if (!ctmp) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Access token from Google is misformed"); 281 *ctmp = 0; 282 ierr = PetscStrncpy(access_token,access,tokensize);CHKERRQ(ierr); 283 *ctmp = '\"'; 284 } 285 PetscFunctionReturn(0); 286 } 287 288 #include <sys/stat.h> 289 290 #undef __FUNCT__ 291 #define __FUNCT__ "PetscGoogleDriveUpload" 292 /*@C 293 PetscGoogleDriveUpload - Loads a file to the Google Drive 294 295 Not collective, only the first process in the MPI_Comm uploads the file 296 297 Input Parameters: 298 + comm - MPI communicator 299 . access_token - obtained with PetscGoogleDriveRefresh(), pass NULL to have PETSc generate one 300 - filename - file to upload; if you upload multiple times it will have different names each time on Google Drive 301 302 Options Database: 303 . -refresh_token XXX 304 305 Usage Patterns: 306 With PETSc option -refresh_token XXX given 307 PetscGoogleDriveUpload(comm,NULL,filename); will upload file with no user interaction 308 309 Without PETSc option -refresh_token XXX given 310 PetscGoogleDriveUpload(comm,NULL,filename); for first use will prompt user to authorize access to Google Drive with their processor 311 312 With PETSc option -refresh_token XXX given 313 PetscGoogleDriveRefresh(comm,NULL,access_token,sizeof(access_token)); 314 PetscGoogleDriveUpload(comm,access_token,filename); 315 316 With refresh token entered in some way by the user 317 PetscGoogleDriveRefresh(comm,refresh_token,access_token,sizeof(access_token)); 318 PetscGoogleDriveUpload(comm,access_token,filename); 319 320 PetscGoogleDriveAuthorize(comm,access_token,refresh_token,sizeof(access_token)); 321 PetscGoogleDriveUpload(comm,access_token,filename); 322 323 .seealso: PetscURLShorten(), PetscGoogleDriveAuthorize(), PetscGoogleDriveRefresh() 324 325 @*/ 326 PetscErrorCode PetscGoogleDriveUpload(MPI_Comm comm,const char access_token[],const char filename[]) 327 { 328 SSL_CTX *ctx; 329 SSL *ssl; 330 int sock; 331 PetscErrorCode ierr; 332 char head[1024],buff[8*1024],*body,*title; 333 PetscMPIInt rank; 334 struct stat sb; 335 size_t len,blen,rd; 336 FILE *fd; 337 338 PetscFunctionBegin; 339 ierr = MPI_Comm_rank(comm,&rank);CHKERRQ(ierr); 340 if (!rank) { 341 ierr = PetscStrcpy(head,"Authorization: Bearer ");CHKERRQ(ierr); 342 ierr = PetscStrcat(head,access_token);CHKERRQ(ierr); 343 ierr = PetscStrcat(head,"\r\n");CHKERRQ(ierr); 344 ierr = PetscStrcat(head,"uploadType: multipart\r\n");CHKERRQ(ierr); 345 346 ierr = stat(filename,&sb); 347 if (ierr) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_FILE_OPEN,"Unable to stat file: %s",filename); 348 len = 1024 + sb.st_size; 349 ierr = PetscMalloc1(len,&body);CHKERRQ(ierr); 350 ierr = PetscStrcpy(body,"--foo_bar_baz\r\n" 351 "Content-Type: application/json\r\n\r\n" 352 "{" 353 "\"title\": \""); 354 ierr = PetscStrcat(body,filename); 355 ierr = PetscStrcat(body,"\"," 356 "\"mimeType\": \"text.html\"," 357 "\"description\": \" a file\"" 358 "}\r\n\r\n" 359 "--foo_bar_baz\r\n" 360 "Content-Type: text/html\r\n\r\n"); 361 ierr = PetscStrlen(body,&blen);CHKERRQ(ierr); 362 fd = fopen (filename, "r"); 363 if (!fd) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_FILE_OPEN,"Unable to open file: %s",filename); 364 rd = fread (body+blen, sizeof (unsigned char), sb.st_size, fd); 365 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); 366 fclose(fd); 367 body[blen + rd] = 0; 368 ierr = PetscStrcat(body,"\r\n\r\n" 369 "--foo_bar_baz\r\n"); 370 ierr = PetscSSLInitializeContext(&ctx);CHKERRQ(ierr); 371 ierr = PetscHTTPSConnect("www.googleapis.com",443,ctx,&sock,&ssl);CHKERRQ(ierr); 372 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); 373 ierr = PetscFree(body);CHKERRQ(ierr); 374 ierr = PetscSSLDestroyContext(ctx);CHKERRQ(ierr); 375 close(sock); 376 ierr = PetscStrstr(buff,"\"title\"",&title);CHKERRQ(ierr); 377 if (!title) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_LIB,"Upload of file %s failed",filename); 378 } 379 PetscFunctionReturn(0); 380 } 381 382 383 #undef __FUNCT__ 384 #define __FUNCT__ "PetscGoogleDriveAuthorize" 385 /*@C 386 PetscGoogleDriveAuthorize - Get authorization and refresh token for accessing Google drive from PETSc 387 388 Not collective, only the first process in MPI_Comm does anything 389 390 Input Parameters: 391 + comm - the MPI communicator 392 - tokensize - size of the token arrays 393 394 Output Parameters: 395 + access_token - can be used with PetscGoogleDriveUpload() for this one session 396 - refresh_token - can be used for ever to obtain new access_tokens with PetscGoogleDriveRefresh(), guard this like a password 397 it gives access to your Google Drive 398 399 Notes: This call requires stdout and stdin access from process 0 on the MPI communicator 400 401 You can run src/sys/webclient/examples/tutorials/obtainrefreshtoken to get a refresh token and then in the future pass it to 402 PETSc programs with -refresh_token XXX 403 404 .seealso: PetscGoogleDriveRefresh(), PetscGoogleDriveUpload(), PetscURLShorten() 405 406 @*/ 407 PetscErrorCode PetscGoogleDriveAuthorize(MPI_Comm comm,char access_token[],char refresh_token[],size_t tokensize) 408 { 409 SSL_CTX *ctx; 410 SSL *ssl; 411 int sock; 412 PetscErrorCode ierr; 413 char buff[8*1024],*ptr,body[1024],*access,*refresh,*ctmp; 414 PetscMPIInt rank; 415 size_t len; 416 417 PetscFunctionBegin; 418 ierr = PetscPrintf(comm,"Cut and paste the following into your browser:\n" 419 "https://accounts.google.com/o/oauth2/auth?" 420 "scope=https%%3A%%2F%%2Fwww.googleapis.com%%2Fauth%%2Fdrive.file&" 421 "redirect_uri=urn:ietf:wg:oauth:2.0:oob&" 422 "response_type=code&" 423 "client_id=" 424 PETSC_CLIENT_ID 425 "\n\n");CHKERRQ(ierr); 426 ierr = PetscPrintf(comm,"Paste the result here:");CHKERRQ(ierr); 427 ierr = MPI_Comm_rank(comm,&rank);CHKERRQ(ierr); 428 if (!rank) { 429 ptr = fgets(buff, 1024, stdin); 430 if (!ptr) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_FILE_READ, "Error reading from stdin: %d", errno); 431 ierr = PetscStrlen(buff,&len);CHKERRQ(ierr); 432 buff[len-1] = 0; /* remove carriage return at end of line */ 433 434 ierr = PetscSSLInitializeContext(&ctx);CHKERRQ(ierr); 435 ierr = PetscHTTPSConnect("accounts.google.com",443,ctx,&sock,&ssl);CHKERRQ(ierr); 436 ierr = PetscStrcpy(body,"code=");CHKERRQ(ierr); 437 ierr = PetscStrcat(body,buff);CHKERRQ(ierr); 438 ierr = PetscStrcat(body,"&client_id=");CHKERRQ(ierr); 439 ierr = PetscStrcat(body,PETSC_CLIENT_ID);CHKERRQ(ierr); 440 ierr = PetscStrcat(body,"&client_secret=");CHKERRQ(ierr); 441 ierr = PetscStrcat(body,PETSC_CLIENT_SECRET);CHKERRQ(ierr); 442 ierr = PetscStrcat(body,"&redirect_uri=urn:ietf:wg:oauth:2.0:oob&");CHKERRQ(ierr); 443 ierr = PetscStrcat(body,"grant_type=authorization_code");CHKERRQ(ierr); 444 445 ierr = PetscHTTPSRequest("POST","https://accounts.google.com/o/oauth2/token",NULL,"application/x-www-form-urlencoded",body,ssl,buff,sizeof(buff));CHKERRQ(ierr); 446 ierr = PetscSSLDestroyContext(ctx);CHKERRQ(ierr); 447 close(sock); 448 449 ierr = PetscStrstr(buff,"\"access_token\" : \"",&access);CHKERRQ(ierr); 450 if (!access) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Did not receive access token from Google"); 451 access += 18; 452 ierr = PetscStrchr(access,'\"',&ctmp);CHKERRQ(ierr); 453 if (!ctmp) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Access token from Google is misformed"); 454 *ctmp = 0; 455 ierr = PetscStrncpy(access_token,access,tokensize);CHKERRQ(ierr); 456 *ctmp = '\"'; 457 458 ierr = PetscStrstr(buff,"\"refresh_token\" : \"",&refresh);CHKERRQ(ierr); 459 if (!refresh) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Did not receive refresh token from Google"); 460 refresh += 19; 461 ierr = PetscStrchr(refresh,'\"',&ctmp);CHKERRQ(ierr); 462 if (!ctmp) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Refresh token from Google is misformed"); 463 *ctmp = 0; 464 ierr = PetscStrncpy(refresh_token,refresh,tokensize);CHKERRQ(ierr); 465 } 466 PetscFunctionReturn(0); 467 } 468 469 470 #undef __FUNCT__ 471 #define __FUNCT__ "PetscURLShorten" 472 /*@C 473 PetscURLShorten - Uses Google's service to get a short url for a long url 474 475 Input Parameters: 476 + url - long URL you want shortened 477 - lenshorturl - length of buffer to contain short URL 478 479 Output Parameter: 480 . shorturl - the shortened URL 481 482 .seealso: PetscGoogleDriveRefresh(), PetscGoogleDriveUpload(), PetscGoogleDriveAuthorize() 483 @*/ 484 PetscErrorCode PetscURLShorten(const char url[],char shorturl[],size_t lenshorturl) 485 { 486 SSL_CTX *ctx; 487 SSL *ssl; 488 int sock; 489 PetscErrorCode ierr; 490 char buff[1024],body[512],*sub1,*sub2; 491 492 PetscFunctionBegin; 493 ierr = PetscSSLInitializeContext(&ctx);CHKERRQ(ierr); 494 ierr = PetscHTTPSConnect("www.googleapis.com",443,ctx,&sock,&ssl);CHKERRQ(ierr); 495 ierr = PetscSNPrintf(body,512,"{\"longUrl\": \"%s\"}",url);CHKERRQ(ierr); 496 ierr = PetscHTTPSRequest("POST","https://www.googleapis.com/urlshortener/v1/url",NULL,"application/json",body,ssl,buff,sizeof(buff));CHKERRQ(ierr); 497 ierr = PetscSSLDestroyContext(ctx);CHKERRQ(ierr); 498 close(sock); 499 ierr = PetscStrstr(buff,"\"id\": \"",&sub1);CHKERRQ(ierr); 500 if (sub1) { 501 sub1 += 7; 502 ierr = PetscStrstr(sub1,"\"",&sub2);CHKERRQ(ierr); 503 if (!sub2) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Google did not shorten URL"); 504 sub2[0] = 0; 505 ierr = PetscStrncpy(shorturl,sub1,lenshorturl);CHKERRQ(ierr); 506 } else SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Google did not shorten URL"); 507 PetscFunctionReturn(0); 508 } 509 510