From b7b47e662a75096c7f99881b0d609731d5d6c8ce Mon Sep 17 00:00:00 2001 From: Max Christian Pohle Date: Sun, 21 Nov 2021 00:10:29 +0100 Subject: First working version TODO: needs refactoring --- Makefile | 4 +- cgi.c | 154 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ main.c | 70 +++++++---------------------- main.h | 67 +++++++++++++++++++++++++++ 4 files changed, 239 insertions(+), 56 deletions(-) create mode 100644 cgi.c create mode 100644 main.h diff --git a/Makefile b/Makefile index bfb3df5..b15cafb 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,8 @@ cflags.debug := -Wall -o0 -g -DDEBUG=1 cflags.release := -os -s CFLAGS := ${cflags.${BUILD}} +LIBS := -lcups + test: main ./main @@ -13,5 +15,5 @@ clean: rm -f ./main %: - $(CC) $(CFLAGS) -o $@ $@.c + $(CC) $(CFLAGS) -o $@ *.c $(LIBS) diff --git a/cgi.c b/cgi.c new file mode 100644 index 0000000..76751fa --- /dev/null +++ b/cgi.c @@ -0,0 +1,154 @@ +/* __ _ +* ____ / /_ ____ ___ __ ___________ _(_) +* / __ \/ __ \/ __ `__ \/ / / / ___/ __ `/ / +* / /_/ / / / / / / / / / /_/ / /__/ /_/ / / +* \____/_/ /_/_/ /_/ /_/\__, /\___/\__, /_/ +* /____/ /____/ +* +* SPDX-License-Identifier: BSD-2-Clause-FreeBSD +* +* Copyright (c) 2021, Max Christian Pohle +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* {{{ DISCLAIMER +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* }}} +*/ + +#include "main.h" +#include + +static const char * line1 = NULL, * line2 = NULL; + +int print_on_correct_printer(void * user_data, unsigned flags, cups_dest_t * dest) { + if(!strstr(dest->name, user_data)) { + printf("> Wrong name: %s continue search\n", dest->name); + return 1; + } + + if(!dest || !line1 || !line2) { + printf("FAILED: %p %p %p\n", dest, line1, line2); + return 1; + } + + int job_id = 0; + int num_options = 0; + + cups_option_t * options = NULL; + num_options = cupsAddOption(CUPS_MEDIA_TYPE, CUPS_MEDIA_TYPE_LABELS, num_options, &options); + num_options = cupsAddOption(CUPS_MEDIA, CUPS_MEDIA_SOURCE_MANUAL, num_options, &options); + num_options = cupsAddOption(CUPS_PRINT_QUALITY, CUPS_PRINT_QUALITY_HIGH, num_options, &options); + num_options = cupsAddOption(CUPS_MEDIA, "62x15mm", num_options, &options); + + cups_dinfo_t * info = cupsCopyDestInfo(CUPS_HTTP_DEFAULT, dest); + + if (cupsCreateDestJob(CUPS_HTTP_DEFAULT, dest, info, &job_id, "My Document", num_options, options) == IPP_STATUS_OK) + printf("Created job: %d\n", job_id); + else + printf("Unable to create job: %s\n", cupsLastErrorString()); + + if (cupsStartDestDocument(CUPS_HTTP_DEFAULT, dest, info, job_id, "filename.pdf", CUPS_FORMAT_TEXT, num_options, options, 1) == HTTP_STATUS_CONTINUE) + { + cupsWriteRequestData(CUPS_HTTP_DEFAULT, line1, strlen(line1)); + cupsWriteRequestData(CUPS_HTTP_DEFAULT, "\n", 1); + cupsWriteRequestData(CUPS_HTTP_DEFAULT, line2, strlen(line2)); + + if (cupsFinishDestDocument(CUPS_HTTP_DEFAULT, dest, info) == IPP_STATUS_OK) + puts("Document send succeeded."); + else + printf("Document send failed: %s\n", cupsLastErrorString()); + } + return 0; +} + +void send_answer(Http_Header * http_header, int fd_socket) { + FILE * f = fdopen((size_t) fd_socket, "w"); + fputs("HTTP/1.0 200 OK\n", f); + fputs("content-type: text/html\n\n", f); + fflush(f); + + char * printer_name = "Brother_QL-720NW"; + cupsEnumDests(CUPS_DEST_FLAGS_NONE, + 0, + NULL, + CUPS_PRINTER_VARIABLE, + CUPS_PRINTER_LOCAL, + print_on_correct_printer, + printer_name + ); + + line1 = NULL; + line2 = NULL; + + int file = open("index.html", O_RDONLY); + if(0 < file) { + struct stat stat; + fstat(file, &stat); + sendfile (fileno(f), file, NULL, stat.st_size); + } else { + if(http_header->url) // TODO: too dangerous to check that here, that is too late. + fprintf(f, "could not open file \"%s\"\n", &http_header->url[1]); + } + +} + +void send_answer_file(Http_Header * http_header, int fd_socket) { + + FILE * f = fdopen((size_t) fd_socket, "w"); + fputs("HTTP/1.0 200 OK\n", f); + fputs("content-type: text/plain\n\n", f); + fflush(f); + + int file = open(&http_header->url[1], O_RDONLY); + if(0 < file) { + struct stat stat; + fstat(file, &stat); + sendfile (fileno(f), file, NULL, stat.st_size); + } else { + if(http_header->url) // TODO: too dangerous to check that here, that is too late. + fprintf(f, "could not open file \"%s\"\n", &http_header->url[1]); + } + + fclose(f); + return; +} + +void next_part(Http_Header * http_header, const char * content, size_t content_size) { + if(!http_header->content_disposition) + return; + + if(strstr(http_header->content_disposition, "name=\"line1\"")) { + line1 = content; + } else if(strstr(http_header->content_disposition, "name=\"line2\"")) { + line2 = content; + } + + printf("> content_disposition: %s\n", http_header->content_disposition); + printf("> size: %ld\n", content_size); + fwrite(content, content_size, 1, stdout); + puts(""); + fflush(stdout); + return; +} + +// modeline for vim: shiftwidth=2 tabstop=2 number foldmethod=marker diff --git a/main.c b/main.c index 3a0c5ca..8f1d3ef 100644 --- a/main.c +++ b/main.c @@ -35,20 +35,7 @@ * }}} */ -// {{{ INCLUDES -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -// #include // maybe later -// }}} +#include "main.h" // {{{ MACROS #define EWOULDBLOCK_DELAY 100 #define READ_BUFFER_LENGTH 9000 // jumboframe? @@ -67,36 +54,7 @@ static inline int verbose(const char * format, ...) { #endif // }}} -typedef struct { - int newline_length; // lenght of one newline in bytes (\n has 1, CR/LF has 2) - char * method; // GET/POST or something like that - char * url; // request URL (e.g. /index.html) - char * boundary; // usually looks similar to ------1234 - size_t boundary_size; // size in bytes, calculated after first header complete; is an indicator for the first header - char * content_type; // sometimes 'text/html', also sometimes 'text/html; boundary=------1234' - char * content_disposition; // includes file names of uploaded files or field names with form-data (e.g. curl -F) -} Http_Header; - -void send_answer(int fd_socket, Http_Header * http_header) { - FILE * f = fdopen((size_t) fd_socket, "w"); - fputs("HTTP/1.0 200 OK\n", f); - fputs("content-type: text/plain\n\n", f); - fflush(f); - - int file = open(&http_header->url[1], O_RDONLY); - if(0 < file) { - struct stat stat; - fstat(file, &stat); - sendfile (fileno(f), file, NULL, stat.st_size); - } else { - if(http_header->url) // TODO: too dangerous to check that here, that is too late. - fprintf(f, "could not open file \"%s\"\n", &http_header->url[1]); - } - - fclose(f); -} - -int read_everything(FILE * f_r, FILE * output) { +static int read_everything(FILE * f_r, FILE * output) { const int read_buffer_length = READ_BUFFER_LENGTH; char read_buffer[read_buffer_length]; for(size_t size = -2 ; ; size = fread(read_buffer, 1, read_buffer_length, f_r)) { @@ -118,7 +76,7 @@ int read_everything(FILE * f_r, FILE * output) { return EXIT_SUCCESS; } -void * next_customer(size_t new_socket) { +static void * next_customer(size_t new_socket) { FILE * f_r = fdopen((size_t) new_socket, "r"); char * output_buffer = NULL; @@ -229,12 +187,16 @@ void * next_customer(size_t new_socket) { end += strspn(end, "-"); if(0 == strncmp(end, http_header.boundary, http_header.boundary_size)) { size_t file_size = content_end - content_start; - DEBUG("> Content ends here, size of the last file is %ld.", file_size); + DEBUG("> Content ends here, size of the last file is %ld\n", file_size); + + content_start[file_size + 1] = '\0'; + next_part(&http_header, content_start, file_size); end += http_header.boundary_size; matchlen = strspn(end, "\r\n"); DEBUG("> end is at %p, matchlen is %ld\n", end, matchlen); + search = ":"; break; } else { @@ -252,9 +214,9 @@ void * next_customer(size_t new_socket) { start = end + matchlen; } - DEBUG("> sending answer..."); - send_answer(new_socket, &http_header); - DEBUG("> answer sent."); + DEBUG("> sending answer...\n"); + send_answer(&http_header, new_socket); + DEBUG("> answer sent.\n"); fclose(f_r); @@ -264,7 +226,7 @@ void * next_customer(size_t new_socket) { return NULL; } -int serve(int server_fd) +static int serve(int server_fd) { struct sockaddr_in address; socklen_t address_len = sizeof(address); @@ -276,10 +238,8 @@ int serve(int server_fd) DEBUG("> Client %ld is connected via port %d\n", new_socket, address.sin_port); // set non blocking mode... - fcntl( - (size_t) new_socket, - F_SETFL, - fcntl((size_t) new_socket, F_GETFL) | O_NONBLOCK + fcntl(new_socket, F_SETFL, + fcntl(new_socket, F_GETFL) | O_NONBLOCK ); next_customer(new_socket); @@ -321,4 +281,4 @@ int main(const int argc, char const * argv[]) { ; return EXIT_FAILURE; } -// vim: shiftwidth=2 tabstop=2 number foldmethod=marker +// modeline for vim: shiftwidth=2 tabstop=2 number foldmethod=marker diff --git a/main.h b/main.h new file mode 100644 index 0000000..2f66920 --- /dev/null +++ b/main.h @@ -0,0 +1,67 @@ +/* __ _ +* ____ / /_ ____ ___ __ ___________ _(_) +* / __ \/ __ \/ __ `__ \/ / / / ___/ __ `/ / +* / /_/ / / / / / / / / / /_/ / /__/ /_/ / / +* \____/_/ /_/_/ /_/ /_/\__, /\___/\__, /_/ +* /____/ /____/ +* +* SPDX-License-Identifier: BSD-2-Clause-FreeBSD +* +* Copyright (c) 2021, Max Christian Pohle +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* {{{ DISCLAIMER +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* }}} +*/ + +// {{{ INCLUDES +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include // maybe later +// }}} + +typedef struct { + int newline_length; // lenght of one newline in bytes (\n has 1, CR/LF has 2) + char * method; // GET/POST or something like that + char * url; // request URL (e.g. /index.html) + char * boundary; // usually looks similar to ------1234 + size_t boundary_size; // size in bytes, calculated after first header complete; is an indicator for the first header + char * content_type; // sometimes 'text/html', also sometimes 'text/html; boundary=------1234' + char * content_disposition; // includes file names of uploaded files or field names with form-data (e.g. curl -F) +} Http_Header; + +void next_part(Http_Header * http_header, const char * content, size_t content_size); +void send_answer(Http_Header * http_header, int fd_socket); + +// modeline for vim: shiftwidth=2 tabstop=2 number foldmethod=marker -- cgit v1.2.3