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