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