xref: /petsc/src/sys/webclient/box.c (revision 4a285bdac9f88c57367533e531b7abef8f597875)
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 #include <ctype.h>
55 
56 char to_hex(char code) {
57   static char hex[] = "0123456789abcdef";
58   return hex[code & 15];
59 }
60 
61 /* Returns a url-encoded version of str */
62 char *url_encode(char *str) {
63   char *pstr = str, *buf = malloc(strlen(str) * 3 + 1), *pbuf = buf;
64   while (*pstr) {
65     if (isalnum(*pstr) || *pstr == '-' || *pstr == '_' || *pstr == '.' || *pstr == '~')
66       *pbuf++ = *pstr;
67     else if (*pstr == ' ')
68       *pbuf++ = '+';
69     else
70       *pbuf++ = '%', *pbuf++ = to_hex(*pstr >> 4), *pbuf++ = to_hex(*pstr & 15);
71     pstr++;
72   }
73   *pbuf = '\0';
74   return buf;
75 }
76 
77 #undef __FUNCT__
78 #define __FUNCT__ "PetscBoxAuthorize"
79 /*@C
80      PetscBoxAuthorize - Get authorization and refresh token for accessing Box drive from PETSc
81 
82    Not collective, only the first process in MPI_Comm does anything
83 
84    Input Parameters:
85 +  comm - the MPI communicator
86 -  tokensize - size of the token arrays
87 
88    Output Parameters:
89 +  access_token - can be used with PetscBoxUpload() for this one session
90 -  refresh_token - can be used for ever to obtain new access_tokens with PetscBoxRefresh(), guard this like a password
91                    it gives access to your Box Drive
92 
93    Notes: This call requires stdout and stdin access from process 0 on the MPI communicator
94 
95    You can run src/sys/webclient/examples/tutorials/obtainrefreshtoken to get a refresh token and then in the future pass it to
96    PETSc programs with -box_refresh_token XXX
97 
98    Developer Notes: For some reason I cannot get this to work! Box always replies with Bad Request and no details. Using Curl works
99    if one is fast enough.  Perhaps the problem is the need to urlencode the message?
100 
101 .seealso: PetscBoxRefresh(), PetscBoxUpload(), PetscURLShorten()
102 
103 @*/
104 PetscErrorCode PetscBoxAuthorize(MPI_Comm comm,char access_token[],char refresh_token[],size_t tokensize)
105 {
106   SSL_CTX        *ctx;
107   SSL            *ssl;
108   int            sock;
109   PetscErrorCode ierr;
110   char           buff[8*1024],body[1024],*access,*refresh,*ctmp;
111   PetscMPIInt    rank;
112   PetscBool      flg;
113 
114   PetscFunctionBegin;
115   ierr = MPI_Comm_rank(comm,&rank);CHKERRQ(ierr);
116   if (!rank) {
117     ierr = PetscPrintf(comm,"Cut and paste the following into your browser:\n\n"
118                             "https://www.box.com/api/oauth2/authorize?"
119                             "response_type=code&"
120                             "client_id="
121                             PETSC_BOX_CLIENT_ID
122                             "&state=PETScState"
123                             "\n\n");CHKERRQ(ierr);
124     ierr = PetscBoxStartWebServer_Private();CHKERRQ(ierr);
125     ierr = PetscStrbeginswith((const char*)result,"state=PETScState&code=",&flg);CHKERRQ(ierr);
126     if (!flg) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_LIB,"Did not get expected string from Box got %s",result);
127     ierr = PetscStrncpy(buff,(const char*)result+22,sizeof(buff));CHKERRQ(ierr);
128 
129     ierr = PetscSSLInitializeContext(&ctx);CHKERRQ(ierr);
130     ierr = PetscHTTPSConnect("www.box.com",443,ctx,&sock,&ssl);CHKERRQ(ierr);
131     ierr = PetscStrcpy(body,"code=");CHKERRQ(ierr);
132     ierr = PetscStrcat(body,buff);CHKERRQ(ierr);
133     ierr = PetscStrcat(body,"&client_id=");CHKERRQ(ierr);
134     ierr = PetscStrcat(body,PETSC_BOX_CLIENT_ID);CHKERRQ(ierr);
135     ierr = PetscStrcat(body,"&client_secret=");CHKERRQ(ierr);
136     ierr = PetscStrcat(body,PETSC_BOX_CLIENT_ST);CHKERRQ(ierr);
137     ierr = PetscStrcat(body,"&grant_type=authorization_code");CHKERRQ(ierr);
138 
139     ierr = PetscHTTPSRequest("POST","https://www.box.com/api/oauth2/token",NULL,"application/x-www-form-urlencoded",body,ssl,buff,sizeof(buff));CHKERRQ(ierr);
140     ierr = PetscSSLDestroyContext(ctx);CHKERRQ(ierr);
141     close(sock);
142 
143     ierr   = PetscStrstr(buff,"\"access_token\" : \"",&access);CHKERRQ(ierr);
144     if (!access) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Did not receive access token from Box");
145     access += 18;
146     ierr   = PetscStrchr(access,'\"',&ctmp);CHKERRQ(ierr);
147     if (!ctmp) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Access token from Box is misformed");
148     *ctmp  = 0;
149     ierr   = PetscStrncpy(access_token,access,tokensize);CHKERRQ(ierr);
150     *ctmp  = '\"';
151 
152     ierr   = PetscStrstr(buff,"\"refresh_token\" : \"",&refresh);CHKERRQ(ierr);
153     if (!refresh) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Did not receive refresh token from Box");
154     refresh += 19;
155     ierr   = PetscStrchr(refresh,'\"',&ctmp);CHKERRQ(ierr);
156     if (!ctmp) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Refresh token from Box is misformed");
157     *ctmp  = 0;
158     ierr = PetscStrncpy(refresh_token,refresh,tokensize);CHKERRQ(ierr);
159 
160     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);
161     ierr = PetscPrintf(comm,"programs with the option -box_refresh_token %d\n",refresh);CHKERRQ(ierr);
162     ierr = PetscPrintf(comm,"to access Box Drive automatically\n");CHKERRQ(ierr);
163   }
164   PetscFunctionReturn(0);
165 }
166 
167 #undef __FUNCT__
168 #define __FUNCT__ "PetscBoxRefresh"
169 /*@C
170      PetscBoxRefresh - Get a new authorization token for accessing Box drive from PETSc from a refresh token
171 
172    Not collective, only the first process in the MPI_Comm does anything
173 
174    Input Parameters:
175 +   comm - MPI communicator
176 .   refresh token - obtained with PetscBoxAuthorize(), if NULL PETSc will first look for one in the options data
177                     if not found it will call PetscBoxAuthorize()
178 -   tokensize - size of the output string access_token
179 
180    Output Parameter:
181 +   access_token - token that can be passed to PetscBoxUpload()
182 -   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
183 
184    Note: This doesn't work I cannot figure out why.
185 
186 .seealso: PetscURLShorten(), PetscBoxAuthorize(), PetscBoxUpload()
187 
188 @*/
189 PetscErrorCode PetscBoxRefresh(MPI_Comm comm,const char refresh_token[],char access_token[],char new_refresh_token[],size_t tokensize)
190 {
191   SSL_CTX        *ctx;
192   SSL            *ssl;
193   int            sock;
194   PetscErrorCode ierr;
195   char           buff[8*1024],body[1024],*access,*ctmp;
196   PetscMPIInt    rank;
197   char           *refreshtoken = (char*)refresh_token;
198 
199   PetscFunctionBegin;
200   ierr = MPI_Comm_rank(comm,&rank);CHKERRQ(ierr);
201   if (!rank) {
202     if (!refresh_token) {
203       PetscBool set;
204       ierr = PetscMalloc1(512,&refreshtoken);CHKERRQ(ierr);
205       ierr = PetscOptionsGetString(NULL,"-box_refresh_token",refreshtoken,512,&set);CHKERRQ(ierr);
206       if (!set) {
207         ierr = PetscBoxAuthorize(comm,access_token,refreshtoken,512*sizeof(char));CHKERRQ(ierr);
208         ierr = PetscFree(refreshtoken);CHKERRQ(ierr);
209         PetscFunctionReturn(0);
210       }
211     }
212     ierr = PetscSSLInitializeContext(&ctx);CHKERRQ(ierr);
213     ierr = PetscHTTPSConnect("www.box.com",443,ctx,&sock,&ssl);CHKERRQ(ierr);
214     ierr = PetscStrcpy(body,"client_id=");CHKERRQ(ierr);
215     ierr = PetscStrcat(body,PETSC_BOX_CLIENT_ID);CHKERRQ(ierr);
216     ierr = PetscStrcat(body,"&client_secret=");CHKERRQ(ierr);
217     ierr = PetscStrcat(body,PETSC_BOX_CLIENT_ST);CHKERRQ(ierr);
218     ierr = PetscStrcat(body,"&refresh_token=");CHKERRQ(ierr);
219     ierr = PetscStrcat(body,refreshtoken);CHKERRQ(ierr);
220     if (!refresh_token) {ierr = PetscFree(refreshtoken);CHKERRQ(ierr);}
221     ierr = PetscStrcat(body,"&grant_type=refresh_token");CHKERRQ(ierr);
222 
223     ierr = PetscHTTPSRequest("POST","https://www.box.com/api/oauth2/token",NULL,"application/x-www-form-urlencoded",body,ssl,buff,sizeof(buff));CHKERRQ(ierr);
224     ierr = PetscSSLDestroyContext(ctx);CHKERRQ(ierr);
225     close(sock);
226 
227     ierr   = PetscStrstr(buff,"\"access_token\" : \"",&access);CHKERRQ(ierr);
228     if (!access) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Did not receive access token from Box");
229     access += 18;
230     ierr   = PetscStrchr(access,'\"',&ctmp);CHKERRQ(ierr);
231     if (!ctmp) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Access token from Box is misformed");
232     *ctmp  = 0;
233     ierr   = PetscStrncpy(access_token,access,tokensize);CHKERRQ(ierr);
234     *ctmp  = '\"';
235   }
236   PetscFunctionReturn(0);
237 }
238 
239 #include <sys/stat.h>
240 
241 #undef __FUNCT__
242 #define __FUNCT__ "PetscBoxUpload"
243 /*@C
244      PetscBoxUpload - Loads a file to the Box Drive
245 
246      Not collective, only the first process in the MPI_Comm uploads the file
247 
248   Input Parameters:
249 +   comm - MPI communicator
250 .   access_token - obtained with PetscBoxRefresh(), pass NULL to have PETSc generate one
251 -   filename - file to upload; if you upload multiple times it will have different names each time on Box Drive
252 
253   Options Database:
254 .  -box_refresh_token   XXX
255 
256   Usage Patterns:
257     With PETSc option -box_refresh_token  XXX given
258     PetscBoxUpload(comm,NULL,filename);        will upload file with no user interaction
259 
260     Without PETSc option -box_refresh_token XXX given
261     PetscBoxUpload(comm,NULL,filename);        for first use will prompt user to authorize access to Box Drive with their processor
262 
263     With PETSc option -box_refresh_token  XXX given
264     PetscBoxRefresh(comm,NULL,access_token,sizeof(access_token));
265     PetscBoxUpload(comm,access_token,filename);
266 
267     With refresh token entered in some way by the user
268     PetscBoxRefresh(comm,refresh_token,access_token,sizeof(access_token));
269     PetscBoxUpload(comm,access_token,filename);
270 
271     PetscBoxAuthorize(comm,access_token,refresh_token,sizeof(access_token));
272     PetscBoxUpload(comm,access_token,filename);
273 
274 .seealso: PetscURLShorten(), PetscBoxAuthorize(), PetscBoxRefresh()
275 
276 @*/
277 PetscErrorCode PetscBoxUpload(MPI_Comm comm,const char access_token[],const char filename[])
278 {
279   SSL_CTX        *ctx;
280   SSL            *ssl;
281   int            sock;
282   PetscErrorCode ierr;
283   char           head[1024],buff[8*1024],*body,*title;
284   PetscMPIInt    rank;
285   struct stat    sb;
286   size_t         len,blen,rd;
287   FILE           *fd;
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     ierr = stat(filename,&sb);
298     if (ierr) 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                          "{"
304                          "\"title\": \"");
305     ierr = PetscStrcat(body,filename);
306     ierr = PetscStrcat(body,"\","
307                          "\"mimeType\": \"text.html\","
308                          "\"description\": \" a file\""
309                          "}\r\n\r\n"
310                          "--foo_bar_baz\r\n"
311                          "Content-Type: text/html\r\n\r\n");
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 != 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);
317     fclose(fd);
318     body[blen + rd] = 0;
319     ierr = PetscStrcat(body,"\r\n\r\n"
320                             "--foo_bar_baz\r\n");
321     ierr = PetscSSLInitializeContext(&ctx);CHKERRQ(ierr);
322     ierr = PetscHTTPSConnect("www.boxapis.com",443,ctx,&sock,&ssl);CHKERRQ(ierr);
323     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);
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 
333 
334