Axeda Corp
/
AxedaGo-ubloxC027
Axeda demo software for u-blox C027 (GSM)
AMMP/axHTTP.c@1:ff6d8adaf6b9, 2014-08-11 (annotated)
- Committer:
- AxedaCorp
- Date:
- Mon Aug 11 19:07:20 2014 +0000
- Revision:
- 1:ff6d8adaf6b9
- Parent:
- 0:a725e8eab383
Pointed to platform (prod)
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
AxedaCorp | 0:a725e8eab383 | 1 | #include "axHTTP.h" |
AxedaCorp | 0:a725e8eab383 | 2 | #include "axStatusCodes.h" |
AxedaCorp | 0:a725e8eab383 | 3 | #include "axConstants.h" |
AxedaCorp | 0:a725e8eab383 | 4 | #include "ctype.h" |
AxedaCorp | 0:a725e8eab383 | 5 | #include <string.h> |
AxedaCorp | 0:a725e8eab383 | 6 | #include <stdio.h> |
AxedaCorp | 0:a725e8eab383 | 7 | #include <stdlib.h> |
AxedaCorp | 0:a725e8eab383 | 8 | #include "axSettings.h" |
AxedaCorp | 0:a725e8eab383 | 9 | #include "axTransport.h" |
AxedaCorp | 0:a725e8eab383 | 10 | |
AxedaCorp | 0:a725e8eab383 | 11 | #define AX_HTTP_OK 200 |
AxedaCorp | 0:a725e8eab383 | 12 | #define AX_HTTP_SYNTAX_INVALID 400 //The Request syntax is incorrect |
AxedaCorp | 0:a725e8eab383 | 13 | #define AX_HTTP_UNAUTHORIZED 401 //You need permission to access that resource. Agent Authentication failure |
AxedaCorp | 0:a725e8eab383 | 14 | #define AX_HTTP_FORBIDDEN 403 //You need permission to access that resource. |
AxedaCorp | 0:a725e8eab383 | 15 | #define AX_HTTP_NOT_FOUND 404 //The resource was not found |
AxedaCorp | 0:a725e8eab383 | 16 | #define AX_HTTP_REQ_INVALID 405 //Content type is invalid, should be application/json most of the time |
AxedaCorp | 0:a725e8eab383 | 17 | #define AX_HTTP_CONTENT_INVALID 422 //The Request is valid syntactically but the content is invalid e.g. wrong instruction |
AxedaCorp | 0:a725e8eab383 | 18 | #define AX_HTTP_SERVER_ERROR 500 //The server attempted to handle your request but a failure occurred; check the server logs. |
AxedaCorp | 0:a725e8eab383 | 19 | |
AxedaCorp | 0:a725e8eab383 | 20 | #define HTTPKEY_GET 0 |
AxedaCorp | 0:a725e8eab383 | 21 | #define HTTPKEY_POST 1 |
AxedaCorp | 0:a725e8eab383 | 22 | #define HTTPKEY_PUT 2 |
AxedaCorp | 0:a725e8eab383 | 23 | #define HTTPKEY_PROTOCOL 3 |
AxedaCorp | 0:a725e8eab383 | 24 | #define HTTPKEY_CRLF 4 |
AxedaCorp | 0:a725e8eab383 | 25 | |
AxedaCorp | 0:a725e8eab383 | 26 | |
AxedaCorp | 0:a725e8eab383 | 27 | #define HTTPH_HOST 0 |
AxedaCorp | 0:a725e8eab383 | 28 | #define HTTPH_TYPE 1 |
AxedaCorp | 0:a725e8eab383 | 29 | #define HTTPH_LENGTH 2 |
AxedaCorp | 0:a725e8eab383 | 30 | #define HTTPH_CONNECTION 3 |
AxedaCorp | 0:a725e8eab383 | 31 | #define HTTPH_XCOUNT 4 |
AxedaCorp | 0:a725e8eab383 | 32 | #define HTTPH_CONTENTDIS 5 |
AxedaCorp | 0:a725e8eab383 | 33 | #define HTTPH_MULTIPART 6 |
AxedaCorp | 0:a725e8eab383 | 34 | #define HTTPH_FORMDATA 7 |
AxedaCorp | 0:a725e8eab383 | 35 | #define HTTPH_ACCEPT 8 |
AxedaCorp | 0:a725e8eab383 | 36 | |
AxedaCorp | 0:a725e8eab383 | 37 | #define HTTPR_TYPE 0 |
AxedaCorp | 0:a725e8eab383 | 38 | #define HTTPR_LENGTH 1 |
AxedaCorp | 0:a725e8eab383 | 39 | #define HTTPR_BODY_SEP 2 |
AxedaCorp | 0:a725e8eab383 | 40 | #define HTTPR_DELIMS 3 |
AxedaCorp | 0:a725e8eab383 | 41 | |
AxedaCorp | 0:a725e8eab383 | 42 | int http_debug=AX_FALSE; |
AxedaCorp | 0:a725e8eab383 | 43 | int x_count=0; |
AxedaCorp | 0:a725e8eab383 | 44 | |
AxedaCorp | 0:a725e8eab383 | 45 | int http_getResponse(HTTP_Transmission *response, ax_socket *source); |
AxedaCorp | 0:a725e8eab383 | 46 | |
AxedaCorp | 0:a725e8eab383 | 47 | char *HTTP_KEY[]= {"GET", "POST", "PUT", "HTTP/1.1", "\r\n"}; |
AxedaCorp | 0:a725e8eab383 | 48 | char *HTTP_HEADER[] = { "Host: ", "Content-Type: ", "Content-Length: ", "Connection: close", "x-count: ", "Content-Disposition: ","multipart/form-data; boundary=", "form-data;", "Accept: */*"}; |
AxedaCorp | 0:a725e8eab383 | 49 | char *HTTP_RESP[] = { "Content-Type:", "Content-Length:", "\r\n\r\n", " \r\n"}; |
AxedaCorp | 0:a725e8eab383 | 50 | |
AxedaCorp | 0:a725e8eab383 | 51 | int http_send(HTTP_Transmission *request, char *host, int port, int secure, HTTP_Transmission *response){ |
AxedaCorp | 0:a725e8eab383 | 52 | int retVal=AX_UNKNOWN; |
AxedaCorp | 0:a725e8eab383 | 53 | |
AxedaCorp | 0:a725e8eab383 | 54 | switch(request->operation) { |
AxedaCorp | 0:a725e8eab383 | 55 | case AX_HTTP_GET: |
AxedaCorp | 0:a725e8eab383 | 56 | retVal=http_send_get(request->resource, host, port, secure, response); |
AxedaCorp | 0:a725e8eab383 | 57 | break; |
AxedaCorp | 0:a725e8eab383 | 58 | case AX_HTTP_POST: |
AxedaCorp | 0:a725e8eab383 | 59 | retVal=http_send_post(request->resource, host, port, secure, &request->headers, request->body, request->body_length, response); |
AxedaCorp | 0:a725e8eab383 | 60 | break; |
AxedaCorp | 0:a725e8eab383 | 61 | case AX_HTTP_PUT: |
AxedaCorp | 0:a725e8eab383 | 62 | retVal=http_send_put(request->resource, host, port, secure, &request->headers, request->body, request->body_length, response); |
AxedaCorp | 0:a725e8eab383 | 63 | break; |
AxedaCorp | 0:a725e8eab383 | 64 | case AX_HTTP_MPOST: |
AxedaCorp | 0:a725e8eab383 | 65 | retVal=http_send_mpost(request->resource, host, port, secure, &request->headers, request->body, request->body_length, request->mpData, 1, response); |
AxedaCorp | 0:a725e8eab383 | 66 | break; |
AxedaCorp | 0:a725e8eab383 | 67 | } |
AxedaCorp | 0:a725e8eab383 | 68 | |
AxedaCorp | 0:a725e8eab383 | 69 | return retVal; |
AxedaCorp | 0:a725e8eab383 | 70 | } |
AxedaCorp | 0:a725e8eab383 | 71 | |
AxedaCorp | 0:a725e8eab383 | 72 | int http_send_get(char *resource, char *host, int port, int secure, HTTP_Transmission *response) { |
AxedaCorp | 0:a725e8eab383 | 73 | int retVal=AX_UNKNOWN; |
AxedaCorp | 0:a725e8eab383 | 74 | |
AxedaCorp | 0:a725e8eab383 | 75 | if(!resource||!host||!response) { return AX_ARGNULL; } |
AxedaCorp | 0:a725e8eab383 | 76 | if((port < 0)||(port>65536)) { return AX_NET_PORT_INVALID; } |
AxedaCorp | 0:a725e8eab383 | 77 | int sz=strlen(HTTP_KEY[HTTPKEY_GET])+strlen(resource)+strlen(HTTP_KEY[HTTPKEY_PROTOCOL])+strlen(HTTP_HEADER[HTTPH_HOST])+strlen(host)+13+6+strlen(HTTP_HEADER[HTTPH_CONNECTION])+strlen(HTTP_HEADER[HTTPH_ACCEPT]); //+6 adds space for extra CRLFs, spaces, and null terminator |
AxedaCorp | 0:a725e8eab383 | 78 | |
AxedaCorp | 0:a725e8eab383 | 79 | ax_socket sock; |
AxedaCorp | 0:a725e8eab383 | 80 | |
AxedaCorp | 0:a725e8eab383 | 81 | retVal=net_socketInit(&sock); |
AxedaCorp | 0:a725e8eab383 | 82 | retVal=net_socketOpen(&sock, host, port, secure); |
AxedaCorp | 0:a725e8eab383 | 83 | if(retVal!=AX_OK){ |
AxedaCorp | 0:a725e8eab383 | 84 | return retVal; |
AxedaCorp | 0:a725e8eab383 | 85 | } |
AxedaCorp | 0:a725e8eab383 | 86 | |
AxedaCorp | 0:a725e8eab383 | 87 | char *buff=(char *)malloc(sizeof(char)*sz); |
AxedaCorp | 0:a725e8eab383 | 88 | int wrtSz=snprintf(buff, sz, "%s %s %s%s%s%s:%d%s%s%s%s", HTTP_KEY[HTTPKEY_GET], resource, HTTP_KEY[HTTPKEY_PROTOCOL], HTTP_KEY[HTTPKEY_CRLF], HTTP_HEADER[HTTPH_HOST], host, port, HTTP_KEY[HTTPKEY_CRLF], HTTP_HEADER[HTTPH_CONNECTION], HTTP_KEY[HTTPKEY_CRLF], HTTP_KEY[HTTPKEY_CRLF]); |
AxedaCorp | 0:a725e8eab383 | 89 | |
AxedaCorp | 0:a725e8eab383 | 90 | if(wrtSz>sz) { printDebug("http_send_get(): Buffer allocated for header was too small, truncated"); } |
AxedaCorp | 0:a725e8eab383 | 91 | retVal=net_socketWrite(&sock, buff, sz); |
AxedaCorp | 0:a725e8eab383 | 92 | retVal=net_socketFlush(&sock); |
AxedaCorp | 0:a725e8eab383 | 93 | |
AxedaCorp | 0:a725e8eab383 | 94 | retVal=http_getResponse(response, &sock); |
AxedaCorp | 0:a725e8eab383 | 95 | if(retVal==AX_HTTP_OK) { retVal=AX_OK; } |
AxedaCorp | 0:a725e8eab383 | 96 | |
AxedaCorp | 0:a725e8eab383 | 97 | net_socketClose(&sock); |
AxedaCorp | 0:a725e8eab383 | 98 | free(buff); |
AxedaCorp | 0:a725e8eab383 | 99 | return retVal; |
AxedaCorp | 0:a725e8eab383 | 100 | } |
AxedaCorp | 0:a725e8eab383 | 101 | |
AxedaCorp | 0:a725e8eab383 | 102 | int http_send_put(char *resource, char *host, int port, int secure, HTTP_Headers *headers, char *data, int data_sz, HTTP_Transmission *response) { |
AxedaCorp | 0:a725e8eab383 | 103 | int retVal=AX_UNKNOWN; |
AxedaCorp | 0:a725e8eab383 | 104 | |
AxedaCorp | 0:a725e8eab383 | 105 | if(!resource||!host||!response) { return AX_ARGNULL; } |
AxedaCorp | 0:a725e8eab383 | 106 | if((port < 0)||(port>65536)) { return AX_NET_PORT_INVALID; } |
AxedaCorp | 0:a725e8eab383 | 107 | if((!data)&&(data_sz!=0)) { return AX_CONFLICTING_ARG; } |
AxedaCorp | 0:a725e8eab383 | 108 | |
AxedaCorp | 0:a725e8eab383 | 109 | ax_socket sock; |
AxedaCorp | 0:a725e8eab383 | 110 | |
AxedaCorp | 0:a725e8eab383 | 111 | retVal=net_socketInit(&sock); |
AxedaCorp | 0:a725e8eab383 | 112 | retVal=net_socketOpen(&sock, host, port, secure); |
AxedaCorp | 0:a725e8eab383 | 113 | if(retVal!=AX_OK){ //if a failure occurred bail out. failures here would be that a port is not free |
AxedaCorp | 0:a725e8eab383 | 114 | return retVal; |
AxedaCorp | 0:a725e8eab383 | 115 | } |
AxedaCorp | 0:a725e8eab383 | 116 | printDebug("Attempting to create headers"); |
AxedaCorp | 0:a725e8eab383 | 117 | char *header=NULL; |
AxedaCorp | 0:a725e8eab383 | 118 | header=createHeaders(header, HTTPKEY_PUT, resource, HTTPKEY_PROTOCOL, host, headers->contentType, data_sz, x_count); //returns a malloc'd string with the headers |
AxedaCorp | 0:a725e8eab383 | 119 | |
AxedaCorp | 0:a725e8eab383 | 120 | retVal=net_socketWrite(&sock, header, strlen(header)); |
AxedaCorp | 0:a725e8eab383 | 121 | if(retVal!=AX_OK){ //if a failure occurred bail out. failures here would be that a port is not free |
AxedaCorp | 0:a725e8eab383 | 122 | goto cleanup; |
AxedaCorp | 0:a725e8eab383 | 123 | } |
AxedaCorp | 0:a725e8eab383 | 124 | retVal=net_socketWrite(&sock, data, data_sz); |
AxedaCorp | 0:a725e8eab383 | 125 | net_socketFlush(&sock); |
AxedaCorp | 0:a725e8eab383 | 126 | if(retVal!=AX_OK) { |
AxedaCorp | 0:a725e8eab383 | 127 | retVal=AX_NET_ERR_DATA_WRITE; |
AxedaCorp | 0:a725e8eab383 | 128 | goto cleanup; |
AxedaCorp | 0:a725e8eab383 | 129 | } |
AxedaCorp | 0:a725e8eab383 | 130 | |
AxedaCorp | 0:a725e8eab383 | 131 | retVal=http_getResponse(response, &sock); |
AxedaCorp | 0:a725e8eab383 | 132 | if(retVal==AX_HTTP_OK) { retVal=AX_OK; } |
AxedaCorp | 0:a725e8eab383 | 133 | |
AxedaCorp | 0:a725e8eab383 | 134 | x_count++; |
AxedaCorp | 0:a725e8eab383 | 135 | goto cleanup; |
AxedaCorp | 0:a725e8eab383 | 136 | |
AxedaCorp | 0:a725e8eab383 | 137 | |
AxedaCorp | 0:a725e8eab383 | 138 | cleanup: |
AxedaCorp | 0:a725e8eab383 | 139 | net_socketClose(&sock); |
AxedaCorp | 0:a725e8eab383 | 140 | free(header); |
AxedaCorp | 0:a725e8eab383 | 141 | return retVal; |
AxedaCorp | 0:a725e8eab383 | 142 | } |
AxedaCorp | 0:a725e8eab383 | 143 | |
AxedaCorp | 0:a725e8eab383 | 144 | |
AxedaCorp | 0:a725e8eab383 | 145 | |
AxedaCorp | 0:a725e8eab383 | 146 | int http_send_post(char *resource, char *host, int port, int secure, HTTP_Headers *headers, char *data, int data_sz, HTTP_Transmission *response){ |
AxedaCorp | 0:a725e8eab383 | 147 | int retVal=AX_UNKNOWN; |
AxedaCorp | 0:a725e8eab383 | 148 | |
AxedaCorp | 0:a725e8eab383 | 149 | if(!resource||!host||!response) { return AX_ARGNULL; } |
AxedaCorp | 0:a725e8eab383 | 150 | if((port < 0)||(port>65536)) { return AX_NET_PORT_INVALID; } |
AxedaCorp | 0:a725e8eab383 | 151 | if((!data)&&(data_sz!=0)) { return AX_CONFLICTING_ARG; } |
AxedaCorp | 0:a725e8eab383 | 152 | |
AxedaCorp | 0:a725e8eab383 | 153 | ax_socket sock; |
AxedaCorp | 0:a725e8eab383 | 154 | |
AxedaCorp | 0:a725e8eab383 | 155 | retVal=net_socketInit(&sock); |
AxedaCorp | 0:a725e8eab383 | 156 | retVal=net_socketOpen(&sock, host, port, secure); |
AxedaCorp | 0:a725e8eab383 | 157 | if(retVal!=AX_OK){ //if a failure occurred bail out. failures here would be that a port is not free |
AxedaCorp | 0:a725e8eab383 | 158 | return retVal; |
AxedaCorp | 0:a725e8eab383 | 159 | } |
AxedaCorp | 0:a725e8eab383 | 160 | printDebug("Attempting to create headers"); |
AxedaCorp | 0:a725e8eab383 | 161 | char *header=NULL; |
AxedaCorp | 0:a725e8eab383 | 162 | header=createHeaders(header, HTTPKEY_POST, resource, HTTPKEY_PROTOCOL, host, headers->contentType, data_sz, x_count); //returns a malloc'd string with the headers |
AxedaCorp | 0:a725e8eab383 | 163 | |
AxedaCorp | 0:a725e8eab383 | 164 | retVal=net_socketWrite(&sock, header, strlen(header)); |
AxedaCorp | 0:a725e8eab383 | 165 | if(retVal!=AX_OK){ //if a failure occurred bail out. failures here would be that a port is not free |
AxedaCorp | 0:a725e8eab383 | 166 | goto cleanup; |
AxedaCorp | 0:a725e8eab383 | 167 | } |
AxedaCorp | 0:a725e8eab383 | 168 | retVal=net_socketWrite(&sock, data, data_sz); |
AxedaCorp | 0:a725e8eab383 | 169 | net_socketFlush(&sock); |
AxedaCorp | 0:a725e8eab383 | 170 | x_count++; |
AxedaCorp | 0:a725e8eab383 | 171 | printDebug("Transmission sent, Waiting for response\n"); |
AxedaCorp | 0:a725e8eab383 | 172 | |
AxedaCorp | 0:a725e8eab383 | 173 | |
AxedaCorp | 0:a725e8eab383 | 174 | retVal=http_getResponse(response, &sock); |
AxedaCorp | 0:a725e8eab383 | 175 | if(retVal==AX_HTTP_OK) { retVal=AX_OK; } |
AxedaCorp | 0:a725e8eab383 | 176 | |
AxedaCorp | 0:a725e8eab383 | 177 | goto cleanup; |
AxedaCorp | 0:a725e8eab383 | 178 | |
AxedaCorp | 0:a725e8eab383 | 179 | cleanup: |
AxedaCorp | 0:a725e8eab383 | 180 | net_socketClose(&sock); |
AxedaCorp | 0:a725e8eab383 | 181 | free(header); |
AxedaCorp | 0:a725e8eab383 | 182 | return retVal; |
AxedaCorp | 0:a725e8eab383 | 183 | } |
AxedaCorp | 0:a725e8eab383 | 184 | |
AxedaCorp | 0:a725e8eab383 | 185 | int http_send_mpost(char *resource, char *host, int port, int secure, HTTP_Headers *headers, char *data, int data_sz, HTTP_Part *files, int part_ct, HTTP_Transmission *response) { |
AxedaCorp | 0:a725e8eab383 | 186 | int retVal=AX_UNKNOWN; |
AxedaCorp | 0:a725e8eab383 | 187 | // char xcount_tmp[10]; |
AxedaCorp | 0:a725e8eab383 | 188 | // char clength_tmp[10]; |
AxedaCorp | 0:a725e8eab383 | 189 | int boundary_sz=10; |
AxedaCorp | 0:a725e8eab383 | 190 | int body_sz=0; |
AxedaCorp | 0:a725e8eab383 | 191 | int hint_sz=0; |
AxedaCorp | 0:a725e8eab383 | 192 | char *hint=NULL; |
AxedaCorp | 0:a725e8eab383 | 193 | char *header=NULL; |
AxedaCorp | 0:a725e8eab383 | 194 | ax_socket sock; |
AxedaCorp | 0:a725e8eab383 | 195 | |
AxedaCorp | 0:a725e8eab383 | 196 | if(!resource||!host||!response) { return AX_ARGNULL; } |
AxedaCorp | 0:a725e8eab383 | 197 | if((port < 0)||(port>65536)) { return AX_NET_PORT_INVALID; } |
AxedaCorp | 0:a725e8eab383 | 198 | if((!data)&&(data_sz!=0)) { return AX_CONFLICTING_ARG; } |
AxedaCorp | 0:a725e8eab383 | 199 | //Assemble body Parts |
AxedaCorp | 0:a725e8eab383 | 200 | char *fileargs=NULL; |
AxedaCorp | 0:a725e8eab383 | 201 | int file_arg_len=strlen(files->file_name)+51; |
AxedaCorp | 0:a725e8eab383 | 202 | fileargs=(char *)malloc(sizeof(char)*file_arg_len); |
AxedaCorp | 0:a725e8eab383 | 203 | int wrtfarg=snprintf(fileargs, file_arg_len, "name=\"file\"; filename=\"%s\"\r\nContent-Type: text/plain", files->file_name); |
AxedaCorp | 0:a725e8eab383 | 204 | if(wrtfarg>file_arg_len) { printDebug("http_send_mpost(): Buffer allocated for file arguments was too small, truncated"); } |
AxedaCorp | 0:a725e8eab383 | 205 | if(files->hint!=NULL) |
AxedaCorp | 0:a725e8eab383 | 206 | { |
AxedaCorp | 0:a725e8eab383 | 207 | hint_sz=strlen(files->hint)+18;//+1 for terminating char |
AxedaCorp | 0:a725e8eab383 | 208 | hint=(char *)malloc(sizeof(char)*hint_sz); |
AxedaCorp | 0:a725e8eab383 | 209 | int wrthint=snprintf(hint, hint_sz, "name=\"hint\"\r\n\r\n%s\r\n", files->hint); |
AxedaCorp | 0:a725e8eab383 | 210 | if(wrthint>hint_sz) { printDebug("http_send_mpost(): Buffer allocated for hint was too small, truncated"); } |
AxedaCorp | 0:a725e8eab383 | 211 | } |
AxedaCorp | 0:a725e8eab383 | 212 | //body size calculation here |
AxedaCorp | 0:a725e8eab383 | 213 | //Boundary: --<boundary> |
AxedaCorp | 0:a725e8eab383 | 214 | body_sz=body_sz+2+boundary_sz; |
AxedaCorp | 0:a725e8eab383 | 215 | //adding body ---for mparts |
AxedaCorp | 0:a725e8eab383 | 216 | if(data_sz>0){ |
AxedaCorp | 0:a725e8eab383 | 217 | body_sz=body_sz+36+data_sz+boundary_sz; |
AxedaCorp | 0:a725e8eab383 | 218 | } |
AxedaCorp | 0:a725e8eab383 | 219 | //Add Hint Content-Disposition: form-data;name="<hint>"\r\n--<boundary>\r\n |
AxedaCorp | 0:a725e8eab383 | 220 | if(files->hint!=NULL){ |
AxedaCorp | 0:a725e8eab383 | 221 | body_sz=body_sz+38+strlen(hint)+boundary_sz; |
AxedaCorp | 0:a725e8eab383 | 222 | } |
AxedaCorp | 0:a725e8eab383 | 223 | if(files->data_sz>0) { |
AxedaCorp | 0:a725e8eab383 | 224 | body_sz=body_sz+38+strlen(fileargs)+files->data_sz; |
AxedaCorp | 0:a725e8eab383 | 225 | } |
AxedaCorp | 0:a725e8eab383 | 226 | body_sz=body_sz+8+boundary_sz; |
AxedaCorp | 0:a725e8eab383 | 227 | |
AxedaCorp | 0:a725e8eab383 | 228 | //get a boundary for the multipart |
AxedaCorp | 0:a725e8eab383 | 229 | char *boundary=(char *)malloc(sizeof(char)*boundary_sz); |
AxedaCorp | 0:a725e8eab383 | 230 | http_getBoundary(boundary, boundary_sz); |
AxedaCorp | 0:a725e8eab383 | 231 | //convert the data size integer into a string |
AxedaCorp | 0:a725e8eab383 | 232 | // int wrtCLen=snprintf(clength_tmp, 10, "%d", body_sz); |
AxedaCorp | 0:a725e8eab383 | 233 | // if(wrtCLen>10) { printDebug("http_send_mpost(): Buffer allocated for clength_tmp was too small, truncated"); } |
AxedaCorp | 0:a725e8eab383 | 234 | //Add operation line |
AxedaCorp | 0:a725e8eab383 | 235 | |
AxedaCorp | 0:a725e8eab383 | 236 | printDebug("Attempting to create headers"); |
AxedaCorp | 0:a725e8eab383 | 237 | |
AxedaCorp | 0:a725e8eab383 | 238 | //Create Content Type |
AxedaCorp | 0:a725e8eab383 | 239 | int cTypeSz=0; |
AxedaCorp | 0:a725e8eab383 | 240 | cTypeSz=strlen(HTTP_HEADER[HTTPH_MULTIPART])+strlen(boundary)+1; |
AxedaCorp | 0:a725e8eab383 | 241 | char *contentType=(char *)calloc(cTypeSz, sizeof(char)); |
AxedaCorp | 0:a725e8eab383 | 242 | int wrt_ctype=snprintf(contentType, cTypeSz, "%s%s", HTTP_HEADER[HTTPH_MULTIPART], boundary); |
AxedaCorp | 0:a725e8eab383 | 243 | if(wrt_ctype>cTypeSz) { printDebug("http_send_mpost(): Buffer allocated for contentType was too small, truncated");} |
AxedaCorp | 0:a725e8eab383 | 244 | //Get the headers |
AxedaCorp | 0:a725e8eab383 | 245 | header=createHeaders(header, HTTPKEY_POST, resource, HTTPKEY_PROTOCOL, host, contentType, body_sz, x_count); //returns a malloc'd string with the headers |
AxedaCorp | 0:a725e8eab383 | 246 | free(contentType); //we're done with the string we used to cat the values |
AxedaCorp | 0:a725e8eab383 | 247 | //Start to send the data |
AxedaCorp | 0:a725e8eab383 | 248 | retVal=net_socketInit(&sock); |
AxedaCorp | 0:a725e8eab383 | 249 | retVal=net_socketOpen(&sock, host, port, secure); |
AxedaCorp | 0:a725e8eab383 | 250 | if(retVal!=AX_OK){ //if a failure occurred, bail out. A failure here would indicate that a port is closed, or the network is not available. |
AxedaCorp | 0:a725e8eab383 | 251 | return retVal; |
AxedaCorp | 0:a725e8eab383 | 252 | } |
AxedaCorp | 0:a725e8eab383 | 253 | //Write the headers |
AxedaCorp | 0:a725e8eab383 | 254 | retVal=net_socketWrite(&sock, header, strlen(header)); |
AxedaCorp | 0:a725e8eab383 | 255 | if(retVal!=AX_OK){ goto cleanup; } |
AxedaCorp | 0:a725e8eab383 | 256 | |
AxedaCorp | 0:a725e8eab383 | 257 | //write the body... |
AxedaCorp | 0:a725e8eab383 | 258 | //start boundary |
AxedaCorp | 0:a725e8eab383 | 259 | retVal=net_socketWrite(&sock, "--", 2); |
AxedaCorp | 0:a725e8eab383 | 260 | retVal=net_socketWrite(&sock, boundary, boundary_sz); |
AxedaCorp | 0:a725e8eab383 | 261 | retVal=net_socketWrite(&sock, HTTP_KEY[HTTPKEY_CRLF], 2); |
AxedaCorp | 0:a725e8eab383 | 262 | //1st section -uses the data in body if specified |
AxedaCorp | 0:a725e8eab383 | 263 | if(data_sz>0){ |
AxedaCorp | 0:a725e8eab383 | 264 | retVal=net_socketWrite(&sock, HTTP_HEADER[HTTPH_CONTENTDIS], 21); |
AxedaCorp | 0:a725e8eab383 | 265 | retVal=net_socketWrite(&sock, HTTP_HEADER[HTTPH_FORMDATA], 11); |
AxedaCorp | 0:a725e8eab383 | 266 | retVal=net_socketWrite(&sock, data, data_sz); //Write body data, the fields and values |
AxedaCorp | 0:a725e8eab383 | 267 | retVal=net_socketWrite(&sock, HTTP_KEY[HTTPKEY_CRLF], 2); |
AxedaCorp | 0:a725e8eab383 | 268 | retVal=net_socketWrite(&sock, "--", 2); |
AxedaCorp | 0:a725e8eab383 | 269 | retVal=net_socketWrite(&sock, boundary, boundary_sz); |
AxedaCorp | 0:a725e8eab383 | 270 | } |
AxedaCorp | 0:a725e8eab383 | 271 | if(files->hint!=NULL){ |
AxedaCorp | 0:a725e8eab383 | 272 | retVal=net_socketWrite(&sock, HTTP_HEADER[HTTPH_CONTENTDIS], 21); |
AxedaCorp | 0:a725e8eab383 | 273 | retVal=net_socketWrite(&sock, HTTP_HEADER[HTTPH_FORMDATA], 11); |
AxedaCorp | 0:a725e8eab383 | 274 | retVal=net_socketWrite(&sock, hint, hint_sz); //Write body data, the fields and values |
AxedaCorp | 0:a725e8eab383 | 275 | retVal=net_socketWrite(&sock, HTTP_KEY[HTTPKEY_CRLF], 2); |
AxedaCorp | 0:a725e8eab383 | 276 | retVal=net_socketWrite(&sock, "--", 2); |
AxedaCorp | 0:a725e8eab383 | 277 | retVal=net_socketWrite(&sock, boundary, boundary_sz); |
AxedaCorp | 0:a725e8eab383 | 278 | retVal=net_socketWrite(&sock, HTTP_KEY[HTTPKEY_CRLF], 2); |
AxedaCorp | 0:a725e8eab383 | 279 | } |
AxedaCorp | 0:a725e8eab383 | 280 | if(files->data_sz>0) { |
AxedaCorp | 0:a725e8eab383 | 281 | retVal=net_socketWrite(&sock, HTTP_HEADER[HTTPH_CONTENTDIS], 21); |
AxedaCorp | 0:a725e8eab383 | 282 | retVal=net_socketWrite(&sock, HTTP_HEADER[HTTPH_FORMDATA], 11); |
AxedaCorp | 0:a725e8eab383 | 283 | retVal=net_socketWrite(&sock, fileargs, file_arg_len); |
AxedaCorp | 0:a725e8eab383 | 284 | retVal=net_socketWrite(&sock, HTTP_KEY[HTTPKEY_CRLF], 2); |
AxedaCorp | 0:a725e8eab383 | 285 | retVal=net_socketWrite(&sock, HTTP_KEY[HTTPKEY_CRLF], 2); |
AxedaCorp | 0:a725e8eab383 | 286 | retVal=net_socketWrite(&sock, (char *)files->data, files->data_sz); |
AxedaCorp | 0:a725e8eab383 | 287 | retVal=net_socketWrite(&sock, HTTP_KEY[HTTPKEY_CRLF], 2); |
AxedaCorp | 0:a725e8eab383 | 288 | } |
AxedaCorp | 0:a725e8eab383 | 289 | retVal=net_socketWrite(&sock, "--", 2); |
AxedaCorp | 0:a725e8eab383 | 290 | retVal=net_socketWrite(&sock, boundary, boundary_sz); |
AxedaCorp | 0:a725e8eab383 | 291 | retVal=net_socketWrite(&sock, "--", 2); |
AxedaCorp | 0:a725e8eab383 | 292 | retVal=net_socketWrite(&sock, HTTP_KEY[HTTPKEY_CRLF], 2); |
AxedaCorp | 0:a725e8eab383 | 293 | retVal=net_socketWrite(&sock, HTTP_KEY[HTTPKEY_CRLF], 2); |
AxedaCorp | 0:a725e8eab383 | 294 | |
AxedaCorp | 0:a725e8eab383 | 295 | |
AxedaCorp | 0:a725e8eab383 | 296 | net_socketFlush(&sock); |
AxedaCorp | 0:a725e8eab383 | 297 | x_count++; |
AxedaCorp | 0:a725e8eab383 | 298 | retVal=http_getResponse(response, &sock); |
AxedaCorp | 0:a725e8eab383 | 299 | if(retVal==AX_HTTP_OK) { retVal=AX_OK; } |
AxedaCorp | 0:a725e8eab383 | 300 | |
AxedaCorp | 0:a725e8eab383 | 301 | cleanup: |
AxedaCorp | 0:a725e8eab383 | 302 | net_socketClose(&sock); |
AxedaCorp | 0:a725e8eab383 | 303 | free(header); |
AxedaCorp | 0:a725e8eab383 | 304 | free(hint); |
AxedaCorp | 0:a725e8eab383 | 305 | free(fileargs); |
AxedaCorp | 0:a725e8eab383 | 306 | free(boundary); |
AxedaCorp | 0:a725e8eab383 | 307 | return retVal; |
AxedaCorp | 0:a725e8eab383 | 308 | } |
AxedaCorp | 0:a725e8eab383 | 309 | |
AxedaCorp | 0:a725e8eab383 | 310 | void http_add_mpart(HTTP_Transmission *request, char *file_name, unsigned char *data, int file_sz) { |
AxedaCorp | 0:a725e8eab383 | 311 | //For future expansion. |
AxedaCorp | 0:a725e8eab383 | 312 | } |
AxedaCorp | 0:a725e8eab383 | 313 | |
AxedaCorp | 0:a725e8eab383 | 314 | //Return Handling |
AxedaCorp | 0:a725e8eab383 | 315 | HTTP_Transmission *http_parse(HTTP_Transmission *response, char *data){ |
AxedaCorp | 0:a725e8eab383 | 316 | printDebug("Starting parse of "); |
AxedaCorp | 0:a725e8eab383 | 317 | printDebug(data); |
AxedaCorp | 0:a725e8eab383 | 318 | |
AxedaCorp | 0:a725e8eab383 | 319 | char clength[7]; |
AxedaCorp | 0:a725e8eab383 | 320 | int reqSize=0; |
AxedaCorp | 0:a725e8eab383 | 321 | char *temp=NULL; |
AxedaCorp | 0:a725e8eab383 | 322 | char *bodyStart=NULL; |
AxedaCorp | 0:a725e8eab383 | 323 | response->body_length=0; |
AxedaCorp | 0:a725e8eab383 | 324 | response->response_code=-1; //indicate that no response code has been recieved(yet) |
AxedaCorp | 0:a725e8eab383 | 325 | |
AxedaCorp | 0:a725e8eab383 | 326 | reqSize=strlen(data); |
AxedaCorp | 0:a725e8eab383 | 327 | if(reqSize<=12) { printDebug("No Body included"); } |
AxedaCorp | 0:a725e8eab383 | 328 | else { printDebug("body present" ); |
AxedaCorp | 0:a725e8eab383 | 329 | bodyStart=strstr(data, HTTP_RESP[HTTPR_BODY_SEP]); |
AxedaCorp | 0:a725e8eab383 | 330 | if(bodyStart==NULL) { printDebug("D'oh! Couldn't parse for body\n"); } |
AxedaCorp | 0:a725e8eab383 | 331 | else { |
AxedaCorp | 0:a725e8eab383 | 332 | int body_length=strlen(bodyStart)-4; //don't count the extra \r\n\r\n |
AxedaCorp | 0:a725e8eab383 | 333 | response->body_length=body_length; |
AxedaCorp | 0:a725e8eab383 | 334 | response->body=(char *)calloc(body_length+1, sizeof(char)); |
AxedaCorp | 0:a725e8eab383 | 335 | snprintf(response->body, body_length+1, "%s", bodyStart+4); //the +4 gets rid of the preceeding \r\n\r\n |
AxedaCorp | 0:a725e8eab383 | 336 | } |
AxedaCorp | 0:a725e8eab383 | 337 | } |
AxedaCorp | 0:a725e8eab383 | 338 | |
AxedaCorp | 0:a725e8eab383 | 339 | temp = strtok(data, HTTP_RESP[HTTPR_DELIMS]); |
AxedaCorp | 0:a725e8eab383 | 340 | while (temp!=NULL) { |
AxedaCorp | 0:a725e8eab383 | 341 | |
AxedaCorp | 0:a725e8eab383 | 342 | if((strlen(temp)==3)&&(isdigit((int)temp[0]))&&(isdigit((int)temp[1]))&&(isdigit((int)temp[2]))) { |
AxedaCorp | 0:a725e8eab383 | 343 | response->response_code=atoi(temp); //convert the text response code to an int. |
AxedaCorp | 0:a725e8eab383 | 344 | } |
AxedaCorp | 0:a725e8eab383 | 345 | |
AxedaCorp | 0:a725e8eab383 | 346 | if(strcmp(temp, HTTP_RESP[HTTPR_TYPE])==0) { |
AxedaCorp | 0:a725e8eab383 | 347 | response->headers.contentType=strtok(NULL, HTTP_RESP[HTTPR_DELIMS]); |
AxedaCorp | 0:a725e8eab383 | 348 | } |
AxedaCorp | 0:a725e8eab383 | 349 | if(strcmp(temp, HTTP_RESP[HTTPR_LENGTH])==0){ |
AxedaCorp | 0:a725e8eab383 | 350 | temp=strtok(NULL, HTTP_RESP[HTTPR_DELIMS]); |
AxedaCorp | 0:a725e8eab383 | 351 | strcpy(clength, temp); |
AxedaCorp | 0:a725e8eab383 | 352 | response->body_length=atoi(clength); |
AxedaCorp | 0:a725e8eab383 | 353 | if(response->body_length<=0) { |
AxedaCorp | 0:a725e8eab383 | 354 | response->body_length=strlen(bodyStart+4); |
AxedaCorp | 0:a725e8eab383 | 355 | } |
AxedaCorp | 0:a725e8eab383 | 356 | } |
AxedaCorp | 0:a725e8eab383 | 357 | |
AxedaCorp | 0:a725e8eab383 | 358 | temp=strtok(NULL, HTTP_RESP[HTTPR_DELIMS]); |
AxedaCorp | 0:a725e8eab383 | 359 | } |
AxedaCorp | 0:a725e8eab383 | 360 | |
AxedaCorp | 0:a725e8eab383 | 361 | return response; |
AxedaCorp | 0:a725e8eab383 | 362 | } |
AxedaCorp | 0:a725e8eab383 | 363 | |
AxedaCorp | 0:a725e8eab383 | 364 | int http_getResponse(HTTP_Transmission *response, ax_socket *source){ |
AxedaCorp | 0:a725e8eab383 | 365 | int retVal=AX_UNKNOWN; |
AxedaCorp | 0:a725e8eab383 | 366 | char *responseTxt=NULL; |
AxedaCorp | 0:a725e8eab383 | 367 | responseTxt=(char *)calloc(AX_NET_BUFF_S, sizeof(char)); |
AxedaCorp | 0:a725e8eab383 | 368 | int length=0; |
AxedaCorp | 0:a725e8eab383 | 369 | response->response_code=0; |
AxedaCorp | 0:a725e8eab383 | 370 | retVal=net_socketRead(source, 300, (unsigned char *)responseTxt, AX_NET_BUFF_S, &length); |
AxedaCorp | 0:a725e8eab383 | 371 | if(retVal==AX_OK) { printDebug("response received"); } |
AxedaCorp | 0:a725e8eab383 | 372 | else { printDebug("Response Timeout"); return AX_NET_ERR_TIMEOUT;} |
AxedaCorp | 0:a725e8eab383 | 373 | http_parse(response, (char *)responseTxt); |
AxedaCorp | 0:a725e8eab383 | 374 | |
AxedaCorp | 0:a725e8eab383 | 375 | free(responseTxt); |
AxedaCorp | 0:a725e8eab383 | 376 | return response->response_code; |
AxedaCorp | 0:a725e8eab383 | 377 | } |
AxedaCorp | 0:a725e8eab383 | 378 | |
AxedaCorp | 0:a725e8eab383 | 379 | |
AxedaCorp | 0:a725e8eab383 | 380 | //Supporting Methods |
AxedaCorp | 0:a725e8eab383 | 381 | /***************************************************************************************************/ |
AxedaCorp | 0:a725e8eab383 | 382 | /*http_getBoundary() */ |
AxedaCorp | 0:a725e8eab383 | 383 | /* */ |
AxedaCorp | 0:a725e8eab383 | 384 | /*This method creates a unique string token that is used when creating mulitpart form transmissions*/ |
AxedaCorp | 0:a725e8eab383 | 385 | /*in HTTP. It will return a random sequence of letters and numbers. */ |
AxedaCorp | 0:a725e8eab383 | 386 | /***************************************************************************************************/ |
AxedaCorp | 0:a725e8eab383 | 387 | char *http_getBoundary(char *buff, int length){ |
AxedaCorp | 0:a725e8eab383 | 388 | int ctr=0; |
AxedaCorp | 0:a725e8eab383 | 389 | if(buff==NULL) { return NULL; } //if no pointer was given to us give a nothing back. |
AxedaCorp | 0:a725e8eab383 | 390 | if(length<=0) {return NULL;} |
AxedaCorp | 0:a725e8eab383 | 391 | int randNum=0; |
AxedaCorp | 0:a725e8eab383 | 392 | for(ctr=0; ctr<length; ctr++) { |
AxedaCorp | 0:a725e8eab383 | 393 | randNum=abs(randInt()%62); |
AxedaCorp | 0:a725e8eab383 | 394 | if(randNum<=10) { |
AxedaCorp | 0:a725e8eab383 | 395 | if(randNum==0) { randNum++; } //avoid using slashes in boundary, char 47 |
AxedaCorp | 0:a725e8eab383 | 396 | buff[ctr]=randNum+47; |
AxedaCorp | 0:a725e8eab383 | 397 | } |
AxedaCorp | 0:a725e8eab383 | 398 | else if((randNum>10)&&(randNum<=36)) { |
AxedaCorp | 0:a725e8eab383 | 399 | buff[ctr]=(randNum-10)+64; |
AxedaCorp | 0:a725e8eab383 | 400 | } |
AxedaCorp | 0:a725e8eab383 | 401 | else { |
AxedaCorp | 0:a725e8eab383 | 402 | buff[ctr]=(randNum-37)+97; |
AxedaCorp | 0:a725e8eab383 | 403 | } |
AxedaCorp | 0:a725e8eab383 | 404 | if(buff[ctr]==32) {buff[ctr]=97; } |
AxedaCorp | 0:a725e8eab383 | 405 | } |
AxedaCorp | 0:a725e8eab383 | 406 | |
AxedaCorp | 0:a725e8eab383 | 407 | return buff; |
AxedaCorp | 0:a725e8eab383 | 408 | } |
AxedaCorp | 0:a725e8eab383 | 409 | |
AxedaCorp | 0:a725e8eab383 | 410 | void zeroOutHTTPXmission(HTTP_Transmission *tgt){ |
AxedaCorp | 0:a725e8eab383 | 411 | |
AxedaCorp | 0:a725e8eab383 | 412 | tgt->operation=-1; |
AxedaCorp | 0:a725e8eab383 | 413 | tgt->resource=NULL; |
AxedaCorp | 0:a725e8eab383 | 414 | tgt->headers.contentType=NULL; |
AxedaCorp | 0:a725e8eab383 | 415 | tgt->body=NULL; |
AxedaCorp | 0:a725e8eab383 | 416 | tgt->body_length=0; |
AxedaCorp | 0:a725e8eab383 | 417 | tgt->mpData=NULL; |
AxedaCorp | 0:a725e8eab383 | 418 | tgt->response_code=-1; |
AxedaCorp | 0:a725e8eab383 | 419 | tgt->secure=-1; |
AxedaCorp | 0:a725e8eab383 | 420 | } |
AxedaCorp | 0:a725e8eab383 | 421 | |
AxedaCorp | 0:a725e8eab383 | 422 | char *createHeaders(char *tgtBuff, int http_operation, char *resource, int httpver, char *hostname, char *contentType, int contentLength, int xCount) { |
AxedaCorp | 0:a725e8eab383 | 423 | int headerSz=4; //we account for the closing CRLF CRLF here |
AxedaCorp | 0:a725e8eab383 | 424 | char clength_tmp[10]; |
AxedaCorp | 0:a725e8eab383 | 425 | char xCount_tmp[10]; |
AxedaCorp | 0:a725e8eab383 | 426 | int wrthdr=0; |
AxedaCorp | 0:a725e8eab383 | 427 | //convert the ints to strings so we can get an strlen on them |
AxedaCorp | 0:a725e8eab383 | 428 | snprintf(clength_tmp, 10, "%d", contentLength); |
AxedaCorp | 0:a725e8eab383 | 429 | snprintf(xCount_tmp, 10, "%d", xCount); |
AxedaCorp | 0:a725e8eab383 | 430 | |
AxedaCorp | 0:a725e8eab383 | 431 | //String Size Calculations |
AxedaCorp | 0:a725e8eab383 | 432 | headerSz=headerSz+strlen(resource)+strlen(HTTP_KEY[http_operation])+strlen(HTTP_KEY[httpver])+5; //"POST HTTP/1.1" there are 2 spaces and a CRLF in there as well., 1 for the null |
AxedaCorp | 0:a725e8eab383 | 433 | headerSz=headerSz+strlen(HTTP_HEADER[HTTPH_HOST])+strlen(hostname)+2; //Host: <hostname> |
AxedaCorp | 0:a725e8eab383 | 434 | headerSz=headerSz+strlen(HTTP_HEADER[HTTPH_TYPE])+strlen(contentType)+2; //Content-Type: <contentType> |
AxedaCorp | 0:a725e8eab383 | 435 | headerSz=headerSz+strlen(HTTP_HEADER[HTTPH_LENGTH])+strlen(clength_tmp)+2; //Content-Length: <contentLength> |
AxedaCorp | 0:a725e8eab383 | 436 | headerSz=headerSz+strlen(HTTP_HEADER[HTTPH_CONNECTION])+2; //Connection: Close |
AxedaCorp | 0:a725e8eab383 | 437 | if(xCount>0) { |
AxedaCorp | 0:a725e8eab383 | 438 | headerSz=headerSz+strlen(HTTP_HEADER[HTTPH_XCOUNT])+strlen(xCount_tmp)+2; |
AxedaCorp | 0:a725e8eab383 | 439 | } |
AxedaCorp | 0:a725e8eab383 | 440 | |
AxedaCorp | 0:a725e8eab383 | 441 | //String buffer creation |
AxedaCorp | 0:a725e8eab383 | 442 | tgtBuff=(char *)calloc(headerSz, sizeof(char)); |
AxedaCorp | 0:a725e8eab383 | 443 | //Stuffin the buffer |
AxedaCorp | 0:a725e8eab383 | 444 | if((xCount>0)&&(http_debug==AX_TRUE)) { |
AxedaCorp | 0:a725e8eab383 | 445 | wrthdr=snprintf(tgtBuff, headerSz, "%s %s %s\r\n%s%s\r\n%s%s\r\n%s%s\r\n%s\r\n%s%s\r\n\r\n", HTTP_KEY[http_operation], resource, HTTP_KEY[httpver], HTTP_HEADER[HTTPH_HOST], hostname, HTTP_HEADER[HTTPH_TYPE], contentType, HTTP_HEADER[HTTPH_LENGTH], clength_tmp, HTTP_HEADER[HTTPH_CONNECTION], HTTP_HEADER[HTTPH_XCOUNT], xCount_tmp); |
AxedaCorp | 0:a725e8eab383 | 446 | } |
AxedaCorp | 0:a725e8eab383 | 447 | else { |
AxedaCorp | 0:a725e8eab383 | 448 | wrthdr=snprintf(tgtBuff, headerSz, "%s %s %s\r\n%s%s\r\n%s%s\r\n%s%s\r\n%s\r\n\r\n", HTTP_KEY[http_operation], resource, HTTP_KEY[httpver], HTTP_HEADER[HTTPH_HOST], hostname, HTTP_HEADER[HTTPH_TYPE], contentType, HTTP_HEADER[HTTPH_LENGTH], clength_tmp, HTTP_HEADER[HTTPH_CONNECTION]); |
AxedaCorp | 0:a725e8eab383 | 449 | |
AxedaCorp | 0:a725e8eab383 | 450 | } |
AxedaCorp | 0:a725e8eab383 | 451 | if(wrthdr>headerSz) { printDebug("http_send_post(): Buffer allocated for header was too small, truncated"); } |
AxedaCorp | 0:a725e8eab383 | 452 | |
AxedaCorp | 0:a725e8eab383 | 453 | return tgtBuff; |
AxedaCorp | 0:a725e8eab383 | 454 | } |
AxedaCorp | 0:a725e8eab383 | 455 | |
AxedaCorp | 0:a725e8eab383 | 456 | |
AxedaCorp | 0:a725e8eab383 | 457 | |
AxedaCorp | 0:a725e8eab383 | 458 | |
AxedaCorp | 0:a725e8eab383 | 459 | |
AxedaCorp | 0:a725e8eab383 | 460 |