summaryrefslogtreecommitdiff
path: root/src/client/HTTP.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/HTTP.cpp')
-rw-r--r--src/client/HTTP.cpp1076
1 files changed, 1076 insertions, 0 deletions
diff --git a/src/client/HTTP.cpp b/src/client/HTTP.cpp
new file mode 100644
index 0000000..7514515
--- /dev/null
+++ b/src/client/HTTP.cpp
@@ -0,0 +1,1076 @@
+/**
+ * Powder Toy - HTTP Library
+ *
+ * Copyright (c) 2008 - 2010 Stanislaw Skowronek.
+ * Copyright (c) 2010 Simon Robertshaw
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifndef MACOSX
+#include <malloc.h>
+#endif
+#include <time.h>
+#ifdef WIN32
+#define _WIN32_WINNT 0x0501
+//#include <iphlpapi.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#else
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#endif
+
+#include "Config.h"
+#include "HTTP.h"
+#include "MD5.h"
+
+#ifdef WIN32
+#define PERROR SOCKET_ERROR
+#define PERRNO WSAGetLastError()
+#define PEAGAIN WSAEWOULDBLOCK
+#define PEINTR WSAEINTR
+#define PEINPROGRESS WSAEINPROGRESS
+#define PEALREADY WSAEALREADY
+#define PCLOSE closesocket
+#else
+#define PERROR -1
+#define PERRNO errno
+#define PEAGAIN EAGAIN
+#define PEINTR EINTR
+#define PEINPROGRESS EINPROGRESS
+#define PEALREADY EALREADY
+#define PCLOSE close
+#endif
+
+static int http_up = 0;
+static long http_timeout = 15;
+static int http_use_proxy = 0;
+static struct sockaddr_in http_proxy;
+
+static char *mystrdup(char *s)
+{
+ char *x;
+ if (s)
+ {
+ x = (char *)malloc(strlen(s)+1);
+ strcpy(x, s);
+ return x;
+ }
+ return s;
+}
+
+static int splituri(char *uri, char **host, char **path)
+{
+ char *p=uri,*q,*x,*y;
+ if (!strncmp(p, "http://", 7))
+ p += 7;
+ q = strchr(p, '/');
+ if (!q)
+ q = p + strlen(p);
+ x = (char *)malloc(q-p+1);
+ if (*q)
+ y = mystrdup(q);
+ else
+ y = mystrdup("/");
+ strncpy(x, p, q-p);
+ x[q-p] = 0;
+ if (q==p || x[q-p-1]==':')
+ {
+ free(x);
+ free(y);
+ return 1;
+ }
+ *host = x;
+ *path = y;
+ return 0;
+}
+
+static char *getserv(char *host)
+{
+ char *q, *x = mystrdup(host);
+ q = strchr(x, ':');
+ if (q)
+ *q = 0;
+ return x;
+}
+
+static char *getport(char *host)
+{
+ char *p, *q;
+ q = strchr(host, ':');
+ if (q)
+ p = mystrdup(q+1);
+ else
+ p = mystrdup("80");
+ return p;
+}
+
+static int resolve(char *dns, char *srv, struct sockaddr_in *addr)
+{
+ struct addrinfo hnt, *res = 0;
+ if (http_use_proxy)
+ {
+ memcpy(addr, &http_proxy, sizeof(struct sockaddr_in));
+ return 0;
+ }
+ memset(&hnt, 0, sizeof(hnt));
+ hnt.ai_family = AF_INET;
+ hnt.ai_socktype = SOCK_STREAM;
+ if (getaddrinfo(dns, srv, &hnt, &res))
+ return 1;
+ if (res)
+ {
+ if (res->ai_family != AF_INET)
+ {
+ freeaddrinfo(res);
+ return 1;
+ }
+ memcpy(addr, res->ai_addr, res->ai_addrlen);
+ freeaddrinfo(res);
+ return 0;
+ }
+ return 1;
+}
+
+void http_init(char *proxy)
+{
+ char *host, *port;
+#ifdef WIN32
+ WSADATA wsadata;
+ if (!WSAStartup(MAKEWORD(2,2), &wsadata))
+ http_up = 1;
+#else
+ signal(SIGPIPE, SIG_IGN);
+ http_up = 1;
+#endif
+ if (proxy)
+ {
+ host = getserv(proxy);
+ port = getport(proxy);
+ if (resolve(host, port, &http_proxy))
+ http_up = 0;
+ else
+ http_use_proxy = 1;
+ free(host);
+ free(port);
+ }
+}
+
+void http_done(void)
+{
+#ifdef WIN32
+ WSACleanup();
+#endif
+ http_up = 0;
+}
+
+#define CHUNK 4096
+
+#define HTS_STRT 0
+#define HTS_RSLV 1
+#define HTS_CONN 2
+#define HTS_IDLE 3
+#define HTS_XMIT 4
+#define HTS_RECV 5
+#define HTS_DONE 6
+struct http_ctx
+{
+ int state;
+ time_t last;
+ int keep;
+ int ret;
+ char *host, *path;
+ char *thdr;
+ int thlen;
+ char *txd;
+ int txdl;
+ struct sockaddr_in addr;
+ char *tbuf;
+ int tlen, tptr;
+ char *hbuf;
+ int hlen, hptr;
+ char *rbuf;
+ int rlen, rptr;
+ int chunked, chunkhdr, rxtogo, contlen, cclose;
+ int fd;
+ char *fdhost;
+};
+void *http_async_req_start(void *ctx, char *uri, char *data, int dlen, int keep)
+{
+ struct http_ctx *cx = (http_ctx *)ctx;
+ if (!ctx)
+ {
+ ctx = calloc(1, sizeof(struct http_ctx));
+ cx = (http_ctx *)ctx;
+ cx->fd = PERROR;
+ }
+
+ if (!cx->hbuf)
+ {
+ cx->hbuf = (char *)malloc(256);
+ cx->hlen = 256;
+ }
+
+ if (!http_up)
+ {
+ cx->ret = 604;
+ cx->state = HTS_DONE;
+ return ctx;
+ }
+
+ if (cx->state!=HTS_STRT && cx->state!=HTS_IDLE)
+ {
+ fprintf(stderr, "HTTP: unclean request restart state.\n");
+ exit(1);
+ }
+
+ cx->keep = keep;
+ cx->ret = 600;
+ if (splituri(uri, &cx->host, &cx->path))
+ {
+ cx->ret = 601;
+ cx->state = HTS_DONE;
+ return ctx;
+ }
+ if (http_use_proxy)
+ {
+ free(cx->path);
+ cx->path = mystrdup(uri);
+ }
+ if (cx->fdhost && strcmp(cx->host, cx->fdhost))
+ {
+ free(cx->fdhost);
+ cx->fdhost = NULL;
+ PCLOSE(cx->fd);
+ cx->fd = PERROR;
+ cx->state = HTS_STRT;
+ }
+ if (data)
+ {
+ if (!dlen)
+ dlen = strlen(data);
+ cx->txd = (char*)malloc(dlen);
+ memcpy(cx->txd, data, dlen);
+ cx->txdl = dlen;
+ }
+ else
+ cx->txdl = 0;
+
+ cx->contlen = 0;
+ cx->chunked = 0;
+ cx->chunkhdr = 0;
+ cx->rxtogo = 0;
+ cx->cclose = 0;
+
+ cx->tptr = 0;
+ cx->tlen = 0;
+
+ cx->last = time(NULL);
+
+ return ctx;
+}
+
+void http_async_add_header(void *ctx, char *name, char *data)
+{
+ struct http_ctx *cx = (http_ctx *)ctx;
+ cx->thdr = (char *)realloc(cx->thdr, cx->thlen + strlen(name) + strlen(data) + 4);
+ cx->thlen += sprintf(cx->thdr+cx->thlen, "%s: %s\n", name, data);
+}
+
+static void process_header(struct http_ctx *cx, char *str)
+{
+ char *p;
+ if (cx->chunkhdr)
+ {
+ p = strchr(str, ';');
+ if (p)
+ *p = 0;
+ cx->rxtogo = strtoul(str, NULL, 16);
+ cx->chunkhdr = 0;
+ if (!cx->rxtogo)
+ cx->chunked = 0;
+ }
+ if (!str[0])
+ {
+ cx->rxtogo = cx->contlen;
+ cx->chunkhdr = cx->chunked;
+ if (!cx->contlen && !cx->chunked && cx->ret!=100)
+ cx->state = HTS_DONE;
+ return;
+ }
+ if (!strncmp(str, "HTTP/", 5))
+ {
+ p = strchr(str, ' ');
+ if (!p)
+ {
+ cx->ret = 603;
+ cx->state = HTS_DONE;
+ return;
+ }
+ p++;
+ cx->ret = atoi(p);
+ return;
+ }
+ if (!strncmp(str, "Content-Length: ", 16))
+ {
+ cx->contlen = atoi(str+16);
+ return;
+ }
+ if (!strcmp(str, "Transfer-Encoding: chunked"))
+ {
+ cx->chunked = 1;
+ return;
+ }
+ if (!strcmp(str, "Connection: close"))
+ {
+ cx->cclose = 1;
+ return;
+ }
+}
+
+static void process_byte(struct http_ctx *cx, char ch)
+{
+ if (cx->rxtogo)
+ {
+ cx->rxtogo--;
+
+ if (!cx->rbuf)
+ {
+ cx->rbuf = (char *)malloc(256);
+ cx->rlen = 256;
+ }
+ if (cx->rptr >= cx->rlen-1)
+ {
+ cx->rlen *= 2;
+ cx->rbuf = (char *)realloc(cx->rbuf, cx->rlen);
+ }
+ cx->rbuf[cx->rptr++] = ch;
+
+ if (!cx->rxtogo && !cx->chunked)
+ cx->state = HTS_DONE;
+ }
+ else
+ {
+ if (ch == '\n')
+ {
+ cx->hbuf[cx->hptr] = 0;
+ process_header(cx, cx->hbuf);
+ cx->hptr = 0;
+ }
+ else if (ch != '\r')
+ {
+ if (cx->hptr >= cx->hlen-1)
+ {
+ cx->hlen *= 2;
+ cx->hbuf = (char *)realloc(cx->hbuf, cx->hlen);
+ }
+ cx->hbuf[cx->hptr++] = ch;
+ }
+ }
+}
+
+int http_async_req_status(void *ctx)
+{
+ struct http_ctx *cx = (http_ctx *)ctx;
+ char *dns,*srv,buf[CHUNK];
+ int tmp, i;
+ time_t now = time(NULL);
+#ifdef WIN32
+ unsigned long tmp2;
+#endif
+
+ switch (cx->state)
+ {
+ case HTS_STRT:
+ dns = getserv(cx->host);
+ srv = getport(cx->host);
+ if (resolve(dns, srv, &cx->addr))
+ {
+ free(dns);
+ free(srv);
+ cx->state = HTS_DONE;
+ cx->ret = 602;
+ return 1;
+ }
+ free(dns);
+ free(srv);
+ cx->state = HTS_RSLV;
+ return 0;
+ case HTS_RSLV:
+ cx->state = HTS_CONN;
+ cx->last = now;
+ return 0;
+ case HTS_CONN:
+ if (cx->fd == PERROR)
+ {
+ cx->fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (cx->fd == PERROR)
+ goto fail;
+ cx->fdhost = mystrdup(cx->host);
+#ifdef WIN32
+ tmp2 = 1;
+ if (ioctlsocket(cx->fd, FIONBIO, &tmp2) == SOCKET_ERROR)
+ goto fail;
+#else
+ tmp = fcntl(cx->fd, F_GETFL);
+ if (tmp < 0)
+ goto fail;
+ if (fcntl(cx->fd, F_SETFL, tmp|O_NONBLOCK) < 0)
+ goto fail;
+#endif
+ }
+ if (!connect(cx->fd, (struct sockaddr *)&cx->addr, sizeof(cx->addr)))
+ cx->state = HTS_IDLE;
+#ifdef WIN32
+ else if (PERRNO==WSAEISCONN)
+ cx->state = HTS_IDLE;
+#endif
+#ifdef MACOSX
+ else if (PERRNO==EISCONN)
+ cx->state = HTS_IDLE;
+#endif
+ else if (PERRNO!=PEINPROGRESS && PERRNO!=PEALREADY
+#ifdef WIN32
+ && PERRNO!=PEAGAIN && PERRNO!=WSAEINVAL
+#endif
+ )
+ goto fail;
+ if (now-cx->last>http_timeout)
+ goto timeout;
+ return 0;
+ case HTS_IDLE:
+ if (cx->txdl)
+ {
+ // generate POST
+ cx->tbuf = (char *)malloc(strlen(cx->host) + strlen(cx->path) + 121 + cx->txdl + cx->thlen);
+ cx->tptr = 0;
+ cx->tlen = 0;
+ cx->tlen += sprintf(cx->tbuf+cx->tlen, "POST %s HTTP/1.1\n", cx->path);
+ cx->tlen += sprintf(cx->tbuf+cx->tlen, "Host: %s\n", cx->host);
+ if (!cx->keep)
+ cx->tlen += sprintf(cx->tbuf+cx->tlen, "Connection: close\n");
+ if (cx->thdr)
+ {
+ memcpy(cx->tbuf+cx->tlen, cx->thdr, cx->thlen);
+ cx->tlen += cx->thlen;
+ free(cx->thdr);
+ cx->thdr = NULL;
+ cx->thlen = 0;
+ }
+ cx->tlen += sprintf(cx->tbuf+cx->tlen, "Content-Length: %d\n", cx->txdl);
+#ifdef BETA
+ cx->tlen += sprintf(cx->tbuf+cx->tlen, "X-Powder-Version: %s%dB%d\n", IDENT_VERSION, SAVE_VERSION, MINOR_VERSION);
+#else
+ cx->tlen += sprintf(cx->tbuf+cx->tlen, "X-Powder-Version: %s%dS%d\n", IDENT_VERSION, SAVE_VERSION, MINOR_VERSION);
+#endif
+ cx->tlen += sprintf(cx->tbuf+cx->tlen, "\n");
+ memcpy(cx->tbuf+cx->tlen, cx->txd, cx->txdl);
+ cx->tlen += cx->txdl;
+ free(cx->txd);
+ cx->txd = NULL;
+ cx->txdl = 0;
+ }
+ else
+ {
+ // generate GET
+ cx->tbuf = (char *)malloc(strlen(cx->host) + strlen(cx->path) + 89 + cx->thlen);
+ cx->tptr = 0;
+ cx->tlen = 0;
+ cx->tlen += sprintf(cx->tbuf+cx->tlen, "GET %s HTTP/1.1\n", cx->path);
+ cx->tlen += sprintf(cx->tbuf+cx->tlen, "Host: %s\n", cx->host);
+ if (cx->thdr)
+ {
+ memcpy(cx->tbuf+cx->tlen, cx->thdr, cx->thlen);
+ cx->tlen += cx->thlen;
+ free(cx->thdr);
+ cx->thdr = NULL;
+ cx->thlen = 0;
+ }
+ if (!cx->keep)
+ cx->tlen += sprintf(cx->tbuf+cx->tlen, "Connection: close\n");
+#ifdef BETA
+ cx->tlen += sprintf(cx->tbuf+cx->tlen, "X-Powder-Version: %s%dB%d\n", IDENT_VERSION, SAVE_VERSION, MINOR_VERSION);
+#else
+ cx->tlen += sprintf(cx->tbuf+cx->tlen, "X-Powder-Version: %s%dS%d\n", IDENT_VERSION, SAVE_VERSION, MINOR_VERSION);
+#endif
+ cx->tlen += sprintf(cx->tbuf+cx->tlen, "\n");
+ }
+ cx->state = HTS_XMIT;
+ cx->last = now;
+ return 0;
+ case HTS_XMIT:
+ tmp = send(cx->fd, cx->tbuf+cx->tptr, cx->tlen-cx->tptr, 0);
+ if (tmp==PERROR && PERRNO!=PEAGAIN && PERRNO!=PEINTR)
+ goto fail;
+ if (tmp!=PERROR)
+ {
+ cx->tptr += tmp;
+ if (cx->tptr == cx->tlen)
+ {
+ cx->tptr = 0;
+ cx->tlen = 0;
+ if (cx->tbuf)
+ free(cx->tbuf);
+ cx->state = HTS_RECV;
+ }
+ cx->last = now;
+ }
+ if (now-cx->last>http_timeout)
+ goto timeout;
+ return 0;
+ case HTS_RECV:
+ tmp = recv(cx->fd, buf, CHUNK, 0);
+ if (tmp==PERROR && PERRNO!=PEAGAIN && PERRNO!=PEINTR)
+ goto fail;
+ if (tmp!=PERROR)
+ {
+ for (i=0; i<tmp; i++)
+ {
+ process_byte(cx, buf[i]);
+ if (cx->state == HTS_DONE)
+ return 1;
+ }
+ cx->last = now;
+ }
+ if (now-cx->last>http_timeout)
+ goto timeout;
+ return 0;
+ case HTS_DONE:
+ return 1;
+ }
+ return 0;
+
+fail:
+ cx->ret = 600;
+ cx->state = HTS_DONE;
+ return 1;
+
+timeout:
+ cx->ret = 605;
+ cx->state = HTS_DONE;
+ return 1;
+}
+
+char *http_async_req_stop(void *ctx, int *ret, int *len)
+{
+ struct http_ctx *cx = (http_ctx *)ctx;
+ char *rxd;
+
+ if (cx->state != HTS_DONE)
+ while (!http_async_req_status(ctx)) ;
+
+ if (cx->host)
+ {
+ free(cx->host);
+ cx->host = NULL;
+ }
+ if (cx->path)
+ {
+ free(cx->path);
+ cx->path = NULL;
+ }
+ if (cx->txd)
+ {
+ free(cx->txd);
+ cx->txd = NULL;
+ cx->txdl = 0;
+ }
+ if (cx->hbuf)
+ {
+ free(cx->hbuf);
+ cx->hbuf = NULL;
+ }
+ if (cx->thdr)
+ {
+ free(cx->thdr);
+ cx->thdr = NULL;
+ cx->thlen = 0;
+ }
+
+ if (ret)
+ *ret = cx->ret;
+ if (len)
+ *len = cx->rptr;
+ if (cx->rbuf)
+ cx->rbuf[cx->rptr] = 0;
+ rxd = cx->rbuf;
+ cx->rbuf = NULL;
+ cx->rlen = 0;
+ cx->rptr = 0;
+ cx->contlen = 0;
+
+ if (!cx->keep)
+ http_async_req_close(ctx);
+ else if (cx->cclose)
+ {
+ PCLOSE(cx->fd);
+ cx->fd = PERROR;
+ if (cx->fdhost)
+ {
+ free(cx->fdhost);
+ cx->fdhost = NULL;
+ }
+ cx->state = HTS_STRT;
+ }
+ else
+ cx->state = HTS_IDLE;
+
+ return rxd;
+}
+
+void http_async_get_length(void *ctx, int *total, int *done)
+{
+ struct http_ctx *cx = (http_ctx *)ctx;
+ if (done)
+ *done = cx->rptr;
+ if (total)
+ *total = cx->contlen;
+}
+
+void http_async_req_close(void *ctx)
+{
+ struct http_ctx *cx = (http_ctx *)ctx;
+ void *tmp;
+ if (cx->host)
+ {
+ cx->keep = 1;
+ tmp = http_async_req_stop(ctx, NULL, NULL);
+ if (tmp)
+ free(tmp);
+ }
+ if (cx->fdhost)
+ free(cx->fdhost);
+ PCLOSE(cx->fd);
+ free(ctx);
+}
+
+char *http_simple_get(char *uri, int *ret, int *len)
+{
+ void *ctx = http_async_req_start(NULL, uri, NULL, 0, 0);
+ if (!ctx)
+ {
+ if (ret)
+ *ret = 600;
+ if (len)
+ *len = 0;
+ return NULL;
+ }
+ return http_async_req_stop(ctx, ret, len);
+}
+static char hex[] = "0123456789abcdef";
+void http_auth_headers(void *ctx, char *user, char *pass, char *session_id)
+{
+ char *tmp;
+ int i;
+ unsigned char hash[16];
+ unsigned int m;
+ struct md5_context md5;
+
+ if (user)
+ {
+ if (pass)
+ {
+ md5_init(&md5);
+ md5_update(&md5, (unsigned char *)user, strlen(user));
+ md5_update(&md5, (unsigned char *)"-", 1);
+ m = 0;
+
+ md5_update(&md5, (unsigned char *)pass, strlen(pass));
+ md5_final(hash, &md5);
+ tmp = (char *)malloc(33);
+ for (i=0; i<16; i++)
+ {
+ tmp[i*2] = hex[hash[i]>>4];
+ tmp[i*2+1] = hex[hash[i]&15];
+ }
+ tmp[32] = 0;
+ http_async_add_header(ctx, "X-Auth-Hash", tmp);
+ free(tmp);
+ }
+ if (session_id)
+ {
+ http_async_add_header(ctx, "X-Auth-User-Id", user);
+ http_async_add_header(ctx, "X-Auth-Session-Key", session_id);
+ }
+ else
+ {
+ http_async_add_header(ctx, "X-Auth-User", user);
+ }
+ }
+}
+char *http_auth_get(char *uri, char *user, char *pass, char *session_id, int *ret, int *len)
+{
+ void *ctx = http_async_req_start(NULL, uri, NULL, 0, 0);
+
+ if (!ctx)
+ {
+ if (ret)
+ *ret = 600;
+ if (len)
+ *len = 0;
+ return NULL;
+ }
+ return http_async_req_stop(ctx, ret, len);
+}
+
+char *http_simple_post(char *uri, char *data, int dlen, int *ret, int *len)
+{
+ void *ctx = http_async_req_start(NULL, uri, data, dlen, 0);
+ if (!ctx)
+ {
+ if (ret)
+ *ret = 600;
+ if (len)
+ *len = 0;
+ return NULL;
+ }
+ return http_async_req_stop(ctx, ret, len);
+}
+
+char *http_ret_text(int ret)
+{
+ switch (ret)
+ {
+ case 100:
+ return "Continue";
+ case 101:
+ return "Switching Protocols";
+ case 102:
+ return "Processing";
+
+ case 200:
+ return "OK";
+ case 201:
+ return "Created";
+ case 202:
+ return "Accepted";
+ case 203:
+ return "Non-Authoritative Information";
+ case 204:
+ return "No Content";
+ case 205:
+ return "Reset Content";
+ case 206:
+ return "Partial Content";
+ case 207:
+ return "Multi-Status";
+
+ case 300:
+ return "Multiple Choices";
+ case 301:
+ return "Moved Permanently";
+ case 302:
+ return "Found";
+ case 303:
+ return "See Other";
+ case 304:
+ return "Not Modified";
+ case 305:
+ return "Use Proxy";
+ case 306:
+ return "Switch Proxy";
+ case 307:
+ return "Temporary Redirect";
+
+ case 400:
+ return "Bad Request";
+ case 401:
+ return "Unauthorized";
+ case 402:
+ return "Payment Required";
+ case 403:
+ return "Forbidden";
+ case 404:
+ return "Not Found";
+ case 405:
+ return "Method Not Allowed";
+ case 406:
+ return "Not Acceptable";
+ case 407:
+ return "Proxy Authentication Required";
+ case 408:
+ return "Request Timeout";
+ case 409:
+ return "Conflict";
+ case 410:
+ return "Gone";
+ case 411:
+ return "Length Required";
+ case 412:
+ return "Precondition Failed";
+ case 413:
+ return "Request Entity Too Large";
+ case 414:
+ return "Request URI Too Long";
+ case 415:
+ return "Unsupported Media Type";
+ case 416:
+ return "Requested Range Not Satisfiable";
+ case 417:
+ return "Expectation Failed";
+ case 418:
+ return "I'm a teapot";
+ case 422:
+ return "Unprocessable Entity";
+ case 423:
+ return "Locked";
+ case 424:
+ return "Failed Dependency";
+ case 425:
+ return "Unordered Collection";
+ case 426:
+ return "Upgrade Required";
+ case 444:
+ return "No Response";
+ case 450:
+ return "Blocked by Windows Parental Controls";
+ case 499:
+ return "Client Closed Request";
+
+ case 500:
+ return "Internal Server Error";
+ case 501:
+ return "Not Implemented";
+ case 502:
+ return "Bad Gateway";
+ case 503:
+ return "Service Unavailable";
+ case 504:
+ return "Gateway Timeout";
+ case 505:
+ return "HTTP Version Not Supported";
+ case 506:
+ return "Variant Also Negotiates";
+ case 507:
+ return "Insufficient Storage";
+ case 509:
+ return "Bandwidth Limit Exceeded";
+ case 510:
+ return "Not Extended";
+
+ case 600:
+ return "Internal Client Error";
+ case 601:
+ return "Unsupported Protocol";
+ case 602:
+ return "Server Not Found";
+ case 603:
+ return "Malformed Response";
+ case 604:
+ return "Network Not Available";
+ case 605:
+ return "Request Timed Out";
+ default:
+ return "Unknown Status Code";
+ }
+}
+char *http_multipart_post(char *uri, char **names, char **parts, int *plens, char *user, char *pass, char *session_id, int *ret, int *len)
+{
+ void *ctx;
+ char *data = NULL, *tmp, *p;
+ int dlen = 0, i, j;
+ unsigned char hash[16];
+ unsigned char boundary[32], ch;
+ int blen = 0;
+ unsigned int map[62], m;
+ struct md5_context md5;
+ //struct md5_context md52;
+ int own_plen = 0;
+
+ if (names)
+ {
+ if (!plens)
+ {
+ own_plen = 1;
+ for (i=0; names[i]; i++) ;
+ plens = (int *)calloc(i, sizeof(int));
+ for (i=0; names[i]; i++)
+ plens[i] = strlen(parts[i]);
+ }
+
+retry:
+ if (blen >= 31)
+ goto fail;
+ memset(map, 0, 62*sizeof(int));
+ for (i=0; names[i]; i++)
+ {
+ for (j=0; j<plens[i]-blen; j++)
+ if (!blen || !memcmp(parts[i]+j, boundary, blen))
+ {
+ ch = parts[i][j+blen];
+ if (ch>='0' && ch<='9')
+ map[ch-'0']++;
+ else if (ch>='A' && ch<='Z')
+ map[ch-'A'+10]++;
+ else if (ch>='a' && ch<='z')
+ map[ch-'a'+36]++;
+ }
+ }
+ m = ~0;
+ j = 61;
+ for (i=0; i<62; i++)
+ if (map[i]<m)
+ {
+ m = map[i];
+ j = i;
+ }
+ if (j<10)
+ boundary[blen] = '0'+j;
+ else if (j<36)
+ boundary[blen] = 'A'+(j-10);
+ else
+ boundary[blen] = 'a'+(j-36);
+ blen++;
+ if (map[j])
+ goto retry;
+ boundary[blen] = 0;
+
+ for (i=0; names[i]; i++)
+ dlen += blen+strlen(names[i])+plens[i]+128;
+ dlen += blen+8;
+ data = (char *)malloc(dlen);
+ dlen = 0;
+ for (i=0; names[i]; i++)
+ {
+ dlen += sprintf(data+dlen, "--%s\r\n", boundary);
+ dlen += sprintf(data+dlen, "Content-transfer-encoding: binary\r\n");
+ if (strchr(names[i], ':'))
+ {
+ tmp = mystrdup(names[i]);
+ p = strchr(tmp, ':');
+ *p = 0;
+ dlen += sprintf(data+dlen, "content-disposition: form-data; name=\"%s\"; ", tmp);
+ free(tmp);
+ p = strchr(names[i], ':');
+ dlen += sprintf(data+dlen, "filename=\"%s\"\r\n\r\n", p+1);
+ }
+ else
+ dlen += sprintf(data+dlen, "content-disposition: form-data; name=\"%s\"\r\n\r\n", names[i]);
+ memcpy(data+dlen, parts[i], plens[i]);
+ dlen += plens[i];
+ dlen += sprintf(data+dlen, "\r\n");
+ }
+ dlen += sprintf(data+dlen, "--%s--\r\n", boundary);
+ }
+
+ ctx = http_async_req_start(NULL, uri, data, dlen, 0);
+ if (!ctx)
+ goto fail;
+
+ if (user)
+ {
+ //http_async_add_header(ctx, "X-Auth-User", user);
+ if (pass)
+ {
+ md5_init(&md5);
+ md5_update(&md5, (unsigned char *)user, strlen(user));
+ md5_update(&md5, (unsigned char *)"-", 1);
+ m = 0;
+ if (names)
+ {
+ for (i=0; names[i]; i++)
+ {
+ //md5_update(&md5, (unsigned char *)parts[i], plens[i]); //WHY?
+ //md5_update(&md5, (unsigned char *)"-", 1);
+ p = strchr(names[i], ':');
+ if (p)
+ m += (p - names[i]) + 1;
+ else
+ m += strlen(names[i])+1;
+ }
+
+ tmp = (char *)malloc(m);
+ m = 0;
+ for (i=0; names[i]; i++)
+ {
+ p = strchr(names[i], ':');
+ if (m)
+ {
+ tmp[m] = ' ';
+ m ++;
+ }
+ if (p)
+ {
+ memcpy(tmp+m, names[i], p-names[i]);
+ m += p - names[i];
+ }
+ else
+ {
+ strcpy(tmp+m, names[i]);
+ m += strlen(names[i]);
+ }
+ }
+ tmp[m] = 0;
+ http_async_add_header(ctx, "X-Auth-Objects", tmp);
+ free(tmp);
+ }
+
+ md5_update(&md5, (unsigned char *)pass, strlen(pass));
+ md5_final(hash, &md5);
+ tmp = (char *)malloc(33);
+ for (i=0; i<16; i++)
+ {
+ tmp[i*2] = hex[hash[i]>>4];
+ tmp[i*2+1] = hex[hash[i]&15];
+ }
+ tmp[32] = 0;
+ http_async_add_header(ctx, "X-Auth-Hash", tmp);
+ free(tmp);
+ }
+ if (session_id)
+ {
+ http_async_add_header(ctx, "X-Auth-User-Id", user);
+ http_async_add_header(ctx, "X-Auth-Session-Key", session_id);
+ }
+ else
+ {
+ http_async_add_header(ctx, "X-Auth-User", user);
+ }
+ }
+
+ if (data)
+ {
+ tmp = (char *)malloc(32+strlen((char *)boundary));
+ sprintf(tmp, "multipart/form-data, boundary=%s", boundary);
+ http_async_add_header(ctx, "Content-type", tmp);
+ free(tmp);
+ free(data);
+ }
+
+ if (own_plen)
+ free(plens);
+ return http_async_req_stop(ctx, ret, len);
+
+fail:
+ if (data)
+ free(data);
+ if (own_plen)
+ free(plens);
+ if (ret)
+ *ret = 600;
+ if (len)
+ *len = 0;
+ return NULL;
+}