xref: /petsc/src/sys/webclient/box.c (revision bb04b57d8fc8b99a9ce04c03188cf4f575836949)
1 
2 #include <petscwebclient.h>
3 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
4 
5 /*
6    These variables identify the code as a PETSc application to Box.
7 
8    See -   http://stackoverflow.com/questions/4616553/using-oauth-in-free-open-source-software
9    Users can get their own application IDs - goto https://developers.box.com
10 
11 */
12 #define PETSC_BOX_CLIENT_ID  "sse42nygt4zqgrdwi0luv79q1u1f0xza"
13 #define PETSC_BOX_CLIENT_ST  "A0Dy4KgOYLB2JIYZqpbze4EzjeIiX5k4"
14 
15 #if defined(PETSC_HAVE_SAWS)
16 #include <mongoose.h>
17 
18 static volatile char *result = NULL;
19 
20 static int PetscBoxWebServer_Private(struct mg_connection *conn)
21 {
22   const struct mg_request_info *request_info = mg_get_request_info(conn);
23   result = (char*) request_info->query_string;
24   return 1;  /* Mongoose will now not handle the request */
25 }
26 
27 /*
28     Box can only return an authorization code to a Webserver, hence we need to start one up and wait for
29     the authorization code to arrive from Box
30 */
31 static PetscErrorCode PetscBoxStartWebServer_Private(void)
32 {
33   PetscErrorCode      ierr;
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   ierr = PetscStrcpy(keyfile,"sslclient.pem");CHKERRQ(ierr);
46   ierr = PetscTestFile(keyfile,'r',&exists);CHKERRQ(ierr);
47   if (!exists) {
48     ierr = PetscGetHomeDirectory(keyfile,PETSC_MAX_PATH_LEN);CHKERRQ(ierr);
49     ierr = PetscStrcat(keyfile,"/");CHKERRQ(ierr);
50     ierr = PetscStrcat(keyfile,"sslclient.pem");CHKERRQ(ierr);
51     ierr = PetscTestFile(keyfile,'r',&exists);CHKERRQ(ierr);
52     if (!exists) SETERRQ(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   ierr = PetscMemzero(&callbacks, sizeof(callbacks));CHKERRQ(ierr);
61   callbacks.begin_request = PetscBoxWebServer_Private;
62   ctx = mg_start(&callbacks, NULL, options);
63   if (!ctx) SETERRQ(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 #undef __FUNCT__
73 #define __FUNCT__ "PetscBoxAuthorize"
74 /*@C
75      PetscBoxAuthorize - Get authorization and refresh token for accessing Box drive from PETSc
76 
77    Not collective, only the first process in MPI_Comm does anything
78 
79    Input Parameters:
80 +  comm - the MPI communicator
81 -  tokensize - size of the token arrays
82 
83    Output Parameters:
84 +  access_token - can be used with PetscBoxUpload() for this one session
85 -  refresh_token - can be used for ever to obtain new access_tokens with PetscBoxRefresh(), guard this like a password
86                    it gives access to your Box Drive
87 
88    Notes: This call requires stdout and stdin access from process 0 on the MPI communicator
89 
90    You can run src/sys/webclient/examples/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 .seealso: PetscBoxRefresh(), PetscBoxUpload(), PetscURLShorten()
104 
105 @*/
106 PetscErrorCode PetscBoxAuthorize(MPI_Comm comm,char access_token[],char refresh_token[],size_t tokensize)
107 {
108   SSL_CTX        *ctx;
109   SSL            *ssl;
110   int            sock;
111   PetscErrorCode ierr;
112   char           buff[8*1024],body[1024];
113   PetscMPIInt    rank;
114   PetscBool      flg,found;
115 
116   PetscFunctionBegin;
117   ierr = MPI_Comm_rank(comm,&rank);CHKERRQ(ierr);
118   if (!rank) {
119     if (!isatty(fileno(PETSC_STDOUT))) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_USER,"Requires users input/output");
120     ierr = 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");CHKERRQ(ierr);
127     ierr = PetscBoxStartWebServer_Private();CHKERRQ(ierr);
128     ierr = PetscStrbeginswith((const char*)result,"state=PETScState&code=",&flg);CHKERRQ(ierr);
129     if (!flg) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_LIB,"Did not get expected string from Box got %s",result);
130     ierr = PetscStrncpy(buff,(const char*)result+22,sizeof(buff));CHKERRQ(ierr);
131 
132     ierr = PetscSSLInitializeContext(&ctx);CHKERRQ(ierr);
133     ierr = PetscHTTPSConnect("www.box.com",443,ctx,&sock,&ssl);CHKERRQ(ierr);
134     ierr = PetscStrcpy(body,"code=");CHKERRQ(ierr);
135     ierr = PetscStrcat(body,buff);CHKERRQ(ierr);
136     ierr = PetscStrcat(body,"&client_id=");CHKERRQ(ierr);
137     ierr = PetscStrcat(body,PETSC_BOX_CLIENT_ID);CHKERRQ(ierr);
138     ierr = PetscStrcat(body,"&client_secret=");CHKERRQ(ierr);
139     ierr = PetscStrcat(body,PETSC_BOX_CLIENT_ST);CHKERRQ(ierr);
140     ierr = PetscStrcat(body,"&grant_type=authorization_code");CHKERRQ(ierr);
141 
142     ierr = PetscHTTPSRequest("POST","www.box.com/api/oauth2/token",NULL,"application/x-www-form-urlencoded",body,ssl,buff,sizeof(buff));CHKERRQ(ierr);
143     ierr = PetscSSLDestroyContext(ctx);CHKERRQ(ierr);
144     close(sock);
145 
146     ierr   = PetscPullJSONValue(buff,"access_token",access_token,tokensize,&found);CHKERRQ(ierr);
147     if (!found) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Box did not return access token");
148     ierr   = PetscPullJSONValue(buff,"refresh_token",refresh_token,tokensize,&found);CHKERRQ(ierr);
149     if (!found) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Box did not return refresh token");
150 
151     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);
152     ierr = PetscPrintf(comm,"programs with the option -box_refresh_token %s\n",refresh_token);CHKERRQ(ierr);
153     ierr = PetscPrintf(comm,"to access Box Drive automatically\n");CHKERRQ(ierr);
154   }
155   PetscFunctionReturn(0);
156 }
157 #endif
158 
159 #undef __FUNCT__
160 #define __FUNCT__ "PetscBoxRefresh"
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 .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   PetscErrorCode ierr;
185   char           buff[8*1024],body[1024];
186   PetscMPIInt    rank;
187   char           *refreshtoken = (char*)refresh_token;
188   PetscBool      found;
189 
190   PetscFunctionBegin;
191   ierr = MPI_Comm_rank(comm,&rank);CHKERRQ(ierr);
192   if (!rank) {
193     if (!refresh_token) {
194       PetscBool set;
195       ierr = PetscMalloc1(512,&refreshtoken);CHKERRQ(ierr);
196       ierr = PetscOptionsGetString(NULL,"-box_refresh_token",refreshtoken,512,&set);CHKERRQ(ierr);
197 #if defined(PETSC_HAVE_SAWS)
198       if (!set) {
199         ierr = PetscBoxAuthorize(comm,access_token,new_refresh_token,512*sizeof(char));CHKERRQ(ierr);
200         ierr = PetscFree(refreshtoken);CHKERRQ(ierr);
201         PetscFunctionReturn(0);
202       }
203 #else
204       if (!set) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Must provide refresh token with -box_refresh_token XXX");
205 #endif
206     }
207     ierr = PetscSSLInitializeContext(&ctx);CHKERRQ(ierr);
208     ierr = PetscHTTPSConnect("www.box.com",443,ctx,&sock,&ssl);CHKERRQ(ierr);
209     ierr = PetscStrcpy(body,"client_id=");CHKERRQ(ierr);
210     ierr = PetscStrcat(body,PETSC_BOX_CLIENT_ID);CHKERRQ(ierr);
211     ierr = PetscStrcat(body,"&client_secret=");CHKERRQ(ierr);
212     ierr = PetscStrcat(body,PETSC_BOX_CLIENT_ST);CHKERRQ(ierr);
213     ierr = PetscStrcat(body,"&refresh_token=");CHKERRQ(ierr);
214     ierr = PetscStrcat(body,refreshtoken);CHKERRQ(ierr);
215     if (!refresh_token) {ierr = PetscFree(refreshtoken);CHKERRQ(ierr);}
216     ierr = PetscStrcat(body,"&grant_type=refresh_token");CHKERRQ(ierr);
217 
218     ierr = PetscHTTPSRequest("POST","www.box.com/api/oauth2/token",NULL,"application/x-www-form-urlencoded",body,ssl,buff,sizeof(buff));CHKERRQ(ierr);
219     ierr = PetscSSLDestroyContext(ctx);CHKERRQ(ierr);
220     close(sock);
221 
222     ierr   = PetscPullJSONValue(buff,"access_token",access_token,tokensize,&found);CHKERRQ(ierr);
223     if (!found) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Box did not return access token");
224     ierr   = PetscPullJSONValue(buff,"refresh_token",new_refresh_token,tokensize,&found);CHKERRQ(ierr);
225     if (!found) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Box did not return refresh token");
226 
227     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);
228     ierr = PetscPrintf(comm,"programs with the option -box_refresh_token %s\n",new_refresh_token);CHKERRQ(ierr);
229     ierr = PetscPrintf(comm,"to access Box Drive automatically\n");CHKERRQ(ierr);
230   }
231   PetscFunctionReturn(0);
232 }
233 
234 #include <sys/stat.h>
235 
236 #undef __FUNCT__
237 #define __FUNCT__ "PetscBoxUpload"
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 .seealso: PetscURLShorten(), PetscBoxAuthorize(), PetscBoxRefresh()
272 
273 @*/
274 PetscErrorCode PetscBoxUpload(MPI_Comm comm,const char access_token[],const char filename[])
275 {
276   SSL_CTX        *ctx;
277   SSL            *ssl;
278   int            sock;
279   PetscErrorCode ierr;
280   char           head[1024],buff[8*1024],*body,*title;
281   PetscMPIInt    rank;
282   struct stat    sb;
283   size_t         len,blen,rd;
284   FILE           *fd;
285 
286   PetscFunctionBegin;
287   ierr = MPI_Comm_rank(comm,&rank);CHKERRQ(ierr);
288   if (!rank) {
289     ierr = PetscStrcpy(head,"Authorization: Bearer ");CHKERRQ(ierr);
290     ierr = PetscStrcat(head,access_token);CHKERRQ(ierr);
291     ierr = PetscStrcat(head,"\r\n");CHKERRQ(ierr);
292     ierr = PetscStrcat(head,"uploadType: multipart\r\n");CHKERRQ(ierr);
293 
294     ierr = stat(filename,&sb);
295     if (ierr) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_FILE_OPEN,"Unable to stat file: %s",filename);
296     len = 1024 + sb.st_size;
297     ierr = PetscMalloc1(len,&body);CHKERRQ(ierr);
298     ierr = PetscStrcpy(body,"--foo_bar_baz\r\n"
299                             "Content-Type: application/json\r\n\r\n"
300                             "{");CHKERRQ(ierr);
301     ierr = PetscPushJSONValue(body,"title",filename,len);CHKERRQ(ierr);
302     ierr = PetscStrcat(body,",");CHKERRQ(ierr);
303     ierr = PetscPushJSONValue(body,"mimeType","text.html",len);CHKERRQ(ierr);
304     ierr = PetscStrcat(body,",");CHKERRQ(ierr);
305     ierr = PetscPushJSONValue(body,"description","a file",len);CHKERRQ(ierr);
306     ierr = PetscStrcat(body, "}\r\n\r\n"
307                              "--foo_bar_baz\r\n"
308                              "Content-Type: text/html\r\n\r\n");CHKERRQ(ierr);
309     ierr = PetscStrlen(body,&blen);CHKERRQ(ierr);
310     fd = fopen (filename, "r");
311     if (!fd) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_FILE_OPEN,"Unable to open file: %s",filename);
312     rd = fread (body+blen, sizeof (unsigned char), sb.st_size, fd);
313     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);
314     fclose(fd);
315     body[blen + rd] = 0;
316     ierr = PetscStrcat(body,"\r\n\r\n"
317                             "--foo_bar_baz\r\n");
318     ierr = PetscSSLInitializeContext(&ctx);CHKERRQ(ierr);
319     ierr = PetscHTTPSConnect("www.boxapis.com",443,ctx,&sock,&ssl);CHKERRQ(ierr);
320     ierr = PetscHTTPSRequest("POST","www.boxapis.com/upload/drive/v2/files/",head,"multipart/related; boundary=\"foo_bar_baz\"",body,ssl,buff,sizeof(buff));CHKERRQ(ierr);
321     ierr = PetscFree(body);CHKERRQ(ierr);
322     ierr = PetscSSLDestroyContext(ctx);CHKERRQ(ierr);
323     close(sock);
324     ierr   = PetscStrstr(buff,"\"title\"",&title);CHKERRQ(ierr);
325     if (!title) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_LIB,"Upload of file %s failed",filename);
326   }
327   PetscFunctionReturn(0);
328 }
329 
330 
331