xref: /petsc/src/sys/webclient/box.c (revision 98c3331e5c81c4bfa5036a9b6bc521ec2d439166)
1 
2 #include <petscwebclient.h>
3 
4 /*
5    These variables identify the code as a PETSc application to Box.
6 
7    See -   http://stackoverflow.com/questions/4616553/using-oauth-in-free-open-source-software
8    Users can get their own application IDs - goto https://developers.box.com
9 
10 */
11 #define PETSC_BOX_CLIENT_ID  "sse42nygt4zqgrdwi0luv79q1u1f0xza"
12 #define PETSC_BOX_CLIENT_ST  "A0Dy4KgOYLB2JIYZqpbze4EzjeIiX5k4"
13 
14 #if defined(PETSC_HAVE_SAWS)
15 #include <mongoose.h>
16 
17 static volatile char *result = NULL;
18 
19 static int PetscBoxWebServer_Private(struct mg_connection *conn)
20 {
21   const struct mg_request_info *request_info = mg_get_request_info(conn);
22   result = (char*) request_info->query_string;
23   return 1;  /* Mongoose will now not handle the request */
24 }
25 
26 /*
27     Box can only return an authorization code to a Webserver, hence we need to start one up and wait for
28     the authorization code to arrive from Box
29 */
30 static PetscErrorCode PetscBoxStartWebServer_Private(void)
31 {
32   PetscErrorCode      ierr;
33   int                 optionsLen = 5;
34   const char          *options[optionsLen];
35   struct mg_callbacks callbacks;
36   struct mg_context   *ctx;
37   char                keyfile[PETSC_MAX_PATH_LEN];
38   PetscBool           exists;
39 
40   PetscFunctionBegin;
41   options[0] = "listening_ports";
42   options[1] = "8081s";
43 
44   ierr = PetscStrcpy(keyfile,"sslclient.pem");CHKERRQ(ierr);
45   ierr = PetscTestFile(keyfile,'r',&exists);CHKERRQ(ierr);
46   if (!exists) {
47     ierr = PetscGetHomeDirectory(keyfile,PETSC_MAX_PATH_LEN);CHKERRQ(ierr);
48     ierr = PetscStrcat(keyfile,"/");CHKERRQ(ierr);
49     ierr = PetscStrcat(keyfile,"sslclient.pem");CHKERRQ(ierr);
50     ierr = PetscTestFile(keyfile,'r',&exists);CHKERRQ(ierr);
51     if (!exists) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FILE_OPEN,"Unable to locate sslclient.pem file in current directory or home directory");
52   }
53 
54   options[2] = "ssl_certificate";
55   options[3] = keyfile;
56   options[4] = NULL;
57 
58   /* Prepare callbacks structure. We have only one callback, the rest are NULL. */
59   ierr = PetscMemzero(&callbacks, sizeof(callbacks));CHKERRQ(ierr);
60   callbacks.begin_request = PetscBoxWebServer_Private;
61   ctx = mg_start(&callbacks, NULL, options);
62   if (!ctx) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Unable to start up webserver");
63   while (!result) {};
64   PetscFunctionReturn(0);
65 }
66 
67 #if defined(PETSC_HAVE_UNISTD_H)
68 #include <unistd.h>
69 #endif
70 
71 #undef __FUNCT__
72 #define __FUNCT__ "PetscBoxAuthorize"
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 .seealso: PetscBoxRefresh(), PetscBoxUpload(), PetscURLShorten()
103 
104 @*/
105 PetscErrorCode PetscBoxAuthorize(MPI_Comm comm,char access_token[],char refresh_token[],size_t tokensize)
106 {
107   SSL_CTX        *ctx;
108   SSL            *ssl;
109   int            sock;
110   PetscErrorCode ierr;
111   char           buff[8*1024],body[1024];
112   PetscMPIInt    rank;
113   PetscBool      flg,found;
114 
115   PetscFunctionBegin;
116   ierr = MPI_Comm_rank(comm,&rank);CHKERRQ(ierr);
117   if (!rank) {
118     if (!isatty(fileno(PETSC_STDOUT))) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_USER,"Requires users input/output");
119     ierr = PetscPrintf(comm,"Cut and paste the following into your browser:\n\n"
120                             "https://www.box.com/api/oauth2/authorize?"
121                             "response_type=code&"
122                             "client_id="
123                             PETSC_BOX_CLIENT_ID
124                             "&state=PETScState"
125                             "\n\n");CHKERRQ(ierr);
126     ierr = PetscBoxStartWebServer_Private();CHKERRQ(ierr);
127     ierr = PetscStrbeginswith((const char*)result,"state=PETScState&code=",&flg);CHKERRQ(ierr);
128     if (!flg) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_LIB,"Did not get expected string from Box got %s",result);
129     ierr = PetscStrncpy(buff,(const char*)result+22,sizeof(buff));CHKERRQ(ierr);
130 
131     ierr = PetscSSLInitializeContext(&ctx);CHKERRQ(ierr);
132     ierr = PetscHTTPSConnect("www.box.com",443,ctx,&sock,&ssl);CHKERRQ(ierr);
133     ierr = PetscStrcpy(body,"code=");CHKERRQ(ierr);
134     ierr = PetscStrcat(body,buff);CHKERRQ(ierr);
135     ierr = PetscStrcat(body,"&client_id=");CHKERRQ(ierr);
136     ierr = PetscStrcat(body,PETSC_BOX_CLIENT_ID);CHKERRQ(ierr);
137     ierr = PetscStrcat(body,"&client_secret=");CHKERRQ(ierr);
138     ierr = PetscStrcat(body,PETSC_BOX_CLIENT_ST);CHKERRQ(ierr);
139     ierr = PetscStrcat(body,"&grant_type=authorization_code");CHKERRQ(ierr);
140 
141     ierr = PetscHTTPSRequest("POST","www.box.com/api/oauth2/token",NULL,"application/x-www-form-urlencoded",body,ssl,buff,sizeof(buff));CHKERRQ(ierr);
142     ierr = PetscSSLDestroyContext(ctx);CHKERRQ(ierr);
143     close(sock);
144 
145     ierr   = PetscPullJSONValue(buff,"access_token",access_token,tokensize,&found);CHKERRQ(ierr);
146     if (!found) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Box did not return access token");
147     ierr   = PetscPullJSONValue(buff,"refresh_token",refresh_token,tokensize,&found);CHKERRQ(ierr);
148     if (!found) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Box did not return refresh token");
149 
150     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);
151     ierr = PetscPrintf(comm,"programs with the option -box_refresh_token %s\n",refresh_token);CHKERRQ(ierr);
152     ierr = PetscPrintf(comm,"to access Box Drive automatically\n");CHKERRQ(ierr);
153   }
154   PetscFunctionReturn(0);
155 }
156 #endif
157 
158 #undef __FUNCT__
159 #define __FUNCT__ "PetscBoxRefresh"
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 .seealso: PetscURLShorten(), PetscBoxAuthorize(), PetscBoxUpload()
176 
177 @*/
178 PetscErrorCode PetscBoxRefresh(MPI_Comm comm,const char refresh_token[],char access_token[],char new_refresh_token[],size_t tokensize)
179 {
180   SSL_CTX        *ctx;
181   SSL            *ssl;
182   int            sock;
183   PetscErrorCode ierr;
184   char           buff[8*1024],body[1024];
185   PetscMPIInt    rank;
186   char           *refreshtoken = (char*)refresh_token;
187   PetscBool      found;
188 
189   PetscFunctionBegin;
190   ierr = MPI_Comm_rank(comm,&rank);CHKERRQ(ierr);
191   if (!rank) {
192     if (!refresh_token) {
193       PetscBool set;
194       ierr = PetscMalloc1(512,&refreshtoken);CHKERRQ(ierr);
195       ierr = PetscOptionsGetString(NULL,"-box_refresh_token",refreshtoken,512,&set);CHKERRQ(ierr);
196 #if defined(PETSC_HAVE_SAWS)
197       if (!set) {
198         ierr = PetscBoxAuthorize(comm,access_token,new_refresh_token,512*sizeof(char));CHKERRQ(ierr);
199         ierr = PetscFree(refreshtoken);CHKERRQ(ierr);
200         PetscFunctionReturn(0);
201       }
202 #else
203       if (!set) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Must provide refresh token with -box_refresh_token XXX");
204 #endif
205     }
206     ierr = PetscSSLInitializeContext(&ctx);CHKERRQ(ierr);
207     ierr = PetscHTTPSConnect("www.box.com",443,ctx,&sock,&ssl);CHKERRQ(ierr);
208     ierr = PetscStrcpy(body,"client_id=");CHKERRQ(ierr);
209     ierr = PetscStrcat(body,PETSC_BOX_CLIENT_ID);CHKERRQ(ierr);
210     ierr = PetscStrcat(body,"&client_secret=");CHKERRQ(ierr);
211     ierr = PetscStrcat(body,PETSC_BOX_CLIENT_ST);CHKERRQ(ierr);
212     ierr = PetscStrcat(body,"&refresh_token=");CHKERRQ(ierr);
213     ierr = PetscStrcat(body,refreshtoken);CHKERRQ(ierr);
214     if (!refresh_token) {ierr = PetscFree(refreshtoken);CHKERRQ(ierr);}
215     ierr = PetscStrcat(body,"&grant_type=refresh_token");CHKERRQ(ierr);
216 
217     ierr = PetscHTTPSRequest("POST","www.box.com/api/oauth2/token",NULL,"application/x-www-form-urlencoded",body,ssl,buff,sizeof(buff));CHKERRQ(ierr);
218     ierr = PetscSSLDestroyContext(ctx);CHKERRQ(ierr);
219     close(sock);
220 
221     ierr   = PetscPullJSONValue(buff,"access_token",access_token,tokensize,&found);CHKERRQ(ierr);
222     if (!found) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Box did not return access token");
223     ierr   = PetscPullJSONValue(buff,"refresh_token",new_refresh_token,tokensize,&found);CHKERRQ(ierr);
224     if (!found) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Box did not return refresh token");
225 
226     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);
227     ierr = PetscPrintf(comm,"programs with the option -box_refresh_token %s\n",new_refresh_token);CHKERRQ(ierr);
228     ierr = PetscPrintf(comm,"to access Box Drive automatically\n");CHKERRQ(ierr);
229   }
230   PetscFunctionReturn(0);
231 }
232 
233 #include <sys/stat.h>
234 
235 #undef __FUNCT__
236 #define __FUNCT__ "PetscBoxUpload"
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 .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   PetscErrorCode ierr;
279   char           head[1024],buff[8*1024],*body,*title;
280   PetscMPIInt    rank;
281   struct stat    sb;
282   size_t         len,blen,rd;
283   FILE           *fd;
284 
285   PetscFunctionBegin;
286   ierr = MPI_Comm_rank(comm,&rank);CHKERRQ(ierr);
287   if (!rank) {
288     ierr = PetscStrcpy(head,"Authorization: Bearer ");CHKERRQ(ierr);
289     ierr = PetscStrcat(head,access_token);CHKERRQ(ierr);
290     ierr = PetscStrcat(head,"\r\n");CHKERRQ(ierr);
291     ierr = PetscStrcat(head,"uploadType: multipart\r\n");CHKERRQ(ierr);
292 
293     ierr = stat(filename,&sb);
294     if (ierr) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_FILE_OPEN,"Unable to stat file: %s",filename);
295     len = 1024 + sb.st_size;
296     ierr = PetscMalloc1(len,&body);CHKERRQ(ierr);
297     ierr = PetscStrcpy(body,"--foo_bar_baz\r\n"
298                             "Content-Type: application/json\r\n\r\n"
299                             "{");CHKERRQ(ierr);
300     ierr = PetscPushJSONValue(body,"title",filename,len);CHKERRQ(ierr);
301     ierr = PetscStrcat(body,",");CHKERRQ(ierr);
302     ierr = PetscPushJSONValue(body,"mimeType","text.html",len);CHKERRQ(ierr);
303     ierr = PetscStrcat(body,",");CHKERRQ(ierr);
304     ierr = PetscPushJSONValue(body,"description","a file",len);CHKERRQ(ierr);
305     ierr = PetscStrcat(body, "}\r\n\r\n"
306                              "--foo_bar_baz\r\n"
307                              "Content-Type: text/html\r\n\r\n");CHKERRQ(ierr);
308     ierr = PetscStrlen(body,&blen);CHKERRQ(ierr);
309     fd = fopen (filename, "r");
310     if (!fd) SETERRQ1(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     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);
313     fclose(fd);
314     body[blen + rd] = 0;
315     ierr = PetscStrcat(body,"\r\n\r\n"
316                             "--foo_bar_baz\r\n");
317     ierr = PetscSSLInitializeContext(&ctx);CHKERRQ(ierr);
318     ierr = PetscHTTPSConnect("www.boxapis.com",443,ctx,&sock,&ssl);CHKERRQ(ierr);
319     ierr = PetscHTTPSRequest("POST","www.boxapis.com/upload/drive/v2/files/",head,"multipart/related; boundary=\"foo_bar_baz\"",body,ssl,buff,sizeof(buff));CHKERRQ(ierr);
320     ierr = PetscFree(body);CHKERRQ(ierr);
321     ierr = PetscSSLDestroyContext(ctx);CHKERRQ(ierr);
322     close(sock);
323     ierr   = PetscStrstr(buff,"\"title\"",&title);CHKERRQ(ierr);
324     if (!title) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_LIB,"Upload of file %s failed",filename);
325   }
326   PetscFunctionReturn(0);
327 }
328 
329 
330