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