xref: /petsc/src/sys/webclient/globus.c (revision f97672e55eacc8688507b9471cd7ec2664d7f203)
1 #include <petscwebclient.h>
2 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
3 #pragma gcc diagnostic ignored "-Wdeprecated-declarations"
4 
5 /*
6     Encodes and decodes from MIME Base64
7 */
8 static char encoding_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
9                                 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
10                                 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
11                                 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
12                                 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
13                                 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
14                                 'w', 'x', 'y', 'z', '0', '1', '2', '3',
15                                 '4', '5', '6', '7', '8', '9', '+', '/'};
16 
17 static PetscErrorCode base64_encode(const unsigned char *data,unsigned char *encoded_data,size_t len)
18 {
19   static size_t  mod_table[] = {0, 2, 1};
20   size_t         i,j;
21   size_t         input_length,output_length;
22 
23   PetscFunctionBegin;
24   PetscCall(PetscStrlen((const char*)data,&input_length));
25   output_length = 4 * ((input_length + 2) / 3);
26   PetscCheck(output_length <= len,PETSC_COMM_SELF,PETSC_ERR_ARG_WRONG,"Output length not large enough");
27 
28   for (i = 0, j = 0; i < input_length;) {
29      uint32_t octet_a = i < input_length ? (unsigned char)data[i++] : 0;
30      uint32_t octet_b = i < input_length ? (unsigned char)data[i++] : 0;
31      uint32_t octet_c = i < input_length ? (unsigned char)data[i++] : 0;
32      uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;
33 
34      encoded_data[j++] = encoding_table[(triple >> 3 * 6) & 0x3F];
35      encoded_data[j++] = encoding_table[(triple >> 2 * 6) & 0x3F];
36      encoded_data[j++] = encoding_table[(triple >> 1 * 6) & 0x3F];
37      encoded_data[j++] = encoding_table[(triple >> 0 * 6) & 0x3F];
38   }
39   encoded_data[j] = 0;
40   for (i = 0; i < mod_table[input_length % 3]; i++) encoded_data[output_length - 1 - i] = '=';
41   PetscFunctionReturn(0);
42 }
43 
44 PETSC_UNUSED static PetscErrorCode base64_decode(const unsigned char *data,unsigned char* decoded_data, size_t length)
45 {
46   static char    decoding_table[257];
47   static int     decode_table_built = 0;
48   size_t         i,j;
49   size_t         input_length,output_length;
50 
51   PetscFunctionBegin;
52   if (!decode_table_built) {
53     for (i = 0; i < 64; i++) decoding_table[(unsigned char) encoding_table[i]] = i;
54     decode_table_built = 1;
55   }
56 
57   PetscCall(PetscStrlen((const char*)data,&input_length));
58   PetscCheck(input_length % 4 == 0,PETSC_COMM_SELF,PETSC_ERR_ARG_WRONG,"Input length must be divisible by 4");
59 
60   output_length = input_length / 4 * 3;
61   if (data[input_length - 1] == '=') (output_length)--;
62   if (data[input_length - 2] == '=') (output_length)--;
63   PetscCheck(output_length <= length,PETSC_COMM_SELF,PETSC_ERR_ARG_WRONG,"Output length too shore");
64 
65   for (i = 0, j = 0; i < input_length;) {
66     uint32_t sextet_a = data[i] == '=' ? 0 & i++ : decoding_table[(int)data[i++]];
67     uint32_t sextet_b = data[i] == '=' ? 0 & i++ : decoding_table[(int)data[i++]];
68     uint32_t sextet_c = data[i] == '=' ? 0 & i++ : decoding_table[(int)data[i++]];
69     uint32_t sextet_d = data[i] == '=' ? 0 & i++ : decoding_table[(int)data[i++]];
70     uint32_t triple = (sextet_a << 3 * 6) + (sextet_b << 2 * 6) + (sextet_c << 1 * 6) + (sextet_d << 0 * 6);
71 
72     if (j < output_length) decoded_data[j++] = (triple >> 2 * 8) & 0xFF;
73     if (j < output_length) decoded_data[j++] = (triple >> 1 * 8) & 0xFF;
74     if (j < output_length) decoded_data[j++] = (triple >> 0 * 8) & 0xFF;
75   }
76   decoded_data[j] = 0;
77   PetscFunctionReturn(0);
78 }
79 
80 #if defined(PETSC_HAVE_UNISTD_H)
81 #include <unistd.h>
82 #endif
83 
84 /*@C
85      PetscGlobusAuthorize - Get an access token allowing PETSc applications to make Globus file transfer requests
86 
87    Not collective, only the first process in MPI_Comm does anything
88 
89    Input Parameters:
90 +  comm - the MPI communicator
91 -  tokensize - size of the token array
92 
93    Output Parameters:
94 .  access_token - can be used with PetscGlobusUpLoad() for 30 days
95 
96    Notes:
97     This call requires stdout and stdin access from process 0 on the MPI communicator
98 
99    You can run src/sys/webclient/tutorials/globusobtainaccesstoken to get an access token
100 
101    Level: intermediate
102 
103 .seealso: `PetscGoogleDriveRefresh()`, `PetscGoogleDriveUpload()`, `PetscURLShorten()`, `PetscGlobusUpload()`
104 
105 @*/
106 PetscErrorCode PetscGlobusAuthorize(MPI_Comm comm,char access_token[],size_t tokensize)
107 {
108   SSL_CTX        *ctx;
109   SSL            *ssl;
110   int            sock;
111   char           buff[8*1024],*ptr,head[1024];
112   PetscMPIInt    rank;
113   size_t         len;
114   PetscBool      found;
115 
116   PetscFunctionBegin;
117   PetscCallMPI(MPI_Comm_rank(comm,&rank));
118   if (rank == 0) {
119     PetscCheck(isatty(fileno(PETSC_STDOUT)),PETSC_COMM_SELF,PETSC_ERR_USER,"Requires users input/output");
120     PetscCall(PetscPrintf(comm,"Enter globus username:"));
121     ptr  = fgets(buff, 1024, stdin);
122     PetscCheck(ptr,PETSC_COMM_SELF, PETSC_ERR_FILE_READ, "Error reading from stdin: %d", errno);
123     PetscCall(PetscStrlen(buff,&len));
124     buff[len-1] = ':'; /* remove carriage return at end of line */
125 
126     PetscCall(PetscPrintf(comm,"Enter globus password:"));
127     ptr  = fgets(buff+len, 1024-len, stdin);
128     PetscCheck(ptr,PETSC_COMM_SELF, PETSC_ERR_FILE_READ, "Error reading from stdin: %d", errno);
129     PetscCall(PetscStrlen(buff,&len));
130     buff[len-1] = '\0'; /* remove carriage return at end of line */
131     PetscCall(PetscStrcpy(head,"Authorization: Basic "));
132     PetscCall(base64_encode((const unsigned char*)buff,(unsigned char*)(head+21),sizeof(head)-21));
133     PetscCall(PetscStrcat(head,"\r\n"));
134 
135     PetscCall(PetscSSLInitializeContext(&ctx));
136     PetscCall(PetscHTTPSConnect("nexus.api.globusonline.org",443,ctx,&sock,&ssl));
137     PetscCall(PetscHTTPSRequest("GET","nexus.api.globusonline.org/goauth/token?grant_type=client_credentials",head,"application/x-www-form-urlencoded",NULL,ssl,buff,sizeof(buff)));
138     PetscCall(PetscSSLDestroyContext(ctx));
139     close(sock);
140 
141     PetscCall(PetscPullJSONValue(buff,"access_token",access_token,tokensize,&found));
142     PetscCheck(found,PETSC_COMM_SELF,PETSC_ERR_LIB,"Globus did not return access token");
143 
144     PetscCall(PetscPrintf(comm,"Here is your Globus access token, save it in a save place, in the future you can run PETSc\n"));
145     PetscCall(PetscPrintf(comm,"programs with the option -globus_access_token %s\n",access_token));
146     PetscCall(PetscPrintf(comm,"to access Globus automatically\n"));
147   }
148   PetscFunctionReturn(0);
149 }
150 
151 /*@C
152      PetscGlobusGetTransfers - Get a record of current transfers requested from Globus
153 
154    Not collective, only the first process in MPI_Comm does anything
155 
156    Input Parameters:
157 +  comm - the MPI communicator
158 .  access_token - Globus access token, if NULL will check in options database for -globus_access_token XXX otherwise
159                   will call PetscGlobusAuthorize().
160 -  buffsize - size of the buffer
161 
162    Output Parameters:
163 .  buff - location to put Globus information
164 
165    Level: intermediate
166 
167 .seealso: `PetscGoogleDriveRefresh()`, `PetscGoogleDriveUpload()`, `PetscURLShorten()`, `PetscGlobusUpload()`, `PetscGlobusAuthorize()`
168 
169 @*/
170 PetscErrorCode PetscGlobusGetTransfers(MPI_Comm comm,const char access_token[],char buff[],size_t buffsize)
171 {
172   SSL_CTX        *ctx;
173   SSL            *ssl;
174   int            sock;
175   char           head[4096];
176   PetscMPIInt    rank;
177 
178   PetscFunctionBegin;
179   PetscCallMPI(MPI_Comm_rank(comm,&rank));
180   if (rank == 0) {
181     PetscCall(PetscStrcpy(head,"Authorization : Globus-Goauthtoken "));
182     if (access_token) {
183       PetscCall(PetscStrcat(head,access_token));
184     } else {
185       PetscBool set;
186       char      accesstoken[4096];
187       PetscCall(PetscOptionsGetString(NULL,NULL,"-globus_access_token",accesstoken,sizeof(accesstoken),&set));
188       PetscCheck(set,PETSC_COMM_SELF,PETSC_ERR_USER,"Pass in Globus accesstoken or use -globus_access_token XXX");
189       PetscCall(PetscStrcat(head,accesstoken));
190     }
191     PetscCall(PetscStrcat(head,"\r\n"));
192 
193     PetscCall(PetscSSLInitializeContext(&ctx));
194     PetscCall(PetscHTTPSConnect("transfer.api.globusonline.org",443,ctx,&sock,&ssl));
195     PetscCall(PetscHTTPSRequest("GET","transfer.api.globusonline.org/v0.10/tasksummary",head,"application/json",NULL,ssl,buff,buffsize));
196     PetscCall(PetscSSLDestroyContext(ctx));
197     close(sock);
198   }
199   PetscFunctionReturn(0);
200 }
201 
202 /*@C
203      PetscGlobusUpload - Loads a file to Globus
204 
205      Not collective, only the first process in the MPI_Comm uploads the file
206 
207   Input Parameters:
208 +   comm - MPI communicator
209 .   access_token - obtained with PetscGlobusAuthorize(), pass NULL to use -globus_access_token XXX from the PETSc database
210 -   filename - file to upload
211 
212   Options Database:
213 .  -globus_access_token XXX - the Globus token
214 
215    Level: intermediate
216 
217 .seealso: `PetscURLShorten()`, `PetscGoogleDriveAuthorize()`, `PetscGoogleDriveRefresh()`, `PetscGlobusAuthorize()`
218 
219 @*/
220 PetscErrorCode PetscGlobusUpload(MPI_Comm comm,const char access_token[],const char filename[])
221 {
222   SSL_CTX        *ctx;
223   SSL            *ssl;
224   int            sock;
225   char           head[4096],buff[8*1024],body[4096],submission_id[4096];
226   PetscMPIInt    rank;
227   PetscBool      flg,found;
228 
229   PetscFunctionBegin;
230   PetscCallMPI(MPI_Comm_rank(comm,&rank));
231   if (rank == 0) {
232     PetscCall(PetscTestFile(filename,'r',&flg));
233     PetscCheck(flg,PETSC_COMM_SELF,PETSC_ERR_FILE_OPEN,"Unable to find file: %s",filename);
234 
235     PetscCall(PetscStrcpy(head,"Authorization : Globus-Goauthtoken "));
236     if (access_token) {
237       PetscCall(PetscStrcat(head,access_token));
238     } else {
239       PetscBool set;
240       char      accesstoken[4096];
241       PetscCall(PetscOptionsGetString(NULL,NULL,"-globus_access_token",accesstoken,sizeof(accesstoken),&set));
242       PetscCheck(set,PETSC_COMM_SELF,PETSC_ERR_USER,"Pass in Globus accesstoken or use -globus_access_token XXX");
243       PetscCall(PetscStrcat(head,accesstoken));
244     }
245     PetscCall(PetscStrcat(head,"\r\n"));
246 
247     /* Get Globus submission id */
248     PetscCall(PetscSSLInitializeContext(&ctx));
249     PetscCall(PetscHTTPSConnect("transfer.api.globusonline.org",443,ctx,&sock,&ssl));
250     PetscCall(PetscHTTPSRequest("GET","transfer.api.globusonline.org/v0.10/submission_id",head,"application/json",NULL,ssl,buff,sizeof(buff)));
251     PetscCall(PetscSSLDestroyContext(ctx));
252     close(sock);
253     PetscCall(PetscPullJSONValue(buff,"value",submission_id,sizeof(submission_id),&found));
254     PetscCheck(found,PETSC_COMM_SELF,PETSC_ERR_LIB,"Globus did not return submission id");
255 
256     /* build JSON body of transfer request */
257     PetscCall(PetscStrcpy(body,"{"));
258     PetscCall(PetscPushJSONValue(body,"submission_id",submission_id,sizeof(body)));                 PetscCall(PetscStrcat(body,","));
259     PetscCall(PetscPushJSONValue(body,"DATA_TYPE","transfer",sizeof(body)));                        PetscCall(PetscStrcat(body,","));
260     PetscCall(PetscPushJSONValue(body,"sync_level","null",sizeof(body)));                           PetscCall(PetscStrcat(body,","));
261     PetscCall(PetscPushJSONValue(body,"source_endpoint","barryfsmith#MacBookPro",sizeof(body)));    PetscCall(PetscStrcat(body,","));
262     PetscCall(PetscPushJSONValue(body,"label","PETSc transfer label",sizeof(body)));                PetscCall(PetscStrcat(body,","));
263     PetscCall(PetscPushJSONValue(body,"length","1",sizeof(body)));                                  PetscCall(PetscStrcat(body,","));
264     PetscCall(PetscPushJSONValue(body,"destination_endpoint","mcs#home",sizeof(body)));             PetscCall(PetscStrcat(body,","));
265 
266     PetscCall(PetscStrcat(body,"\"DATA\": [ {"));
267     PetscCall(PetscPushJSONValue(body,"source_path","/~/FEM_GPU.pdf",sizeof(body)));                PetscCall(PetscStrcat(body,","));
268     PetscCall(PetscPushJSONValue(body,"destination_path","/~/FEM_GPU.pdf",sizeof(body)));           PetscCall(PetscStrcat(body,","));
269     PetscCall(PetscPushJSONValue(body,"verify_size","null",sizeof(body)));                          PetscCall(PetscStrcat(body,","));
270     PetscCall(PetscPushJSONValue(body,"recursive","false",sizeof(body)));                           PetscCall(PetscStrcat(body,","));
271     PetscCall(PetscPushJSONValue(body,"DATA_TYPE","transfer_item",sizeof(body)));
272     PetscCall(PetscStrcat(body,"} ] }"));
273 
274     PetscCall(PetscSSLInitializeContext(&ctx));
275     PetscCall(PetscHTTPSConnect("transfer.api.globusonline.org",443,ctx,&sock,&ssl));
276     PetscCall(PetscHTTPSRequest("POST","transfer.api.globusonline.org/v0.10/transfer",head,"application/json",body,ssl,buff,sizeof(buff)));
277     PetscCall(PetscSSLDestroyContext(ctx));
278     close(sock);
279     PetscCall(PetscPullJSONValue(buff,"code",submission_id,sizeof(submission_id),&found));
280     PetscCheck(found,PETSC_COMM_SELF,PETSC_ERR_LIB,"Globus did not return code on transfer");
281     PetscCall(PetscStrcmp(submission_id,"Accepted",&found));
282     PetscCheck(found,PETSC_COMM_SELF,PETSC_ERR_LIB,"Globus did not accept transfer");
283   }
284   PetscFunctionReturn(0);
285 }
286