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