{"id":2144,"date":"2014-02-23T18:05:35","date_gmt":"2014-02-23T16:05:35","guid":{"rendered":"http:\/\/9v.lt\/blog\/?p=2144"},"modified":"2022-01-19T08:34:39","modified_gmt":"2022-01-19T06:34:39","slug":"sending-multipart-post-requests-c","status":"publish","type":"post","link":"https:\/\/9v.lt\/blog\/sending-multipart-post-requests-c\/","title":{"rendered":"Sending multipart POST requests with C++"},"content":{"rendered":"<p>Finally I had the time to revisit it again. Many moons ago I tried to understand how Multipart file upload works and kinda didn&#8217;t succeed, but then again I didn&#8217;t put that much effort into it. I did it now because I have a project on my hands which requires this, whole theory didn&#8217;t seem very hard and I was able to produce working code for client and server sides in a few hours.<br \/>\n<!--more--><br \/>\nSending multipart requests is great for file uploading and other large quantities of data. I won&#8217;t go about and write a tutorial on how to send these requests, instead I will point you to an <a href=\"http:\/\/www.ietf.org\/rfc\/rfc1867.txt\" target=\"_blank\" rel=\"noopener noreferrer\">awesome RFC<\/a> and <a href=\"http:\/\/www.tutorialspoint.com\/http\/http_header_fields.htm\" target=\"_blank\" rel=\"noopener noreferrer\">this tutorial<\/a>.<\/p>\n<p>While browsing I had found a working example done in Python. Cannot remember where I found it or to who it belongs, but anyway, that example code can be found <a href=\"http:\/\/9v.lt\/projects\/python\/PythonicMultiPartSender\/send_multipart.py\" target=\"_blank\" rel=\"noopener noreferrer\">in my projects folder here<\/a>.<\/p>\n<p>And so based on the docs and that example code I was able to produce a CPP project for my own needs. I tried to make it very simple to adapt for other projects. The project can be <a href=\"http:\/\/9v.lt\/projects\/C\/MultiPartSender\/main.cpp\" target=\"_blank\" rel=\"noopener noreferrer\">found here<\/a>, and along with it I made a <a href=\"http:\/\/9v.lt\/projects\/C\/MultiPartSender\/MultiPartSender.zip\" target=\"_blank\" rel=\"noopener noreferrer\">simple PHP script<\/a> that receives the commands and acts upon them, it&#8217;s also a part of other bigger project&#8230;<\/p>\n<p>For those who only want a quick review, here&#8217;s my C++ code. C++ and PHP codes assume your data is already Base64 encoded:<\/p>\n<pre lang=\"cpp\">\r\n\/*\r\n    Author: Kulverstukas\r\n    Date: 2014.02.23\r\n    Website: http:\/\/9v.lt; Evilzone.org\r\n    Description:\r\n        Simple prototype of Multipart POST sending in C++\r\n*\/\r\n\r\n#include <iostream>\r\n#include <winsock2.h>\r\n#include <string>\r\n#include <fstream>\r\n#include \"windows.h\"\r\n#include \"stdio.h\"\r\n\r\nusing namespace std;\r\n\r\n#define PORT       80\r\n#define IP         \"127.0.0.1\"\r\n#define HOST       \"locahost\"\r\n#define RECEIVER   \"\/receive_data.php\"\r\n#define COMPNAME   \"compname\"\r\n#define PROGRAM    \"program\"\r\n#define FILENAME   \"file\"\r\n#define BOUNDARY   \"----------Evil_B0uNd4Ry_$\"\r\n#define DUMMY_DATA \"c2FzYXNhc2FzZGRmZGZkY2Q=\"\r\n#define DUMMY_FILE \"dummy.txt\"\r\n\r\n\/\/------------------------------------\r\nstring constructBody(string args[2], string file[2]);\r\nstring readFile(string fileName);\r\n\/\/------------------------------------\r\n\r\nint main() {\r\n    \/\/ initiate the socket!\r\n    SOCKET dataSock;\r\n    WSADATA wsaData;\r\n    int error = WSAStartup(0x0202, &wsaData);\r\n    if (error != 0) {\r\n        WSACleanup();\r\n        exit(1);  \/\/ oh shit, this shouldn't happen!\r\n    }\r\n    \/\/ all internets, engage!\r\n    SOCKADDR_IN target;\r\n    target.sin_family = AF_INET;\r\n    target.sin_port = htons(PORT);\r\n    target.sin_addr.s_addr = inet_addr(IP);\r\n    dataSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);\r\n    if (dataSock == INVALID_SOCKET) {\r\n        exit(1); \/\/ Houston, we have a problem!\r\n    }\r\n    connect(dataSock, (SOCKADDR*)&target, sizeof(target));\r\n\r\n    string programNames[5][2] = {{\"KULVERTOP\", \"Chrome\"}, {\"KULVERTOP\", \"Firefox\"}, {\"KULVERTOP\", \"InternetExplorer\"}, {\"KULVERTOP\", \"Opera\"}, {\"KULVERTOP\", \"Skype\"}};\r\n    string file[2] = {FILENAME, \"Default.txt\"};\r\n\r\n    int a = sizeof(programNames)\/sizeof(programNames[0]);\r\n    for (int i = 0; i < a; i++) {\r\n        printf(\"Sending data for %s\\n\", (programNames[i][1]).c_str());\r\n        string body = constructBody(programNames[i], file);\r\n        char header[1024];\r\n        sprintf(header, \"POST %s HTTP 1.1\\r\\n\"\r\n                        \"Host: %s\\r\\n\"\r\n                        \"Content-Length: %d\\r\\n\"\r\n                        \"Connection: Keep-Alive\\r\\n\"\r\n                        \"Content-Type: multipart\/form-data; boundary=%s\\r\\n\"\r\n                        \"Accept-Charset: utf-8\\r\\n\\r\\n\", RECEIVER, IP, strlen(body.c_str()), BOUNDARY);\r\n\/\/        printf(\"%s\\n\\n\", header);\r\n        int p = send(dataSock, header, strlen(header), 0);\r\n\/\/        printf(\"p == %d\\n\", p);\r\n        int k = send(dataSock, body.c_str(), strlen(body.c_str()), 0);\r\n\/\/        printf(\"k == %d\\n\", k);\r\n\r\n\/\/        char buff[1024];\r\n\/\/        recv(dataSock, buff, 1024, 0);\r\n\/\/        printf(\"%s\\n\\n\", buff);\r\n    }\r\n\r\n    closesocket(dataSock);\r\n    WSACleanup();\r\n}\r\n\r\nstring readFile(string fileName) {\r\n    string fileContents;\r\n    ifstream tmp(fileName.c_str());\r\n    getline(tmp, fileContents);\r\n    tmp.close();\r\n\r\n    return fileContents;\r\n}\r\n\r\n\r\nstring constructBody(string args[2], string file[2]) {\r\n    string body;\r\n    string CRLF = \"\\r\\n\";\r\n\r\n    \/\/ first we add the args\r\n    body.append(\"--\"+string(BOUNDARY)+CRLF);\r\n    body.append(\"Content-Disposition: form-data; name=\\\"\"+string(COMPNAME)+\"\\\"\"+CRLF);\r\n    body.append(CRLF);\r\n    body.append(args[0]+CRLF);\r\n    body.append(\"--\"+string(BOUNDARY)+CRLF);\r\n    body.append(\"Content-Disposition: form-data; name=\\\"\"+string(PROGRAM)+\"\\\"\"+CRLF);\r\n    body.append(CRLF);\r\n    body.append(args[1]+CRLF);\r\n\r\n    \/\/ now we add the file\r\n    body.append(\"--\"+string(BOUNDARY)+CRLF);\r\n    body.append(\"Content-Disposition: form-data; name=\\\"\"+string(FILENAME)+\"\\\"; filename=\\\"\"+string(DUMMY_FILE)+\"\\\"\"+CRLF);\r\n    body.append(\"Content-Type: text\/plain\"+CRLF);\r\n    body.append(CRLF);\r\n    body.append(DUMMY_DATA+CRLF);\r\n    body.append(\"--\"+string(BOUNDARY)+\"--\"+CRLF);\r\n    body.append(CRLF);\r\n\r\n\/\/    printf(body.c_str()); exit(0);\r\n\r\n    return body;\r\n}\r\n\r\n<\/pre>\n<p>Python code:<\/p>\n<pre lang=\"python\">\r\nimport httplib, mimetypes\r\n\r\ndef post_multipart(host, selector, fields, files):\r\n    \"\"\"\r\n    Post fields and files to an http host as multipart\/form-data.\r\n    fields is a sequence of (name, value) elements for regular form fields.\r\n    files is a sequence of (name, filename, value) elements for data to be uploaded as files\r\n    Return the server's response page.\r\n    \"\"\"\r\n    content_type, body = encode_multipart_formdata(fields, files)\r\n    h = httplib.HTTP(host)\r\n    h.putrequest('POST', selector)\r\n    h.putheader('content-type', content_type)\r\n    h.putheader('content-length', str(len(body)))\r\n    h.endheaders()\r\n    h.send(body)\r\n    errcode, errmsg, headers = h.getreply()\r\n    return h.file.read()\r\n\r\ndef encode_multipart_formdata(fields, files):\r\n    \"\"\"\r\n    fields is a sequence of (name, value) elements for regular form fields.\r\n    files is a sequence of (name, filename, value) elements for data to be uploaded as files\r\n    Return (content_type, body) ready for httplib.HTTP instance\r\n    \"\"\"\r\n    BOUNDARY = '----------ThIs_Is_tHe_bouNdaRY_$'\r\n    CRLF = '\\r\\n'\r\n    L = []\r\n    for (key, value) in fields:\r\n        L.append('--' + BOUNDARY)\r\n        L.append('Content-Disposition: form-data; name=\"%s\"' % key)\r\n        L.append('')\r\n        L.append(value)\r\n    for (key, filename, value) in files:\r\n        L.append('--' + BOUNDARY)\r\n        L.append('Content-Disposition: form-data; name=\"%s\"; filename=\"%s\"' % (key, filename))\r\n        L.append('Content-Type: %s' % get_content_type(filename))\r\n        L.append('')\r\n        L.append(value)\r\n    L.append('--' + BOUNDARY + '--')\r\n    L.append('')\r\n    body = CRLF.join(L)\r\n    content_type = 'multipart\/form-data; boundary=%s' % BOUNDARY\r\n    return content_type, body\r\n\r\ndef get_content_type(filename):\r\n    return mimetypes.guess_type(filename)[0] or 'application\/octet-stream'\r\n\r\n<\/pre>\n<p>And the PHP code that receives all your garbage:<\/p>\n<pre lang=\"php\">\r\n<?php\r\n\/* ===== CONSTANTS ===== *\/\r\n$ROOT_DIR = 'FILES';\r\n$COMPUTER_NAME = 'compname';\r\n$PROGRAM = 'program';\r\n$FILENAME = 'file';\r\n$CHUNK_SIZE = 1024;\r\n\/* ===================== *\/\r\n\r\n\/\/=====================================\r\n\/**\r\n\tFunction that gets current time and formats it into pretty looking date\r\n*\/\r\nfunction makeDate() {\r\n\treturn strftime('%Y-%m-%d, %H.%M');\r\n}\r\n\/\/=====================================\r\n\/\/ check here if the parameters are set. If it's not then it's safe to say some one is snooping around...\r\n if (isset($_POST[$COMPUTER_NAME], $_POST[$PROGRAM], $_FILES[$FILENAME])) {\r\n\t\/\/ construct a full path and create it\r\n\t$fullPath = $ROOT_DIR.'\\\\'.$_POST[$COMPUTER_NAME].'\\\\'.$_POST[$PROGRAM].'\\\\'.makeDate();\r\n\tmkdir($fullPath, 0777, true);\r\n\t\r\n\t\/\/ move the files and rename them as temporary\r\n\t$filename = $_FILES[$FILENAME]['name'];\r\n\tmove_uploaded_file(($_FILES[$FILENAME]['tmp_name']), $fullPath.'\\\\'.$filename.'.tmp');\r\n\t\r\n\t\/\/ decode received files\r\n\t$src = fopen($fullPath.'\\\\'.$filename.'.tmp', 'rb');\r\n\t$dst = fopen($fullPath.'\\\\'.$filename, 'wb');\r\n\twhile (!feof($src)) {\r\n\t\tfwrite($dst, base64_decode(fread($src, $CHUNK_SIZE)));\r\n\t}\r\n\tfclose($dst);\r\n\tfclose($src);\r\n\tunlink($fullPath.'\\\\'.$filename.'.tmp'); \/\/ remove the temp file after decoding it\r\n\t\r\n\techo 'OK!';\r\n} else {\r\n\t\/\/ if someone is snooping around...\r\n\techo '<html><center><img decoding=\"async\" src=\"umad.jpg\" \/><\/center><\/html>';\r\n\t\/\/ echo 'oh no :(';\r\n}\r\n\/\/=====================================\r\n?>\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Finally I had the time to revisit it again. Many moons ago I tried to<\/p>\n","protected":false},"author":2,"featured_media":2150,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[9,750],"tags":[848,652,879,880,881],"class_list":["post-2144","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-projects","category-software-projects","tag-cpp","tag-file","tag-multipart","tag-send","tag-upload"],"_links":{"self":[{"href":"https:\/\/9v.lt\/blog\/wp-json\/wp\/v2\/posts\/2144","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/9v.lt\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/9v.lt\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/9v.lt\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/9v.lt\/blog\/wp-json\/wp\/v2\/comments?post=2144"}],"version-history":[{"count":0,"href":"https:\/\/9v.lt\/blog\/wp-json\/wp\/v2\/posts\/2144\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/9v.lt\/blog\/wp-json\/wp\/v2\/media\/2150"}],"wp:attachment":[{"href":"https:\/\/9v.lt\/blog\/wp-json\/wp\/v2\/media?parent=2144"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/9v.lt\/blog\/wp-json\/wp\/v2\/categories?post=2144"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/9v.lt\/blog\/wp-json\/wp\/v2\/tags?post=2144"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}