/* ** JNetLib ** Copyright (C) Joshua Teitelbaum, sergent first class, 1014 army. ** Author: Joshua Teitelbaum ** File: httppost.cpp ** License: see jnetlib.h */ #ifdef _WIN32 #include #include #endif #include "netinc.h" #include "util.h" #include "httppost.h" #include #include static char *PREFIX = "--"; static char *NEWLINE = "\r\n"; /* ** Joshua Teitelbaum 1/15/2006 ** 1014 Army, Sergent First Class ** HTTP Post implementation employing HTTPGet as base class. */ /* ** _endstack ** when building the POST stack for a FORM, need a postamble */ void JNL_HTTPPost::_endstack() { std::string cd; cd = PREFIX; cd += m_boundary; cd += PREFIX; cd += NEWLINE; FILEStackObject fso; fso.m_otype = 2; fso.m_str = cd; m_filestack.push_back(fso); } /* ** prepares one field and puts it on the stack */ void JNL_HTTPPost::_preparefield(std::string &name) { std::string cd; cd = PREFIX; cd += m_boundary; cd += NEWLINE; cd += "content-disposition: form-data; name=\""; cd += name; cd += "\""; cd += NEWLINE; cd += NEWLINE; cd += m_fields[name]; cd += "\r\n"; FILEStackObject fso; fso.m_otype = 3; fso.m_str = cd; m_filestack.push_back(fso); } /* ** prepare all the fields, and push them on the stack */ void JNL_HTTPPost::_preparefieldstack() { std::map::iterator it; for(it = m_fields.begin(); it != m_fields.end(); it++) { std::string str = (*it).first; _preparefield(str); } } /* ** prepare files and put them on the stack */ void JNL_HTTPPost::_preparefilestack() { std::map::iterator it; for(it = m_paths.begin(); it != m_paths.end(); it++) { /* --boundary\r\n Content-Disposition: form-data; name=""; filename=""\r\n Content-Type: \r\n \r\n \r\n */ std::string cd ; cd = PREFIX; cd += m_boundary; cd += NEWLINE; cd += "content-disposition: form-data; name=\""; cd += (*it).first; cd += "\"; filename=\""; cd += m_filenames[(*it).first]; cd += "\""; cd += NEWLINE; cd += "content-type: "; cd += m_content_type[(*it).first]; cd += NEWLINE; cd += NEWLINE; FILEStackObject fso; fso.m_otype = 0; fso.m_str = cd; m_filestack.push_back(fso); fso.m_otype = 1; fso.m_str = (*it).first; m_filestack.push_back(fso); cd = NEWLINE; fso.m_otype = 2; fso.m_str = cd; m_filestack.push_back(fso); } } /* ** payload function, run. ** the tactic is this: ** if we can't write, do normal run. ** ** while there is stuff on the POST stack ** pop and item ** write to the buffers as much as we can ** if we can't write more, stop ** end while ** do normal run */ int JNL_HTTPPost::run() { bool stop = 0; std::map::iterator it; int ntowrite; int retval; do { if(m_con->send_bytes_available() <= 0) { /* ** rut roh, no buffa */ break; } if(m_filestack.size() <= 0) { /* ** Nothing to do */ break; } /* ** Get the current object off the stack */ switch(m_filestack.front().m_otype) { case 0: /*preamble*/ case 2: /*postamble*/ case 3: /*plain jane field*/ { ntowrite = m_filestack.front().m_str.length() - m_filestack.front().nwritten; if(m_con->send_bytes_available() < ntowrite) { ntowrite = m_con->send_bytes_available(); } if(ntowrite>0) { retval = m_con->send(m_filestack.front().m_str.c_str() + m_filestack.front().nwritten,ntowrite); if(retval < 0) { break; } m_filestack.front().nwritten += ntowrite; m_nwritten += ntowrite; if( m_filestack.front().nwritten == m_filestack.front().m_str.length()) { /* ** all done with this object */ m_filestack.pop_front(); } } else { retval = 0; stop = true; } } break; case 1: /*MEAT*/ { FILE *fp; int sresult; fp = fopen(m_paths[m_filestack.front().m_str].c_str(),"rb"); if(fp == NULL) { /* ** someone gave this process a hot foot? */ return -1; } sresult = fseek(fp,m_filestack.front().nwritten,SEEK_SET); if(sresult < 0) { /* ** someone gave this process a hot foot? */ fclose(fp); return -1; } if(((m_content_length[m_filestack.front().m_str] - m_filestack.front().nwritten)) > (unsigned long)m_con->send_bytes_available()) { ntowrite = m_con->send_bytes_available(); } else { ntowrite = (m_content_length[m_filestack.front().m_str] - m_filestack.front().nwritten); } char *b=NULL; b = (char *)malloc(ntowrite); if(b == NULL) { return -1; /* ** whiskey tango foxtrot */ } /* ** read from where we left off */ sresult = fread(b,1,ntowrite,fp); if(sresult != ntowrite) { /* ** hot foot with matches again */ if(b) { free(b); } fclose(fp); return -1; } fclose(fp); retval = m_con->send(b,ntowrite); free(b); if(retval < 0) { return retval; } m_filestack.front().nwritten += ntowrite; m_nwritten += ntowrite; if(m_filestack.front().nwritten == m_content_length[m_filestack.front().m_str]) { m_filestack.pop_front(); } } break; } } while(!stop); return JNL_HTTPGet::run(); } /* ** addfield, adds field */ int JNL_HTTPPost::addfield(char *name, char *val) { if(name == NULL || val == NULL) { return -1; } m_fields[name]=val; return 0; } /* ** addfile, adds file */ int JNL_HTTPPost::addfile(char *name, char *mimetype, char *filename, char *filepath) { struct stat buffer; FILE *fp=NULL; if(name==NULL || filepath == NULL || mimetype == NULL || filename == NULL || (!(fp=fopen(filepath,"rb")))) { return -1; } m_content_type[name] = mimetype; m_paths[name] = filepath; m_filenames[name] = filename; fstat(fileno(fp),&buffer); fclose(fp); m_content_length[name] = buffer.st_size; return 0; } /* ** After adding fields and files, get the content length */ int JNL_HTTPPost::contentlength() { std::map::iterator it; std::string tmp; int length = 0; // fields { for (it = m_fields.begin(); it != m_fields.end(); it++) { std::string name = (*it).first; std::string value = (*it).second; tmp = "--" + m_boundary + "\r\n" "content-disposition: form-data; name=\"" + name + "\"\r\n" "\r\n"; tmp += value + "\r\n"; length += tmp.size(); } } // files { for (it = m_filenames.begin(); it != m_filenames.end(); it++) { std::string name = (*it).first; std::string filename = (*it).second; long content_length = m_content_length[name]; std::string content_type = m_content_type[name]; tmp = "--" + m_boundary + "\r\n" "content-disposition: form-data; name=\"" + name + "\"; filename=\"" + filename + "\"\r\n" "content-type: " + content_type + "\r\n" "\r\n"; length += tmp.size(); length += content_length; length += 2; // crlf after file } } // end tmp = "--" + m_boundary + "--\r\n"; length += tmp.size(); return length; } void JNL_HTTPPost::_addformheader() { char *h; #ifdef _WIN32 srand(GetTickCount()); #else srand(time(NULL)); #endif int x; char *preamble = "content-type:multipart/form-data; boundary="; char *dashes = "----"; char *post_boundary = (char*)alloca(10+strlen(dashes)); memcpy(post_boundary,dashes,strlen(dashes)); for(x=strlen(dashes); x < (int)strlen(dashes)+9; x++) { post_boundary[x] = (char)(rand() % 9) + '0'; } post_boundary[x] = 0; h = (char *)alloca(strlen(preamble) + strlen(post_boundary) + 1); sprintf(h,"%s%s",preamble,post_boundary); addheader(h); m_boundary = post_boundary; }