xref: /petsc/src/sys/webclient/client.c (revision fe278a28753fced19e37d8a7d85f74fadc2143a9)
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