diff options
| -rw-r--r-- | Makefile | 4 | ||||
| -rw-r--r-- | cgi.c | 154 | ||||
| -rw-r--r-- | main.c | 70 | ||||
| -rw-r--r-- | main.h | 67 |
4 files changed, 239 insertions, 56 deletions
| @@ -4,6 +4,8 @@ cflags.debug := -Wall -o0 -g -DDEBUG=1 | |||
| 4 | cflags.release := -os -s | 4 | cflags.release := -os -s |
| 5 | CFLAGS := ${cflags.${BUILD}} | 5 | CFLAGS := ${cflags.${BUILD}} |
| 6 | 6 | ||
| 7 | LIBS := -lcups | ||
| 8 | |||
| 7 | test: main | 9 | test: main |
| 8 | ./main | 10 | ./main |
| 9 | 11 | ||
| @@ -13,5 +15,5 @@ clean: | |||
| 13 | rm -f ./main | 15 | rm -f ./main |
| 14 | 16 | ||
| 15 | %: | 17 | %: |
| 16 | $(CC) $(CFLAGS) -o $@ $@.c | 18 | $(CC) $(CFLAGS) -o $@ *.c $(LIBS) |
| 17 | 19 | ||
| @@ -0,0 +1,154 @@ | |||
| 1 | /* __ _ | ||
| 2 | * ____ / /_ ____ ___ __ ___________ _(_) | ||
| 3 | * / __ \/ __ \/ __ `__ \/ / / / ___/ __ `/ / | ||
| 4 | * / /_/ / / / / / / / / / /_/ / /__/ /_/ / / | ||
| 5 | * \____/_/ /_/_/ /_/ /_/\__, /\___/\__, /_/ | ||
| 6 | * /____/ /____/ | ||
| 7 | * | ||
| 8 | * SPDX-License-Identifier: BSD-2-Clause-FreeBSD | ||
| 9 | * | ||
| 10 | * Copyright (c) 2021, Max Christian Pohle <max@coderonline.de> | ||
| 11 | * | ||
| 12 | * Redistribution and use in source and binary forms, with or without | ||
| 13 | * modification, are permitted provided that the following conditions | ||
| 14 | * are met: | ||
| 15 | * | ||
| 16 | * 1. Redistributions of source code must retain the above copyright | ||
| 17 | * notice, this list of conditions and the following disclaimer. | ||
| 18 | * | ||
| 19 | * 2. Redistributions in binary form must reproduce the above copyright | ||
| 20 | * notice, this list of conditions and the following disclaimer in the | ||
| 21 | * documentation and/or other materials provided with the distribution. | ||
| 22 | * | ||
| 23 | * {{{ DISCLAIMER | ||
| 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
| 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
| 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
| 27 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | ||
| 28 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||
| 29 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||
| 30 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||
| 31 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||
| 32 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| 33 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| 34 | * POSSIBILITY OF SUCH DAMAGE. | ||
| 35 | * }}} | ||
| 36 | */ | ||
| 37 | |||
| 38 | #include "main.h" | ||
| 39 | #include <cups/cups.h> | ||
| 40 | |||
| 41 | static const char * line1 = NULL, * line2 = NULL; | ||
| 42 | |||
| 43 | int print_on_correct_printer(void * user_data, unsigned flags, cups_dest_t * dest) { | ||
| 44 | if(!strstr(dest->name, user_data)) { | ||
| 45 | printf("> Wrong name: %s continue search\n", dest->name); | ||
| 46 | return 1; | ||
| 47 | } | ||
| 48 | |||
| 49 | if(!dest || !line1 || !line2) { | ||
| 50 | printf("FAILED: %p %p %p\n", dest, line1, line2); | ||
| 51 | return 1; | ||
| 52 | } | ||
| 53 | |||
| 54 | int job_id = 0; | ||
| 55 | int num_options = 0; | ||
| 56 | |||
| 57 | cups_option_t * options = NULL; | ||
| 58 | num_options = cupsAddOption(CUPS_MEDIA_TYPE, CUPS_MEDIA_TYPE_LABELS, num_options, &options); | ||
| 59 | num_options = cupsAddOption(CUPS_MEDIA, CUPS_MEDIA_SOURCE_MANUAL, num_options, &options); | ||
| 60 | num_options = cupsAddOption(CUPS_PRINT_QUALITY, CUPS_PRINT_QUALITY_HIGH, num_options, &options); | ||
| 61 | num_options = cupsAddOption(CUPS_MEDIA, "62x15mm", num_options, &options); | ||
| 62 | |||
| 63 | cups_dinfo_t * info = cupsCopyDestInfo(CUPS_HTTP_DEFAULT, dest); | ||
| 64 | |||
| 65 | if (cupsCreateDestJob(CUPS_HTTP_DEFAULT, dest, info, &job_id, "My Document", num_options, options) == IPP_STATUS_OK) | ||
| 66 | printf("Created job: %d\n", job_id); | ||
| 67 | else | ||
| 68 | printf("Unable to create job: %s\n", cupsLastErrorString()); | ||
| 69 | |||
| 70 | if (cupsStartDestDocument(CUPS_HTTP_DEFAULT, dest, info, job_id, "filename.pdf", CUPS_FORMAT_TEXT, num_options, options, 1) == HTTP_STATUS_CONTINUE) | ||
| 71 | { | ||
| 72 | cupsWriteRequestData(CUPS_HTTP_DEFAULT, line1, strlen(line1)); | ||
| 73 | cupsWriteRequestData(CUPS_HTTP_DEFAULT, "\n", 1); | ||
| 74 | cupsWriteRequestData(CUPS_HTTP_DEFAULT, line2, strlen(line2)); | ||
| 75 | |||
| 76 | if (cupsFinishDestDocument(CUPS_HTTP_DEFAULT, dest, info) == IPP_STATUS_OK) | ||
| 77 | puts("Document send succeeded."); | ||
| 78 | else | ||
| 79 | printf("Document send failed: %s\n", cupsLastErrorString()); | ||
| 80 | } | ||
| 81 | return 0; | ||
| 82 | } | ||
| 83 | |||
| 84 | void send_answer(Http_Header * http_header, int fd_socket) { | ||
| 85 | FILE * f = fdopen((size_t) fd_socket, "w"); | ||
| 86 | fputs("HTTP/1.0 200 OK\n", f); | ||
| 87 | fputs("content-type: text/html\n\n", f); | ||
| 88 | fflush(f); | ||
| 89 | |||
| 90 | char * printer_name = "Brother_QL-720NW"; | ||
| 91 | cupsEnumDests(CUPS_DEST_FLAGS_NONE, | ||
| 92 | 0, | ||
| 93 | NULL, | ||
| 94 | CUPS_PRINTER_VARIABLE, | ||
| 95 | CUPS_PRINTER_LOCAL, | ||
| 96 | print_on_correct_printer, | ||
| 97 | printer_name | ||
| 98 | ); | ||
| 99 | |||
| 100 | line1 = NULL; | ||
| 101 | line2 = NULL; | ||
| 102 | |||
| 103 | int file = open("index.html", O_RDONLY); | ||
| 104 | if(0 < file) { | ||
| 105 | struct stat stat; | ||
| 106 | fstat(file, &stat); | ||
| 107 | sendfile (fileno(f), file, NULL, stat.st_size); | ||
| 108 | } else { | ||
| 109 | if(http_header->url) // TODO: too dangerous to check that here, that is too late. | ||
| 110 | fprintf(f, "could not open file \"%s\"\n", &http_header->url[1]); | ||
| 111 | } | ||
| 112 | |||
| 113 | } | ||
| 114 | |||
| 115 | void send_answer_file(Http_Header * http_header, int fd_socket) { | ||
| 116 | |||
| 117 | FILE * f = fdopen((size_t) fd_socket, "w"); | ||
| 118 | fputs("HTTP/1.0 200 OK\n", f); | ||
| 119 | fputs("content-type: text/plain\n\n", f); | ||
| 120 | fflush(f); | ||
| 121 | |||
| 122 | int file = open(&http_header->url[1], O_RDONLY); | ||
| 123 | if(0 < file) { | ||
| 124 | struct stat stat; | ||
| 125 | fstat(file, &stat); | ||
| 126 | sendfile (fileno(f), file, NULL, stat.st_size); | ||
| 127 | } else { | ||
| 128 | if(http_header->url) // TODO: too dangerous to check that here, that is too late. | ||
| 129 | fprintf(f, "could not open file \"%s\"\n", &http_header->url[1]); | ||
| 130 | } | ||
| 131 | |||
| 132 | fclose(f); | ||
| 133 | return; | ||
| 134 | } | ||
| 135 | |||
| 136 | void next_part(Http_Header * http_header, const char * content, size_t content_size) { | ||
| 137 | if(!http_header->content_disposition) | ||
| 138 | return; | ||
| 139 | |||
| 140 | if(strstr(http_header->content_disposition, "name=\"line1\"")) { | ||
| 141 | line1 = content; | ||
| 142 | } else if(strstr(http_header->content_disposition, "name=\"line2\"")) { | ||
| 143 | line2 = content; | ||
| 144 | } | ||
| 145 | |||
| 146 | printf("> content_disposition: %s\n", http_header->content_disposition); | ||
| 147 | printf("> size: %ld\n", content_size); | ||
| 148 | fwrite(content, content_size, 1, stdout); | ||
| 149 | puts(""); | ||
| 150 | fflush(stdout); | ||
| 151 | return; | ||
| 152 | } | ||
| 153 | |||
| 154 | // modeline for vim: shiftwidth=2 tabstop=2 number foldmethod=marker | ||
| @@ -35,20 +35,7 @@ | |||
| 35 | * }}} | 35 | * }}} |
| 36 | */ | 36 | */ |
| 37 | 37 | ||
| 38 | // {{{ INCLUDES | 38 | #include "main.h" |
| 39 | #include <stdlib.h> | ||
| 40 | #include <stdio.h> | ||
| 41 | #include <string.h> | ||
| 42 | #include <unistd.h> | ||
| 43 | #include <sys/socket.h> | ||
| 44 | #include <err.h> | ||
| 45 | #include <errno.h> | ||
| 46 | #include <arpa/inet.h> | ||
| 47 | #include <fcntl.h> | ||
| 48 | #include <sys/sendfile.h> | ||
| 49 | #include <sys/stat.h> | ||
| 50 | // #include <pthread.h> // maybe later | ||
| 51 | // }}} | ||
| 52 | // {{{ MACROS | 39 | // {{{ MACROS |
| 53 | #define EWOULDBLOCK_DELAY 100 | 40 | #define EWOULDBLOCK_DELAY 100 |
| 54 | #define READ_BUFFER_LENGTH 9000 // jumboframe? | 41 | #define READ_BUFFER_LENGTH 9000 // jumboframe? |
| @@ -67,36 +54,7 @@ static inline int verbose(const char * format, ...) { | |||
| 67 | #endif | 54 | #endif |
| 68 | // }}} | 55 | // }}} |
| 69 | 56 | ||
| 70 | typedef struct { | 57 | static int read_everything(FILE * f_r, FILE * output) { |
| 71 | int newline_length; // lenght of one newline in bytes (\n has 1, CR/LF has 2) | ||
| 72 | char * method; // GET/POST or something like that | ||
| 73 | char * url; // request URL (e.g. /index.html) | ||
| 74 | char * boundary; // usually looks similar to ------1234 | ||
| 75 | size_t boundary_size; // size in bytes, calculated after first header complete; is an indicator for the first header | ||
| 76 | char * content_type; // sometimes 'text/html', also sometimes 'text/html; boundary=------1234' | ||
| 77 | char * content_disposition; // includes file names of uploaded files or field names with form-data (e.g. curl -F) | ||
| 78 | } Http_Header; | ||
| 79 | |||
| 80 | void send_answer(int fd_socket, Http_Header * http_header) { | ||
| 81 | FILE * f = fdopen((size_t) fd_socket, "w"); | ||
| 82 | fputs("HTTP/1.0 200 OK\n", f); | ||
| 83 | fputs("content-type: text/plain\n\n", f); | ||
| 84 | fflush(f); | ||
| 85 | |||
| 86 | int file = open(&http_header->url[1], O_RDONLY); | ||
| 87 | if(0 < file) { | ||
| 88 | struct stat stat; | ||
| 89 | fstat(file, &stat); | ||
| 90 | sendfile (fileno(f), file, NULL, stat.st_size); | ||
| 91 | } else { | ||
| 92 | if(http_header->url) // TODO: too dangerous to check that here, that is too late. | ||
| 93 | fprintf(f, "could not open file \"%s\"\n", &http_header->url[1]); | ||
| 94 | } | ||
| 95 | |||
| 96 | fclose(f); | ||
| 97 | } | ||
| 98 | |||
| 99 | int read_everything(FILE * f_r, FILE * output) { | ||
| 100 | const int read_buffer_length = READ_BUFFER_LENGTH; | 58 | const int read_buffer_length = READ_BUFFER_LENGTH; |
| 101 | char read_buffer[read_buffer_length]; | 59 | char read_buffer[read_buffer_length]; |
| 102 | for(size_t size = -2 ; ; size = fread(read_buffer, 1, read_buffer_length, f_r)) { | 60 | 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) { | |||
| 118 | return EXIT_SUCCESS; | 76 | return EXIT_SUCCESS; |
| 119 | } | 77 | } |
| 120 | 78 | ||
| 121 | void * next_customer(size_t new_socket) { | 79 | static void * next_customer(size_t new_socket) { |
| 122 | FILE * f_r = fdopen((size_t) new_socket, "r"); | 80 | FILE * f_r = fdopen((size_t) new_socket, "r"); |
| 123 | 81 | ||
| 124 | char * output_buffer = NULL; | 82 | char * output_buffer = NULL; |
| @@ -229,12 +187,16 @@ void * next_customer(size_t new_socket) { | |||
| 229 | end += strspn(end, "-"); | 187 | end += strspn(end, "-"); |
| 230 | if(0 == strncmp(end, http_header.boundary, http_header.boundary_size)) { | 188 | if(0 == strncmp(end, http_header.boundary, http_header.boundary_size)) { |
| 231 | size_t file_size = content_end - content_start; | 189 | size_t file_size = content_end - content_start; |
| 232 | DEBUG("> Content ends here, size of the last file is %ld.", file_size); | 190 | DEBUG("> Content ends here, size of the last file is %ld\n", file_size); |
| 191 | |||
| 192 | content_start[file_size + 1] = '\0'; | ||
| 193 | next_part(&http_header, content_start, file_size); | ||
| 233 | 194 | ||
| 234 | end += http_header.boundary_size; | 195 | end += http_header.boundary_size; |
| 235 | matchlen = strspn(end, "\r\n"); | 196 | matchlen = strspn(end, "\r\n"); |
| 236 | DEBUG("> end is at %p, matchlen is %ld\n", end, matchlen); | 197 | DEBUG("> end is at %p, matchlen is %ld\n", end, matchlen); |
| 237 | 198 | ||
| 199 | |||
| 238 | search = ":"; | 200 | search = ":"; |
| 239 | break; | 201 | break; |
| 240 | } else { | 202 | } else { |
| @@ -252,9 +214,9 @@ void * next_customer(size_t new_socket) { | |||
| 252 | start = end + matchlen; | 214 | start = end + matchlen; |
| 253 | } | 215 | } |
| 254 | 216 | ||
| 255 | DEBUG("> sending answer..."); | 217 | DEBUG("> sending answer...\n"); |
| 256 | send_answer(new_socket, &http_header); | 218 | send_answer(&http_header, new_socket); |
| 257 | DEBUG("> answer sent."); | 219 | DEBUG("> answer sent.\n"); |
| 258 | 220 | ||
| 259 | fclose(f_r); | 221 | fclose(f_r); |
| 260 | 222 | ||
| @@ -264,7 +226,7 @@ void * next_customer(size_t new_socket) { | |||
| 264 | return NULL; | 226 | return NULL; |
| 265 | } | 227 | } |
| 266 | 228 | ||
| 267 | int serve(int server_fd) | 229 | static int serve(int server_fd) |
| 268 | { | 230 | { |
| 269 | struct sockaddr_in address; | 231 | struct sockaddr_in address; |
| 270 | socklen_t address_len = sizeof(address); | 232 | socklen_t address_len = sizeof(address); |
| @@ -276,10 +238,8 @@ int serve(int server_fd) | |||
| 276 | DEBUG("> Client %ld is connected via port %d\n", new_socket, address.sin_port); | 238 | DEBUG("> Client %ld is connected via port %d\n", new_socket, address.sin_port); |
| 277 | 239 | ||
| 278 | // set non blocking mode... | 240 | // set non blocking mode... |
| 279 | fcntl( | 241 | fcntl(new_socket, F_SETFL, |
| 280 | (size_t) new_socket, | 242 | fcntl(new_socket, F_GETFL) | O_NONBLOCK |
| 281 | F_SETFL, | ||
| 282 | fcntl((size_t) new_socket, F_GETFL) | O_NONBLOCK | ||
| 283 | ); | 243 | ); |
| 284 | 244 | ||
| 285 | next_customer(new_socket); | 245 | next_customer(new_socket); |
| @@ -321,4 +281,4 @@ int main(const int argc, char const * argv[]) { | |||
| 321 | ; return EXIT_FAILURE; | 281 | ; return EXIT_FAILURE; |
| 322 | } | 282 | } |
| 323 | 283 | ||
| 324 | // vim: shiftwidth=2 tabstop=2 number foldmethod=marker | 284 | // modeline for vim: shiftwidth=2 tabstop=2 number foldmethod=marker |
| @@ -0,0 +1,67 @@ | |||
| 1 | /* __ _ | ||
| 2 | * ____ / /_ ____ ___ __ ___________ _(_) | ||
| 3 | * / __ \/ __ \/ __ `__ \/ / / / ___/ __ `/ / | ||
| 4 | * / /_/ / / / / / / / / / /_/ / /__/ /_/ / / | ||
| 5 | * \____/_/ /_/_/ /_/ /_/\__, /\___/\__, /_/ | ||
| 6 | * /____/ /____/ | ||
| 7 | * | ||
| 8 | * SPDX-License-Identifier: BSD-2-Clause-FreeBSD | ||
| 9 | * | ||
| 10 | * Copyright (c) 2021, Max Christian Pohle <max@coderonline.de> | ||
| 11 | * | ||
| 12 | * Redistribution and use in source and binary forms, with or without | ||
| 13 | * modification, are permitted provided that the following conditions | ||
| 14 | * are met: | ||
| 15 | * | ||
| 16 | * 1. Redistributions of source code must retain the above copyright | ||
| 17 | * notice, this list of conditions and the following disclaimer. | ||
| 18 | * | ||
| 19 | * 2. Redistributions in binary form must reproduce the above copyright | ||
| 20 | * notice, this list of conditions and the following disclaimer in the | ||
| 21 | * documentation and/or other materials provided with the distribution. | ||
| 22 | * | ||
| 23 | * {{{ DISCLAIMER | ||
| 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
| 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
| 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
| 27 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | ||
| 28 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||
| 29 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||
| 30 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||
| 31 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||
| 32 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| 33 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| 34 | * POSSIBILITY OF SUCH DAMAGE. | ||
| 35 | * }}} | ||
| 36 | */ | ||
| 37 | |||
| 38 | // {{{ INCLUDES | ||
| 39 | #include <stddef.h> | ||
| 40 | #include <stdlib.h> | ||
| 41 | #include <stdio.h> | ||
| 42 | #include <string.h> | ||
| 43 | #include <unistd.h> | ||
| 44 | #include <sys/socket.h> | ||
| 45 | #include <err.h> | ||
| 46 | #include <errno.h> | ||
| 47 | #include <arpa/inet.h> | ||
| 48 | #include <fcntl.h> | ||
| 49 | #include <sys/sendfile.h> | ||
| 50 | #include <sys/stat.h> | ||
| 51 | // #include <pthread.h> // maybe later | ||
| 52 | // }}} | ||
| 53 | |||
| 54 | typedef struct { | ||
| 55 | int newline_length; // lenght of one newline in bytes (\n has 1, CR/LF has 2) | ||
| 56 | char * method; // GET/POST or something like that | ||
| 57 | char * url; // request URL (e.g. /index.html) | ||
| 58 | char * boundary; // usually looks similar to ------1234 | ||
| 59 | size_t boundary_size; // size in bytes, calculated after first header complete; is an indicator for the first header | ||
| 60 | char * content_type; // sometimes 'text/html', also sometimes 'text/html; boundary=------1234' | ||
| 61 | char * content_disposition; // includes file names of uploaded files or field names with form-data (e.g. curl -F) | ||
| 62 | } Http_Header; | ||
| 63 | |||
| 64 | void next_part(Http_Header * http_header, const char * content, size_t content_size); | ||
| 65 | void send_answer(Http_Header * http_header, int fd_socket); | ||
| 66 | |||
| 67 | // modeline for vim: shiftwidth=2 tabstop=2 number foldmethod=marker | ||
