xref: /petsc/src/sys/webclient/box.c (revision 2982c208629f08ee2cc040892793c02d4a46344c)
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 #include <mongoose.h>
15 
16 static volatile char *result = NULL;
17 
18 /*this is the main handler call. It switched based on what uri is in the request*/
19 static int PetscBoxWebServer_Private(struct mg_connection *conn)
20 {
21   const struct mg_request_info *request_info = mg_get_request_info(conn);
22   printf("Hi %s\n",request_info->uri);
23   printf("Hi %s\n",request_info->query_string);
24   result = (char*) request_info->query_string;
25 return 0;
26 }
27 
28 
29 static PetscErrorCode PetscBoxStartWebServer_Private(void)
30 {
31   PetscErrorCode      ierr;
32   int                 optionsLen = 5;
33   const char          *options[optionsLen];
34   struct mg_callbacks callbacks;
35   struct mg_context   *ctx;
36 
37   PetscFunctionBegin;
38   options[0] = "listening_ports";
39   options[1] = "8081s";
40   options[2] = "ssl_certificate";
41   options[3] = "/Users/barrysmith/Src/saws/saws.pem";
42   options[4] = NULL;
43 
44 
45   /* Prepare callbacks structure. We have only one callback, the rest are NULL. */
46   ierr = PetscMemzero(&callbacks, sizeof(callbacks));CHKERRQ(ierr);
47   callbacks.begin_request = PetscBoxWebServer_Private;
48   ctx = mg_start(&callbacks, NULL, options);
49   if (!ctx) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Unable to start up webserver");
50   while (!result) {};
51   PetscFunctionReturn(0);
52 }
53 
54 
55 #undef __FUNCT__
56 #define __FUNCT__ "PetscBoxAuthorize"
57 /*@C
58      PetscBoxAuthorize - Get authorization and refresh token for accessing Box drive from PETSc
59 
60    Not collective, only the first process in MPI_Comm does anything
61 
62    Input Parameters:
63 +  comm - the MPI communicator
64 -  tokensize - size of the token arrays
65 
66    Output Parameters:
67 +  access_token - can be used with PetscBoxUpload() for this one session
68 -  refresh_token - can be used for ever to obtain new access_tokens with PetscBoxRefresh(), guard this like a password
69                    it gives access to your Box Drive
70 
71    Notes: This call requires stdout and stdin access from process 0 on the MPI communicator
72 
73    You can run src/sys/webclient/examples/tutorials/obtainrefreshtoken to get a refresh token and then in the future pass it to
74    PETSc programs with -box_refresh_token XXX
75 
76    Developer Notes: For some reason I cannot get this to work! Box always replies with Bad Request and no details. Using Curl works
77    if one is fast enough.  Perhaps the problem is the need to urlencode the message?
78 
79 .seealso: PetscBoxRefresh(), PetscBoxUpload(), PetscURLShorten()
80 
81 @*/
82 PetscErrorCode PetscBoxAuthorize(MPI_Comm comm,char access_token[],char refresh_token[],size_t tokensize)
83 {
84   SSL_CTX        *ctx;
85   SSL            *ssl;
86   int            sock;
87   PetscErrorCode ierr;
88   char           buff[8*1024],body[1024],*access,*refresh,*ctmp;
89   PetscMPIInt    rank;
90   PetscBool      flg;
91 
92   PetscFunctionBegin;
93   ierr = MPI_Comm_rank(comm,&rank);CHKERRQ(ierr);
94   if (!rank) {
95     ierr = PetscPrintf(comm,"Cut and paste the following into your browser:\n\n"
96                             "https://www.box.com/api/oauth2/authorize?"
97                             "response_type=code&"
98                             "client_id="
99                             PETSC_BOX_CLIENT_ID
100                             "&state=PETScState"
101                             "\n\n");CHKERRQ(ierr);
102     ierr = PetscBoxStartWebServer_Private();CHKERRQ(ierr);
103     ierr = PetscStrbeginswith((const char*)result,"state=PETScState&code=",&flg);CHKERRQ(ierr);
104     if (!flg) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_LIB,"Did not get expected string from Box got %s",result);
105     ierr = PetscStrncpy(buff,(const char*)result+22,sizeof(buff));CHKERRQ(ierr);
106 
107     ierr = PetscSSLInitializeContext(&ctx);CHKERRQ(ierr);
108     ierr = PetscHTTPSConnect("www.box.com",443,ctx,&sock,&ssl);CHKERRQ(ierr);
109     ierr = PetscStrcpy(body,"code=");CHKERRQ(ierr);
110     ierr = PetscStrcat(body,buff);CHKERRQ(ierr);
111     ierr = PetscStrcat(body,"&client_id=");CHKERRQ(ierr);
112     ierr = PetscStrcat(body,PETSC_BOX_CLIENT_ID);CHKERRQ(ierr);
113     ierr = PetscStrcat(body,"&client_secret=");CHKERRQ(ierr);
114     ierr = PetscStrcat(body,PETSC_BOX_CLIENT_ST);CHKERRQ(ierr);
115     ierr = PetscStrcat(body,"&grant_type=authorization_code");CHKERRQ(ierr);
116 
117     ierr = PetscHTTPSRequest("POST","https://www.box.com/api/oauth2/token",NULL,"application/x-www-form-urlencoded",body,ssl,buff,sizeof(buff));CHKERRQ(ierr);
118     ierr = PetscSSLDestroyContext(ctx);CHKERRQ(ierr);
119     close(sock);
120 
121     ierr   = PetscStrstr(buff,"\"access_token\" : \"",&access);CHKERRQ(ierr);
122     if (!access) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Did not receive access token from Box");
123     access += 18;
124     ierr   = PetscStrchr(access,'\"',&ctmp);CHKERRQ(ierr);
125     if (!ctmp) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Access token from Box is misformed");
126     *ctmp  = 0;
127     ierr   = PetscStrncpy(access_token,access,tokensize);CHKERRQ(ierr);
128     *ctmp  = '\"';
129 
130     ierr   = PetscStrstr(buff,"\"refresh_token\" : \"",&refresh);CHKERRQ(ierr);
131     if (!refresh) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Did not receive refresh token from Box");
132     refresh += 19;
133     ierr   = PetscStrchr(refresh,'\"',&ctmp);CHKERRQ(ierr);
134     if (!ctmp) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Refresh token from Box is misformed");
135     *ctmp  = 0;
136     ierr = PetscStrncpy(refresh_token,refresh,tokensize);CHKERRQ(ierr);
137 
138     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);
139     ierr = PetscPrintf(comm,"programs with the option -box_refresh_token %d\n",refresh);CHKERRQ(ierr);
140     ierr = PetscPrintf(comm,"to access Box Drive automatically\n");CHKERRQ(ierr);
141   }
142   PetscFunctionReturn(0);
143 }
144 
145 #undef __FUNCT__
146 #define __FUNCT__ "PetscBoxRefresh"
147 /*@C
148      PetscBoxRefresh - Get a new authorization token for accessing Box drive from PETSc from a refresh token
149 
150    Not collective, only the first process in the MPI_Comm does anything
151 
152    Input Parameters:
153 +   comm - MPI communicator
154 .   refresh token - obtained with PetscBoxAuthorize(), if NULL PETSc will first look for one in the options data
155                     if not found it will call PetscBoxAuthorize()
156 -   tokensize - size of the output string access_token
157 
158    Output Parameter:
159 +   access_token - token that can be passed to PetscBoxUpload()
160 -   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
161 
162    Note: This doesn't work I cannot figure out why.
163 
164 .seealso: PetscURLShorten(), PetscBoxAuthorize(), PetscBoxUpload()
165 
166 @*/
167 PetscErrorCode PetscBoxRefresh(MPI_Comm comm,const char refresh_token[],char access_token[],char new_refresh_token[],size_t tokensize)
168 {
169   SSL_CTX        *ctx;
170   SSL            *ssl;
171   int            sock;
172   PetscErrorCode ierr;
173   char           buff[8*1024],body[1024],*access,*ctmp;
174   PetscMPIInt    rank;
175   char           *refreshtoken = (char*)refresh_token;
176 
177   PetscFunctionBegin;
178   ierr = MPI_Comm_rank(comm,&rank);CHKERRQ(ierr);
179   if (!rank) {
180     if (!refresh_token) {
181       PetscBool set;
182       ierr = PetscMalloc1(512,&refreshtoken);CHKERRQ(ierr);
183       ierr = PetscOptionsGetString(NULL,"-box_refresh_token",refreshtoken,512,&set);CHKERRQ(ierr);
184       if (!set) {
185         ierr = PetscBoxAuthorize(comm,access_token,refreshtoken,512*sizeof(char));CHKERRQ(ierr);
186         ierr = PetscFree(refreshtoken);CHKERRQ(ierr);
187         PetscFunctionReturn(0);
188       }
189     }
190     ierr = PetscSSLInitializeContext(&ctx);CHKERRQ(ierr);
191     ierr = PetscHTTPSConnect("www.box.com",443,ctx,&sock,&ssl);CHKERRQ(ierr);
192     ierr = PetscStrcpy(body,"client_id=");CHKERRQ(ierr);
193     ierr = PetscStrcat(body,PETSC_BOX_CLIENT_ID);CHKERRQ(ierr);
194     ierr = PetscStrcat(body,"&client_secret=");CHKERRQ(ierr);
195     ierr = PetscStrcat(body,PETSC_BOX_CLIENT_ST);CHKERRQ(ierr);
196     ierr = PetscStrcat(body,"&refresh_token=");CHKERRQ(ierr);
197     ierr = PetscStrcat(body,refreshtoken);CHKERRQ(ierr);
198     if (!refresh_token) {ierr = PetscFree(refreshtoken);CHKERRQ(ierr);}
199     ierr = PetscStrcat(body,"&grant_type=refresh_token");CHKERRQ(ierr);
200 
201     ierr = PetscHTTPSRequest("POST","https://www.box.com/api/oauth2/token",NULL,"application/x-www-form-urlencoded",body,ssl,buff,sizeof(buff));CHKERRQ(ierr);
202     ierr = PetscSSLDestroyContext(ctx);CHKERRQ(ierr);
203     close(sock);
204 
205     ierr   = PetscStrstr(buff,"\"access_token\" : \"",&access);CHKERRQ(ierr);
206     if (!access) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Did not receive access token from Box");
207     access += 18;
208     ierr   = PetscStrchr(access,'\"',&ctmp);CHKERRQ(ierr);
209     if (!ctmp) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Access token from Box is misformed");
210     *ctmp  = 0;
211     ierr   = PetscStrncpy(access_token,access,tokensize);CHKERRQ(ierr);
212     *ctmp  = '\"';
213   }
214   PetscFunctionReturn(0);
215 }
216 
217 #include <sys/stat.h>
218 
219 #undef __FUNCT__
220 #define __FUNCT__ "PetscBoxUpload"
221 /*@C
222      PetscBoxUpload - Loads a file to the Box Drive
223 
224      Not collective, only the first process in the MPI_Comm uploads the file
225 
226   Input Parameters:
227 +   comm - MPI communicator
228 .   access_token - obtained with PetscBoxRefresh(), pass NULL to have PETSc generate one
229 -   filename - file to upload; if you upload multiple times it will have different names each time on Box Drive
230 
231   Options Database:
232 .  -box_refresh_token   XXX
233 
234   Usage Patterns:
235     With PETSc option -box_refresh_token  XXX given
236     PetscBoxUpload(comm,NULL,filename);        will upload file with no user interaction
237 
238     Without PETSc option -box_refresh_token XXX given
239     PetscBoxUpload(comm,NULL,filename);        for first use will prompt user to authorize access to Box Drive with their processor
240 
241     With PETSc option -box_refresh_token  XXX given
242     PetscBoxRefresh(comm,NULL,access_token,sizeof(access_token));
243     PetscBoxUpload(comm,access_token,filename);
244 
245     With refresh token entered in some way by the user
246     PetscBoxRefresh(comm,refresh_token,access_token,sizeof(access_token));
247     PetscBoxUpload(comm,access_token,filename);
248 
249     PetscBoxAuthorize(comm,access_token,refresh_token,sizeof(access_token));
250     PetscBoxUpload(comm,access_token,filename);
251 
252 .seealso: PetscURLShorten(), PetscBoxAuthorize(), PetscBoxRefresh()
253 
254 @*/
255 PetscErrorCode PetscBoxUpload(MPI_Comm comm,const char access_token[],const char filename[])
256 {
257   SSL_CTX        *ctx;
258   SSL            *ssl;
259   int            sock;
260   PetscErrorCode ierr;
261   char           head[1024],buff[8*1024],*body,*title;
262   PetscMPIInt    rank;
263   struct stat    sb;
264   size_t         len,blen,rd;
265   FILE           *fd;
266 
267   PetscFunctionBegin;
268   ierr = MPI_Comm_rank(comm,&rank);CHKERRQ(ierr);
269   if (!rank) {
270     ierr = PetscStrcpy(head,"Authorization: Bearer ");CHKERRQ(ierr);
271     ierr = PetscStrcat(head,access_token);CHKERRQ(ierr);
272     ierr = PetscStrcat(head,"\r\n");CHKERRQ(ierr);
273     ierr = PetscStrcat(head,"uploadType: multipart\r\n");CHKERRQ(ierr);
274 
275     ierr = stat(filename,&sb);
276     if (ierr) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_FILE_OPEN,"Unable to stat file: %s",filename);
277     len = 1024 + sb.st_size;
278     ierr = PetscMalloc1(len,&body);CHKERRQ(ierr);
279     ierr = PetscStrcpy(body,"--foo_bar_baz\r\n"
280                          "Content-Type: application/json\r\n\r\n"
281                          "{"
282                          "\"title\": \"");
283     ierr = PetscStrcat(body,filename);
284     ierr = PetscStrcat(body,"\","
285                          "\"mimeType\": \"text.html\","
286                          "\"description\": \" a file\""
287                          "}\r\n\r\n"
288                          "--foo_bar_baz\r\n"
289                          "Content-Type: text/html\r\n\r\n");
290     ierr = PetscStrlen(body,&blen);CHKERRQ(ierr);
291     fd = fopen (filename, "r");
292     if (!fd) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_FILE_OPEN,"Unable to open file: %s",filename);
293     rd = fread (body+blen, sizeof (unsigned char), sb.st_size, fd);
294     if (rd != sb.st_size) SETERRQ3(PETSC_COMM_SELF,PETSC_ERR_FILE_OPEN,"Unable to read entire file: %s %d %d",filename,(int)rd,sb.st_size);
295     fclose(fd);
296     body[blen + rd] = 0;
297     ierr = PetscStrcat(body,"\r\n\r\n"
298                             "--foo_bar_baz\r\n");
299     ierr = PetscSSLInitializeContext(&ctx);CHKERRQ(ierr);
300     ierr = PetscHTTPSConnect("www.boxapis.com",443,ctx,&sock,&ssl);CHKERRQ(ierr);
301     ierr = PetscHTTPSRequest("POST","https://www.boxapis.com/upload/drive/v2/files/",head,"multipart/related; boundary=\"foo_bar_baz\"",body,ssl,buff,sizeof(buff));CHKERRQ(ierr);
302     ierr = PetscFree(body);CHKERRQ(ierr);
303     ierr = PetscSSLDestroyContext(ctx);CHKERRQ(ierr);
304     close(sock);
305     ierr   = PetscStrstr(buff,"\"title\"",&title);CHKERRQ(ierr);
306     if (!title) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_LIB,"Upload of file %s failed",filename);
307   }
308   PetscFunctionReturn(0);
309 }
310 
311 
312