summaryrefslogtreecommitdiff
path: root/imap/src/c-client
diff options
context:
space:
mode:
authorEduardo Chappa <chappa@washington.edu>2021-07-30 09:00:04 -0600
committerEduardo Chappa <chappa@washington.edu>2021-07-30 09:00:04 -0600
commitbba1f63e9be0b65c090d1707a6c9168443604ed6 (patch)
tree7dc6534d6693936d1249f81e790f264750d5e248 /imap/src/c-client
parentc39814e31d89bd14111347aadca9f0e225b8f06d (diff)
downloadalpine-bba1f63e9be0b65c090d1707a6c9168443604ed6.tar.xz
* Improvements to the http and json code.
Diffstat (limited to 'imap/src/c-client')
-rw-r--r--imap/src/c-client/http.c106
-rw-r--r--imap/src/c-client/http.h20
-rw-r--r--imap/src/c-client/json.c46
-rw-r--r--imap/src/c-client/json.h18
-rw-r--r--imap/src/c-client/oauth2_aux.c72
5 files changed, 114 insertions, 148 deletions
diff --git a/imap/src/c-client/http.c b/imap/src/c-client/http.c
index 0162502d..e158db37 100644
--- a/imap/src/c-client/http.c
+++ b/imap/src/c-client/http.c
@@ -21,15 +21,6 @@ unsigned long http_debug;
static char http_notok[] = "\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37\40\42\50\51\54\57\72\73\74\75\76\77\100\133\134\135\173\175\177\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377";
static char http_noparam_val[] = "\1\2\3\4\5\6\7\10\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37\42\134\177";
-#define HTTPTCPPORT (long) 80 /* assigned TCP contact port */
-#define HTTPSSLPORT (long) 443 /* assigned SSL TCP contact port */
-
-typedef struct http_request_s {
- unsigned char *request;
- unsigned char *header;
- unsigned char *body;
-} HTTP_REQUEST_S;
-
#define HTP_NOVAL 0x001 /* the header accepts parameters without value */
#define HTP_UNLIMITED (-1) /* parse and infinite list */
@@ -96,20 +87,12 @@ typedef struct http_header_data_s {
/* helper functions */
HTTP_STATUS_S *http_status_line_get(unsigned char *);
void http_status_line_free(HTTP_STATUS_S **);
-HTTP_REQUEST_S *http_request_get(void);
-void http_request_free(HTTP_REQUEST_S **);
-unsigned char *http_request_line(unsigned char *, unsigned char *, unsigned char *);
-void http_add_header(HTTP_REQUEST_S **, unsigned char *, unsigned char *);
void http_add_body(HTTP_REQUEST_S **, unsigned char *);
void buffer_add(unsigned char **, unsigned char *);
unsigned char *hex_escape_url_part(unsigned char *, unsigned char *);
unsigned char *encode_url_body_part(unsigned char *, unsigned char *);
-unsigned char *http_response_from_reply(HTTPSTREAM *);
/* HTTP function prototypes */
-int http_valid_net_parse (unsigned char *, NETMBX *);
-
-long http_send (HTTPSTREAM *, HTTP_REQUEST_S *);
long http_reply (HTTPSTREAM *);
long http_fake (HTTPSTREAM *, unsigned char *);
@@ -147,28 +130,15 @@ http_parameters (long function,void *value)
unsigned char *
http_response_from_reply(HTTPSTREAM *stream)
{
- unsigned char *rv = NULL, *s, *t;
+ unsigned char *rv = NULL, *s;
if(stream == NULL || stream->reply == NULL || stream->header == NULL)
return rv;
- if(stream->header->content_length){
- s = strstr(stream->reply, "\r\n\r\n");
- if(s != NULL) rv = s + 4;
- }
- else if (stream->header->transfer_encoding){
- HTTP_PARAM_LIST_S *p = stream->header->transfer_encoding->p;
- for(; p ; p = p->next){
- if(!compare_cstring(p->vp->value, "chunked"))
- break;
- }
- if(p && p->vp->value){ /* chunked transfer */
- if((s = strstr(stream->reply, "\r\n\r\n")) != NULL
- && (t = strstr(s + 4, "\r\n")) != NULL)
- rv = t + 2;
- }
- }
- return rv;
+ s = strstr(stream->reply, "\r\n\r\n");
+ if(s != NULL) rv = s + 4;
+
+ return s ? rv : NIL;
}
void
@@ -996,54 +966,19 @@ http_post_param(HTTPSTREAM *stream, HTTP_PARAM_S *param)
}
unsigned char *
-http_post_param2(HTTPSTREAM *stream, HTTP_PARAM_S *param)
+http_get(HTTPSTREAM *stream, HTTP_PARAM_S **h)
{
- HTTP_PARAM_S enc_param;
- HTTP_REQUEST_S *http_request = NULL;
- unsigned char *response = NULL;
- int i;
-
- if(stream == NULL || param == NULL) return response;
-
- http_request = http_request_get();
- http_request->request = http_request_line("POST", stream->urltail, HTTP_1_1_VERSION);
- http_add_header(&http_request, "Host", stream->urlhost);
- http_add_header(&http_request, "User-Agent", "Alpine");
- http_add_header(&http_request, "Content-Type", HTTP_MIME_URLENCODED);
-
- for(i = 0; param[i].name != NULL; i++){
- enc_param.name = encode_url_body_part(param[i].name, NULL);
- enc_param.value = encode_url_body_part(param[i].value, NULL);
- if(i > 0)
- http_add_body(&http_request, "&");
- http_add_body(&http_request, enc_param.name);
- http_add_body(&http_request, "=");
- http_add_body(&http_request, enc_param.value);
- fs_give((void **) &enc_param.name);
- fs_give((void **) &enc_param.value);
- }
-
- if(http_send(stream, http_request)){
- unsigned char *s = http_response_from_reply(stream);
- response = cpystr(s ? (char *) s : "");
- }
-
- http_request_free(&http_request);
-
- return response;
-}
-
-unsigned char *
-http_get(HTTPSTREAM *stream)
-{
HTTP_REQUEST_S *http_request;
unsigned char *response = NIL;
+ int i;
if(!stream) return response;
http_request = http_request_get();
http_request->request = http_request_line("GET", stream->urltail, HTTP_1_1_VERSION);
http_add_header(&http_request, "Host", stream->urlhost);
+ for(i = 0; h && h[i]->name && h[i]->value; i++)
+ http_add_header(&http_request, h[i]->name, h[i]->value);
if(http_send(stream, http_request)){
unsigned char *s = http_response_from_reply(stream);
@@ -1146,7 +1081,7 @@ http_reply (HTTPSTREAM *stream)
if (stream->response) fs_give ((void **) &stream->response);
stream->response = (unsigned char *) net_getline(stream->netstream);
- if(stream->debug) mm_log(stream->response, HTTPDEBUG);
+ if(stream->debug) mm_log(stream->response ? stream->response : (unsigned char *) "<NIL RESPONSE>", HTTPDEBUG);
if(stream->response){
buffer_add(&stream->reply, stream->response);
@@ -1192,22 +1127,21 @@ http_reply (HTTPSTREAM *stream)
break;
}
if(p && p->vp->value){ /* chunked transfer */
- int done = 0;
- size = 0L;
- while(!done){
- if (stream->response) fs_give ((void **) &stream->response);
- stream->response = (unsigned char *) net_getline (stream->netstream);
- if(stream->response){
- buffer_add(&stream->reply, stream->response);
- buffer_add(&stream->reply, "\015\012");
- size = strtol((unsigned char *) stream->response, NIL, 16);
+ unsigned char *s = NIL;
+ do {
+ if (s) fs_give ((void **) &s);
+ size = 0L;
+ if((s = (unsigned char *) net_getline (stream->netstream)) != NIL){
+// buffer_add(&stream->reply, s);
+// buffer_add(&stream->reply, "\015\012");
+ if(stream->debug) mm_log(s, HTTPDEBUG);
+ size = strtol(s, NIL, 16);
fs_give ((void **) &stream->response);
stream->response = (unsigned char *) net_getsize (stream->netstream, size);
buffer_add(&stream->reply, stream->response);
if(stream->debug) mm_log(stream->response, HTTPDEBUG);
}
- if(size == 0L) done++;
- }
+ } while (stream && stream->netstream && s && (size > 0 || !*s ));
}
}
diff --git a/imap/src/c-client/http.h b/imap/src/c-client/http.h
index 1ddc988b..4e1f55b1 100644
--- a/imap/src/c-client/http.h
+++ b/imap/src/c-client/http.h
@@ -11,6 +11,9 @@
*
*/
+#define HTTPTCPPORT (long) 80 /* assigned TCP contact port */
+#define HTTPSSLPORT (long) 443 /* assigned SSL TCP contact port */
+
typedef struct http_val_param_s {
char *value;
PARAMETER *plist;
@@ -105,11 +108,24 @@ typedef struct http_param_s {
char *value;
} HTTP_PARAM_S;
+typedef struct http_request_s {
+ unsigned char *request;
+ unsigned char *header;
+ unsigned char *body;
+} HTTP_REQUEST_S;
+
/* exported prototypes */
+HTTP_REQUEST_S *http_request_get(void);
+void http_request_free(HTTP_REQUEST_S **);
+unsigned char *http_request_line(unsigned char *, unsigned char *, unsigned char *);
+void http_add_header(HTTP_REQUEST_S **, unsigned char *, unsigned char *);
+unsigned char *http_response_from_reply(HTTPSTREAM *);
+
+int http_valid_net_parse (unsigned char *, NETMBX *);
HTTPSTREAM *http_open (unsigned char *);
+long http_send (HTTPSTREAM *, HTTP_REQUEST_S *);
unsigned char *http_post_param(HTTPSTREAM *, HTTP_PARAM_S *);
-unsigned char *http_post_param2(HTTPSTREAM *, HTTP_PARAM_S *);
-unsigned char *http_get(HTTPSTREAM *);
+unsigned char *http_get(HTTPSTREAM *, HTTP_PARAM_S **);
void http_close (HTTPSTREAM *stream);
HTTP_PARAM_S *http_param_get(int);
diff --git a/imap/src/c-client/json.c b/imap/src/c-client/json.c
index d19e99d6..245cb48c 100644
--- a/imap/src/c-client/json.c
+++ b/imap/src/c-client/json.c
@@ -216,7 +216,7 @@ json_value_parse(unsigned char **s)
w = *s;
json_skipws(w);
jx = fs_get(sizeof(JSON_X));
- memset((void **) jx, 0, sizeof(JSON_X));
+ memset((void *) jx, 0, sizeof(JSON_X));
jx->jtype = JEnd;
switch(*w){
case '\"': jx->jtype = JString; break;
@@ -261,7 +261,7 @@ json_value_parse(unsigned char **s)
}
break;
- case JObject: jx->value = (void *) json_parse(&w);
+ case JObject: jx->value = (void *) json_parse_work(&w);
break;
case JLong : l = fs_get(sizeof(unsigned long));
@@ -322,12 +322,13 @@ JSON_S *
json_array_parse_work(unsigned char **s)
{
unsigned char *w = *s;
- JSON_S *j;
+ JSON_S *j = NIL;
json_skipws(w);
j = fs_get(sizeof(JSON_S));
memset((void *) j, 0, sizeof(JSON_S));
- j->value = json_value_parse(&w);
+ if(*w != ']')
+ j->value = json_value_parse(&w);
json_skipws(w);
switch(*w){
case ',' : json_skipchar(w);
@@ -421,7 +422,13 @@ json_free(JSON_S **jp)
}
JSON_S *
-json_parse(unsigned char **s)
+json_parse(unsigned char *s)
+{
+ return json_parse_work(&s);
+}
+
+JSON_S *
+json_parse_work(unsigned char **s)
{
JSON_S *j = NULL;
JSON_X *jx;
@@ -447,3 +454,32 @@ json_parse(unsigned char **s)
*s = w;
return j;
}
+
+void
+json_assign(void **v, JSON_S *j, char *s, JObjType t)
+{
+ JSON_X *jx = json_body_value(j, s);
+ long l;
+ unsigned long ul;
+ int i;
+
+ if(jx && jx->jtype == t && jx->value){
+ switch(t){ /* override here */
+ case JString : *v = (void *) cpystr((char *) jx->value); break;
+ default : break;
+ }
+ }
+}
+
+JSON_S *
+json_by_name_and_type(JSON_S *json, char *name, JObjType jtype)
+{
+ JSON_S *j;
+
+ for(j = json; j ; j = j->next)
+ if(j->name && !compare_cstring(j->name, name))
+ break;
+
+ return j && j->value && j->value->jtype == jtype
+ ? (JSON_S *) (j->value->value) : NIL;
+}
diff --git a/imap/src/c-client/json.h b/imap/src/c-client/json.h
index 88585170..75a08c38 100644
--- a/imap/src/c-client/json.h
+++ b/imap/src/c-client/json.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 Eduardo Chappa
+ * Copyright 2018-2021 Eduardo Chappa
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,7 +25,21 @@ typedef struct json_s {
struct json_s *next;
} JSON_S;
-JSON_S *json_parse(unsigned char **);
+#define json_value_type(J, I, T) \
+ (((jx = json_body_value((J), (I))) != NIL) \
+ && jx->jtype == (T) && jx->value) \
+ ? ((T) == JLong \
+ ? *(long *) jx->value \
+ : ((T) == JBoolean \
+ ? (compare_cstring("false", (char *) jx->value) ? 1 : 0)\
+ : NIL \
+ ) \
+ ) \
+ : NIL
+
+void json_assign(void **, JSON_S *, char *, JObjType);
+JSON_S *json_by_name_and_type(JSON_S *, char *, JObjType);
+JSON_S *json_parse(unsigned char *);
JSON_X *json_body_value(JSON_S *, unsigned char *);
void json_free(JSON_S **);
diff --git a/imap/src/c-client/oauth2_aux.c b/imap/src/c-client/oauth2_aux.c
index 550f3b56..9d9fa304 100644
--- a/imap/src/c-client/oauth2_aux.c
+++ b/imap/src/c-client/oauth2_aux.c
@@ -114,11 +114,10 @@ JSON_S *oauth2_json_reply(OAUTH2_SERVER_METHOD_S RefreshMethod, OAUTH2_S *oauth2
if(strcmp(RefreshMethod.name, "POST") == 0
&& ((stream = http_open(server)) != NULL)
&& ((s = http_post_param(stream, params)) != NULL)){
- unsigned char *u = s;
- json = json_parse(&u);
+ json = json_parse(s);
fs_give((void **) &s);
}
- *status = stream->status ? stream->status->code : -1;
+ *status = stream && stream->status ? stream->status->code : -1;
if(stream) http_close(stream);
if(server)
fs_give((void **) &server);
@@ -164,18 +163,9 @@ mm_login_oauth2_c_client_method (NETMBX *mb, char *user, char *method,
if(json != NULL){
JSON_X *jx;
- jx = json_body_value(json, "device_code");
- if(jx && jx->jtype == JString)
- oauth2->devicecode.device_code = cpystr((char *) jx->value);
-
- jx = json_body_value(json, "user_code");
- if(jx && jx->jtype == JString)
- oauth2->devicecode.user_code = cpystr((char *) jx->value);
-
- jx = json_body_value(json, "verification_uri");
- if(jx && jx->jtype == JString)
- oauth2->devicecode.verification_uri = cpystr((char *) jx->value);
-
+ json_assign ((void **) &oauth2->devicecode.device_code, json, "device_code", JString);
+ json_assign ((void **) &oauth2->devicecode.user_code, json, "user_code", JString);
+ json_assign ((void **) &oauth2->devicecode.verification_uri, json, "verification_uri", JString);
if((jx = json_body_value(json, "expires_in")) != NULL)
switch(jx->jtype){
case JString: oauth2->devicecode.expires_in = atoi((char *) jx->value);
@@ -194,10 +184,7 @@ mm_login_oauth2_c_client_method (NETMBX *mb, char *user, char *method,
default : break;
}
- jx = json_body_value(json, "message");
- if(jx && jx->jtype == JString)
- oauth2->devicecode.message = cpystr((char *) jx->value);
-
+ json_assign ((void **) &oauth2->devicecode.message, json, "message", JString);
json_free(&json);
if(oauth2->devicecode.verification_uri && oauth2->devicecode.user_code){
@@ -220,10 +207,7 @@ mm_login_oauth2_c_client_method (NETMBX *mb, char *user, char *method,
case HTTP_UNAUTHORIZED:
mm_log("Client not authorized (wrong client-id?)", ERROR);
break;
- case HTTP_OK: jx = json_body_value(json, "access_token");
- if(jx && jx->jtype == JString)
- oauth2->access_token = cpystr((char *) jx->value);
-
+ case HTTP_OK: json_assign ((void **) &oauth2->access_token, json, "access_token", JString);
if((jx = json_body_value(json, "expires_in")) != NULL)
switch(jx->jtype){
case JString: oauth2->expiration = time(0) + atol((char *) jx->value);
@@ -282,30 +266,21 @@ mm_login_oauth2_c_client_method (NETMBX *mb, char *user, char *method,
JSON_X *jx;
switch(status){
- case HTTP_OK : jx = json_body_value(json, "refresh_token");
- if(jx && jx->jtype == JString)
- oauth2->param[OA2_RefreshToken].value = cpystr((char *) jx->value);
-
- jx = json_body_value(json, "access_token");
- if(jx && jx->jtype == JString)
- oauth2->access_token = cpystr((char *) jx->value);
+ case HTTP_OK : json_assign ((void **) &oauth2->param[OA2_RefreshToken].value, json, "refresh_token", JString);
+ json_assign ((void **) &oauth2->access_token, json, "access_token", JString);
- if((jx = json_body_value(json, "expires_in")) != NULL)
- switch(jx->jtype){
+ if((jx = json_body_value(json, "expires_in")) != NULL)
+ switch(jx->jtype){
case JString: oauth2->expiration = time(0) + atol((char *) jx->value);
break;
case JLong : oauth2->expiration = time(0) + *(long *) jx->value;
break;
default : break;
- }
-
- jx = json_body_value(json, "expires_in");
- if(jx && jx->jtype == JString)
- oauth2->expiration = time(0) + atol((char *) jx->value);
+ }
- oauth2->cancel_refresh_token = 0; /* do not cancel this token. It is good */
+ oauth2->cancel_refresh_token = 0; /* do not cancel this token. It is good */
- break;
+ break;
case HTTP_BAD : break;
@@ -346,15 +321,11 @@ void oauth2deviceinfo_get_accesscode(void *inp, void *outp)
if(json != NULL){
JSON_X *jx;
- char *error;
+ char *error = NIL;
switch(status){
- case HTTP_BAD : jx = json_body_value(json, "error");
- if(jx && jx->jtype == JString)
- error = cpystr((char *) jx->value);
- else
- break;
-
+ case HTTP_BAD : json_assign ((void **) &error, json, "error", JString);
+ if(!error) break;
if(compare_cstring(error, "authorization_pending") == 0)
rv = OA2_CODE_WAIT;
else if(compare_cstring(error, "authorization_declined") == 0)
@@ -368,13 +339,8 @@ void oauth2deviceinfo_get_accesscode(void *inp, void *outp)
break;
- case HTTP_OK : jx = json_body_value(json, "refresh_token");
- if(jx && jx->jtype == JString)
- oauth2->param[OA2_RefreshToken].value = cpystr((char *) jx->value);
-
- jx = json_body_value(json, "access_token");
- if(jx && jx->jtype == JString)
- oauth2->access_token = cpystr((char *) jx->value);
+ case HTTP_OK : json_assign ((void **) &oauth2->param[OA2_RefreshToken].value, json, "refresh_token", JString);
+ json_assign ((void **) &oauth2->access_token, json, "access_token", JString);
if((jx = json_body_value(json, "expires_in")) != NULL)
switch(jx->jtype){