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