/* multimedia pastebin cgi script Copyright (C) 2010 Johannes Schauer This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License 3 as published by the Free Software Foundation. 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 Affero General Public License for more details. For a copy of the GNU Affero General Public License see . */ /* this script is written as a cgi for a chrooted thttpd setup to statically build it with required libs compiled in do: gcc -static -Wall -o paste paste.c /usr/lib/libmagic.a /usr/lib/libz.a */ #include #include #include #include #include #include #include #include #define MAX_UPLOAD 8388608 #define MAGIC_FILE "magic.mgc" const char base64[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_' }; const char default_msg[] = "\n" "
\n"
"mister-muffin(1)                 MISTER-MUFFIN                 mister-muffin(1)\n"
"\n"
"NAME\n"
"       mister-muffin - multimedia command line pastebin\n"
"\n"
"SYNOPSIS\n"
"       <command> | curl -F 'arg=<-' http://mister-muffin.de/paste\n"
"\n"
"DESCRIPTION\n"
"       Inspired by sprunge(1) but being more powerful and only lacking\n"
"       syntax highlighting and the cool domain, this pastebin allows to upload\n"
"       files of any kind and adds a proper filename extension to filetypes\n"
"       known to it.\n"
"\n"
"       The maximum allowed Content-Length is set to 8388608 bytes.\n"
"\n"
"FILES\n"
"       /magic.mgc\n"
"              magic file used by this script to identify filetypes\n"
"\n"
"EXAMPLES\n"
"       ~$ cat bin/ching | curl -F 'arg=<-' http://mister-muffin.de/paste\n"
"          http://mister-muffin.de/p/b33f\n"
"       ~$ firefox http://mister-muffin.de/p/b33f\n"
"\n"
"WWW\n"
"       http://mister-muffin.de/p/b33f\n"
"              the AGPL3 c program source\n"
"       http://mister-muffin.de/post.htm\n"
"              a webinterface for non curl users\n"
"\n"
"AUTHORS\n"
"       Johannes 'josch' Schauer <josch at pyneo.org>\n"
"\n"
"COPYRIGHT\n"
"       Distributed under the terms of the GNU Affero General Public License 3\n"
"\n"
"SEE ALSO\n"
"       sprunge(1)\n"
"\n"
"                                  12 Feb 2010                  mister-muffin(1)\n"
"
\n"; static void print_default() { printf("Content-Type: text/html; charset=UTF-8\n\n"); printf("%s", default_msg); exit(0); } static void error(const char* format, ...) { va_list argptr; va_start(argptr, format); printf("Content-Type: text/plain; charset=UTF-8\n\n"); vfprintf(stdout, format, argptr); va_end(argptr); exit(0); } static char* get_ext(char *buffer, int len) { const char *mime; int ret; char *ext; magic_t cookie = magic_open(MAGIC_MIME_TYPE); if (cookie == NULL) { error("cannot magic_open: %s", strerror(errno)); } ret = magic_load(cookie, MAGIC_FILE); if (ret != 0) { error("cannot magic_load: %s", magic_error(cookie)); } mime = magic_buffer(cookie, buffer, len); if (mime == NULL) { fprintf(stderr, "magic == NULL :(\n"); magic_close(cookie); return ""; } if(!strcmp(mime, "image/png")) ext = ".png"; else if(!strcmp(mime, "image/jpeg")) ext = ".jpg"; else if(!strcmp(mime, "image/bmp")) ext = ".bmp"; else if(!strcmp(mime, "image/gif")) ext = ".gif"; else if(!strcmp(mime, "image/tiff")) ext = ".tiff"; else if(!strcmp(mime, "audio/midi")) ext = ".mid"; else if(!strcmp(mime, "audio/mpeg")) ext = ".mp3"; else if(!strcmp(mime, "application/pdf")) ext = ".pdf"; else if(!strcmp(mime, "application/postscript")) ext = ".ps"; else if(!strcmp(mime, "application/octet-stream")) ext = ".bin"; else if(!strcmp(mime, "application/zip")) ext = ".zip"; else if(!strcmp(mime, "application/x-dvi")) ext = ".dvi"; else if(!strcmp(mime, "application/x-javascript")) ext = ".js"; else if(!strcmp(mime, "application/x-latex")) ext = ".tex"; else if(!strcmp(mime, "application/x-ogg")) ext = ".ogg"; else if(!strcmp(mime, "application/x-bittorrent")) ext = ".torrent"; else if(!strcmp(mime, "video/mp4")) ext = ".mp4"; else if(!strcmp(mime, "video/quicktime")) ext = ".mov"; else if(!strcmp(mime, "video/mpeg")) ext = ".mpeg"; else if(!strcmp(mime, "video/x-msvideo")) ext = ".avi"; else if(!strcmp(mime, "text/plain")) ext = ".txt"; else if(!strcmp(mime, "text/css")) ext = ".css"; else if(!strcmp(mime, "text/xml")) ext = ".xml"; else if(!strcmp(mime, "text/html")) ext = ".html"; else ext = ""; magic_close(cookie); return ext; } static char* get_random_str(int n) { int i = 4; char *r; r = (char *)malloc((i+1)*sizeof(char)); r[i] = '\0'; for(;i>0;i--) { r[i-1] = base64[(n>>(6*(i-1)))&0x3f]; } return r; } static void write_postdata_to_file(int len, char **filename) { FILE *to; char buf[8192]; char *end_of_delim; char *end_of_header; int len_header, len_delim; int r = 0; // read one first block if(len > sizeof(buf)) { fread(buf, sizeof(char), sizeof(buf), stdin); } else { fread(buf, sizeof(char), len, stdin); } // read delimiter if((end_of_delim = strstr(buf, "\r\n")) == NULL) error("Bad Request - no delimiter\n"); else end_of_delim += 2; len_delim = end_of_delim - buf + 4; // read content-disposition if((end_of_header = strstr(end_of_delim, "\r\n\r\n")) == NULL) error("Bad Request - no content disposition\n"); else end_of_header += 4; len_header = end_of_header - buf; *filename = (char *)malloc(39*sizeof(char*)); srand(time(NULL)); do { r = rand(); if(len > sizeof(buf)) sprintf(*filename, "p/%s%s", get_random_str(r), get_ext(end_of_header, sizeof(buf)-len_header)); else sprintf(*filename, "p/%s%s", get_random_str(r), get_ext(end_of_header, len-len_header-len_delim)); } while(!access(*filename, F_OK)); to = fopen(*filename, "w+"); if (!to) error("could not open output file\n"); // write real content to file if(len > sizeof(buf)) { // write the chunk of data we just read but without the header fwrite(end_of_header, sizeof(char), sizeof(buf)-len_header, to); len -= sizeof(buf)+len_delim; // read all the other blocks while(len > sizeof(buf)) { fread(buf, sizeof(char), sizeof(buf), stdin); fwrite(buf, sizeof(char), sizeof(buf), to); len -= sizeof(buf); } // read last block fread(buf, sizeof(char), len, stdin); fwrite(buf, sizeof(char), len, to); fclose(to); // read the delimeter at the end but dont write it fread(buf, sizeof(char), len_delim, stdin); } else { // read all content fwrite(end_of_header, sizeof(char), len-len_header-len_delim, to); fclose(to); } } int main(int argc, char** argv) { char *clen; int input_len; char *filename; if (access(MAGIC_FILE, F_OK)) error("magic file "MAGIC_FILE" not accessible\n"); if((clen = getenv("CONTENT_LENGTH")) == NULL) { print_default(); } input_len = atoi(clen); if(input_len == 0) { print_default(); } if(input_len > MAX_UPLOAD) error("Request Entity Too Large\n"); write_postdata_to_file(input_len, &filename); printf("Content-Type: text/plain; charset=UTF-8\n\n"); printf("http://mister-muffin.de/%s\n", filename); return 0; }