summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile4
-rw-r--r--cgi.c154
-rw-r--r--main.c70
-rw-r--r--main.h67
4 files changed, 239 insertions, 56 deletions
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
4cflags.release := -os -s 4cflags.release := -os -s
5CFLAGS := ${cflags.${BUILD}} 5CFLAGS := ${cflags.${BUILD}}
6 6
7LIBS := -lcups
8
7test: main 9test: 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
diff --git a/cgi.c b/cgi.c
new file mode 100644
index 0000000..76751fa
--- /dev/null
+++ b/cgi.c
@@ -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
41static const char * line1 = NULL, * line2 = NULL;
42
43int 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
84void 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
115void 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
136void 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
diff --git a/main.c b/main.c
index 3a0c5ca..8f1d3ef 100644
--- a/main.c
+++ b/main.c
@@ -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
70typedef struct { 57static 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
80void 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
99int 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
121void * next_customer(size_t new_socket) { 79static 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
267int serve(int server_fd) 229static 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
diff --git a/main.h b/main.h
new file mode 100644
index 0000000..2f66920
--- /dev/null
+++ b/main.h
@@ -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
54typedef 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
64void next_part(Http_Header * http_header, const char * content, size_t content_size);
65void send_answer(Http_Header * http_header, int fd_socket);
66
67// modeline for vim: shiftwidth=2 tabstop=2 number foldmethod=marker
..