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