xref: /petsc/src/sys/webclient/box.c (revision 68e695932dc3ba76353bc2baf983104ab033ae17)
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],*access,*refresh,*ctmp;
112   PetscMPIInt    rank;
113   PetscBool      flg;
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   = PetscStrstr(buff,"\"access_token\" : \"",&access);CHKERRQ(ierr);
146     if (!access) {ierr   = PetscStrstr(buff,"\"access_token\":\"",&access);CHKERRQ(ierr);}
147     if (!access) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Did not receive access token from Box");
148     access += 18;
149     ierr   = PetscStrchr(access,'\"',&ctmp);CHKERRQ(ierr);
150     if (!ctmp) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Access token from Box is misformed");
151     *ctmp  = 0;
152     ierr   = PetscStrncpy(access_token,access,tokensize);CHKERRQ(ierr);
153     *ctmp  = '\"';
154 
155     ierr   = PetscStrstr(buff,"\"refresh_token\" : \"",&refresh);CHKERRQ(ierr);
156     if (!refresh) {ierr   = PetscStrstr(buff,"\"refresh_token\":\"",&refresh);CHKERRQ(ierr);}
157     if (!refresh) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Did not receive refresh token from Box");
158     refresh += 19;
159     ierr   = PetscStrchr(refresh,'\"',&ctmp);CHKERRQ(ierr);
160     if (!ctmp) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Refresh token from Box is misformed");
161     *ctmp  = 0;
162     ierr = PetscStrncpy(refresh_token,refresh,tokensize);CHKERRQ(ierr);
163 
164     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);
165     ierr = PetscPrintf(comm,"programs with the option -box_refresh_token %s\n",refresh);CHKERRQ(ierr);
166     ierr = PetscPrintf(comm,"to access Box Drive automatically\n");CHKERRQ(ierr);
167   }
168   PetscFunctionReturn(0);
169 }
170 #endif
171 
172 #undef __FUNCT__
173 #define __FUNCT__ "PetscBoxRefresh"
174 /*@C
175      PetscBoxRefresh - Get a new authorization token for accessing Box drive from PETSc from a refresh token
176 
177    Not collective, only the first process in the MPI_Comm does anything
178 
179    Input Parameters:
180 +   comm - MPI communicator
181 .   refresh token - obtained with PetscBoxAuthorize(), if NULL PETSc will first look for one in the options data
182                     if not found it will call PetscBoxAuthorize()
183 -   tokensize - size of the output string access_token
184 
185    Output Parameter:
186 +   access_token - token that can be passed to PetscBoxUpload()
187 -   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
188 
189 .seealso: PetscURLShorten(), PetscBoxAuthorize(), PetscBoxUpload()
190 
191 @*/
192 PetscErrorCode PetscBoxRefresh(MPI_Comm comm,const char refresh_token[],char access_token[],char new_refresh_token[],size_t tokensize)
193 {
194   SSL_CTX        *ctx;
195   SSL            *ssl;
196   int            sock;
197   PetscErrorCode ierr;
198   char           buff[8*1024],body[1024],*access,*ctmp;
199   PetscMPIInt    rank;
200   char           *refreshtoken = (char*)refresh_token,*refresh;
201 
202   PetscFunctionBegin;
203   ierr = MPI_Comm_rank(comm,&rank);CHKERRQ(ierr);
204   if (!rank) {
205     if (!refresh_token) {
206       PetscBool set;
207       ierr = PetscMalloc1(512,&refreshtoken);CHKERRQ(ierr);
208       ierr = PetscOptionsGetString(NULL,"-box_refresh_token",refreshtoken,512,&set);CHKERRQ(ierr);
209 #if defined(PETSC_HAVE_SAWS)
210       if (!set) {
211         ierr = PetscBoxAuthorize(comm,access_token,new_refresh_token,512*sizeof(char));CHKERRQ(ierr);
212         ierr = PetscFree(refreshtoken);CHKERRQ(ierr);
213         PetscFunctionReturn(0);
214       }
215 #else
216       if (!set) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Must provide refresh token with -box_refresh_token XXX");
217 #endif
218     }
219     ierr = PetscSSLInitializeContext(&ctx);CHKERRQ(ierr);
220     ierr = PetscHTTPSConnect("www.box.com",443,ctx,&sock,&ssl);CHKERRQ(ierr);
221     ierr = PetscStrcpy(body,"client_id=");CHKERRQ(ierr);
222     ierr = PetscStrcat(body,PETSC_BOX_CLIENT_ID);CHKERRQ(ierr);
223     ierr = PetscStrcat(body,"&client_secret=");CHKERRQ(ierr);
224     ierr = PetscStrcat(body,PETSC_BOX_CLIENT_ST);CHKERRQ(ierr);
225     ierr = PetscStrcat(body,"&refresh_token=");CHKERRQ(ierr);
226     ierr = PetscStrcat(body,refreshtoken);CHKERRQ(ierr);
227     if (!refresh_token) {ierr = PetscFree(refreshtoken);CHKERRQ(ierr);}
228     ierr = PetscStrcat(body,"&grant_type=refresh_token");CHKERRQ(ierr);
229 
230     ierr = PetscHTTPSRequest("POST","www.box.com/api/oauth2/token",NULL,"application/x-www-form-urlencoded",body,ssl,buff,sizeof(buff));CHKERRQ(ierr);
231     ierr = PetscSSLDestroyContext(ctx);CHKERRQ(ierr);
232     close(sock);
233 
234     ierr   = PetscStrstr(buff,"\"access_token\":\"",&access);CHKERRQ(ierr);
235     if (!access) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Did not receive access token from Box");
236     access += 16;
237     ierr   = PetscStrchr(access,'\"',&ctmp);CHKERRQ(ierr);
238     if (!ctmp) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Access token from Box is misformed");
239     *ctmp  = 0;
240     ierr   = PetscStrncpy(access_token,access,tokensize);CHKERRQ(ierr);
241     *ctmp  = '\"';
242 
243     ierr   = PetscStrstr(buff,"\"refresh_token\":\"",&refresh);CHKERRQ(ierr);
244     if (!refresh) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Did not receive refresh token from Box");
245     refresh += 17;
246     ierr   = PetscStrchr(refresh,'\"',&ctmp);CHKERRQ(ierr);
247     if (!ctmp) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Refresh token from Box is misformed");
248     *ctmp  = 0;
249     ierr = PetscStrncpy(new_refresh_token,refresh,tokensize);CHKERRQ(ierr);
250     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);
251     ierr = PetscPrintf(comm,"programs with the option -box_refresh_token %s\n",refresh);CHKERRQ(ierr);
252     ierr = PetscPrintf(comm,"to access Box Drive automatically\n");CHKERRQ(ierr);
253   }
254   PetscFunctionReturn(0);
255 }
256 
257 #include <sys/stat.h>
258 
259 #undef __FUNCT__
260 #define __FUNCT__ "PetscBoxUpload"
261 /*@C
262      PetscBoxUpload - Loads a file to the Box Drive
263 
264      This routine has not yet been written; it is just copied from Google Drive
265 
266      Not collective, only the first process in the MPI_Comm uploads the file
267 
268   Input Parameters:
269 +   comm - MPI communicator
270 .   access_token - obtained with PetscBoxRefresh(), pass NULL to have PETSc generate one
271 -   filename - file to upload; if you upload multiple times it will have different names each time on Box Drive
272 
273   Options Database:
274 .  -box_refresh_token   XXX
275 
276   Usage Patterns:
277     With PETSc option -box_refresh_token  XXX given
278     PetscBoxUpload(comm,NULL,filename);        will upload file with no user interaction
279 
280     Without PETSc option -box_refresh_token XXX given
281     PetscBoxUpload(comm,NULL,filename);        for first use will prompt user to authorize access to Box Drive with their processor
282 
283     With PETSc option -box_refresh_token  XXX given
284     PetscBoxRefresh(comm,NULL,access_token,sizeof(access_token));
285     PetscBoxUpload(comm,access_token,filename);
286 
287     With refresh token entered in some way by the user
288     PetscBoxRefresh(comm,refresh_token,access_token,sizeof(access_token));
289     PetscBoxUpload(comm,access_token,filename);
290 
291     PetscBoxAuthorize(comm,access_token,refresh_token,sizeof(access_token));
292     PetscBoxUpload(comm,access_token,filename);
293 
294 .seealso: PetscURLShorten(), PetscBoxAuthorize(), PetscBoxRefresh()
295 
296 @*/
297 PetscErrorCode PetscBoxUpload(MPI_Comm comm,const char access_token[],const char filename[])
298 {
299   SSL_CTX        *ctx;
300   SSL            *ssl;
301   int            sock;
302   PetscErrorCode ierr;
303   char           head[1024],buff[8*1024],*body,*title;
304   PetscMPIInt    rank;
305   struct stat    sb;
306   size_t         len,blen,rd;
307   FILE           *fd;
308 
309   PetscFunctionBegin;
310   ierr = MPI_Comm_rank(comm,&rank);CHKERRQ(ierr);
311   if (!rank) {
312     ierr = PetscStrcpy(head,"Authorization: Bearer ");CHKERRQ(ierr);
313     ierr = PetscStrcat(head,access_token);CHKERRQ(ierr);
314     ierr = PetscStrcat(head,"\r\n");CHKERRQ(ierr);
315     ierr = PetscStrcat(head,"uploadType: multipart\r\n");CHKERRQ(ierr);
316 
317     ierr = stat(filename,&sb);
318     if (ierr) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_FILE_OPEN,"Unable to stat file: %s",filename);
319     len = 1024 + sb.st_size;
320     ierr = PetscMalloc1(len,&body);CHKERRQ(ierr);
321     ierr = PetscStrcpy(body,"--foo_bar_baz\r\n"
322                          "Content-Type: application/json\r\n\r\n"
323                          "{"
324                          "\"title\": \"");
325     ierr = PetscStrcat(body,filename);
326     ierr = PetscStrcat(body,"\","
327                          "\"mimeType\": \"text.html\","
328                          "\"description\": \" a file\""
329                          "}\r\n\r\n"
330                          "--foo_bar_baz\r\n"
331                          "Content-Type: text/html\r\n\r\n");
332     ierr = PetscStrlen(body,&blen);CHKERRQ(ierr);
333     fd = fopen (filename, "r");
334     if (!fd) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_FILE_OPEN,"Unable to open file: %s",filename);
335     rd = fread (body+blen, sizeof (unsigned char), sb.st_size, fd);
336     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);
337     fclose(fd);
338     body[blen + rd] = 0;
339     ierr = PetscStrcat(body,"\r\n\r\n"
340                             "--foo_bar_baz\r\n");
341     ierr = PetscSSLInitializeContext(&ctx);CHKERRQ(ierr);
342     ierr = PetscHTTPSConnect("www.boxapis.com",443,ctx,&sock,&ssl);CHKERRQ(ierr);
343     ierr = PetscHTTPSRequest("POST","www.boxapis.com/upload/drive/v2/files/",head,"multipart/related; boundary=\"foo_bar_baz\"",body,ssl,buff,sizeof(buff));CHKERRQ(ierr);
344     ierr = PetscFree(body);CHKERRQ(ierr);
345     ierr = PetscSSLDestroyContext(ctx);CHKERRQ(ierr);
346     close(sock);
347     ierr   = PetscStrstr(buff,"\"title\"",&title);CHKERRQ(ierr);
348     if (!title) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_LIB,"Upload of file %s failed",filename);
349   }
350   PetscFunctionReturn(0);
351 }
352 
353 
354