1/*
2 * Copyright 2012, Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Michael Lotz <mmlr@mlotz.ch>
7 */
8
9
10#include <debug.h>
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14
15extern "C" {
16#include "qrencode.h"
17#include "qrspec.h"
18}
19
20
21extern "C" {
22extern void abort_debugger_command();
23}
24
25
26static const char* kWebPostBaseURL = "http://mlotz.ch/q";
27
28static char sStringBuffer[16 * 1024];
29static char sEncodeBuffer[3 * 1024];
30static int sBufferPosition = 0;
31static int sQRCodeVersion = 19;
32static QRecLevel sQRCodeLevel = QR_ECLEVEL_L;
33static char sWebPostId[64];
34static int sWebPostCounter = 0;
35
36
37static bool
38qrcode_bit(QRcode* qrCode, int x, int y)
39{
40	if (x >= qrCode->width || y >= qrCode->width)
41		return false;
42
43	return (qrCode->data[y * qrCode->width + x] & 0x01) == 1;
44}
45
46
47static void
48move_to_and_clear_line(int line)
49{
50	kprintf(" \x1b[%dd\x1b[G\x1b[K", line + 1);
51}
52
53
54static bool
55print_qrcode(QRcode* qrCode, bool waitForKey)
56{
57	move_to_and_clear_line(0);
58	for (int y = 0; y < qrCode->width; y += 2) {
59		move_to_and_clear_line(y / 2 + 1);
60		kputs("  ");
61
62		for (int x = 0; x < qrCode->width; x++) {
63			bool upper = qrcode_bit(qrCode, x, y);
64			bool lower = qrcode_bit(qrCode, x, y + 1);
65			if (upper == lower)
66				kputs(upper ? "\x11" : " ");
67			else
68				kputs(upper ? "\x12" : "\x13");
69		}
70	}
71
72	move_to_and_clear_line(qrCode->width / 2 + 2);
73	move_to_and_clear_line(qrCode->width / 2 + 3);
74
75	if (waitForKey) {
76		kputs("press q to abort or any other key to continue...\x1b[1B\x1b[G");
77		if (kgetc() == 'q')
78			return false;
79	}
80
81	return true;
82}
83
84
85static int
86encode_url(const char* query, const char* data, int encodeLength,
87	int inputLength)
88{
89	sEncodeBuffer[0] = 0;
90	strlcat(sEncodeBuffer, kWebPostBaseURL, encodeLength + 1);
91	strlcat(sEncodeBuffer, "?i=", encodeLength + 1);
92	strlcat(sEncodeBuffer, sWebPostId, encodeLength + 1);
93	strlcat(sEncodeBuffer, "&", encodeLength + 1);
94	strlcat(sEncodeBuffer, query, encodeLength + 1);
95	int position = strlcat(sEncodeBuffer, "=", encodeLength + 1);
96	if (position > encodeLength)
97		return -1;
98
99	int copyCount = 0;
100	while (inputLength > 0 && position < encodeLength) {
101		char character = data[copyCount];
102		if ((character >= 'a' && character <= 'z')
103			|| (character >= 'A' && character <= 'Z')
104			|| (character >= '0' && character <= '9')
105			|| character == '.' || character == '-' || character == '_'
106			|| character == '~'
107			// These aren't strictly valid, but seem to work.
108			|| character == '/' || character == '(' || character == ')'
109			|| character == '=' || character == '^' || character == '?'
110			|| character == '|' || character == '*' || character == '@'
111			|| character == ';' || character == ':' || character == ','
112			|| character == '{' || character == '}' || character == '['
113			|| character == ']' || character == '<' || character == '>'
114			|| character == '!' || character == '\\') {
115			sEncodeBuffer[position++] = character;
116			sEncodeBuffer[position] = 0;
117		} else if (character == ' ') {
118			// Encode spaces as '+' as that's shorter than %20.
119			sEncodeBuffer[position++] = '+';
120			sEncodeBuffer[position] = 0;
121		} else {
122			// Encode to a %xx escape.
123			if (encodeLength - position < 3) {
124				// Doesn't fit anymore, we're done.
125				break;
126			}
127
128			char escaped[4];
129			sprintf(escaped, "%%%.2x", character);
130			position = strlcat(sEncodeBuffer, escaped, encodeLength + 1);
131		}
132
133		inputLength--;
134		copyCount++;
135	}
136
137	return copyCount;
138}
139
140
141static int
142qrencode(int argc, char* argv[])
143{
144	if (argc > 1 && strcmp(argv[1], "--help") == 0) {
145		kprintf("%s [<string>]\n", argv[0]);
146		kprintf("If an argument is given, encodes that string as a QR code.\n"
147			"Otherwise the current QR buffer is encoded as QR codes.\n"
148			"When encoding from the QR buffer, the buffer is left intact.\n"
149			"use qrclear to clear the QR buffer or qrflush to encode/clear.\n");
150		return 1;
151	}
152
153	const char* source = NULL;
154	if (argc < 2) {
155		sStringBuffer[sBufferPosition] = 0;
156		source = sStringBuffer;
157	} else
158		source = argv[1];
159
160	int inputLength = strlen(source);
161	int encodeLength = QRspec_getDataLength(sQRCodeVersion, sQRCodeLevel) - 3;
162	while (inputLength > 0) {
163		int copyCount = 0;
164		if (sWebPostId[0] != 0) {
165			copyCount = encode_url(sWebPostCounter++ == 0 ? "clear" : "d",
166				source, encodeLength, inputLength);
167			if (copyCount < 0) {
168				kprintf("Failed to URL encode buffer.\n");
169				QRcode_clearCache();
170				return 1;
171			}
172		} else {
173			copyCount = inputLength < encodeLength ? inputLength : encodeLength;
174			memcpy(sEncodeBuffer, source, copyCount);
175			sEncodeBuffer[copyCount] = 0;
176		}
177
178		QRcode* qrCode = QRcode_encodeString8bit(sEncodeBuffer, sQRCodeVersion,
179			sQRCodeLevel);
180		if (qrCode == NULL) {
181			kprintf("Failed to encode buffer into qr code.\n");
182			QRcode_clearCache();
183			return 1;
184		}
185
186		source += copyCount;
187		inputLength -= copyCount;
188
189		bool doContinue = print_qrcode(qrCode, inputLength > 0);
190		QRcode_free(qrCode);
191
192		if (!doContinue) {
193			QRcode_clearCache();
194			abort_debugger_command();
195				// Does not return.
196		}
197	}
198
199	QRcode_clearCache();
200	return 0;
201}
202
203
204static int
205qrclear(int argc, char* argv[])
206{
207	if (argc > 1 && strcmp(argv[1], "--help") == 0) {
208		kprintf("Clears the current QR buffer.\n");
209		return 0;
210	}
211
212	sBufferPosition = 0;
213	sStringBuffer[0] = 0;
214	return 0;
215}
216
217
218static int
219qrflush(int argc, char* argv[])
220{
221	if (argc > 1 && strcmp(argv[1], "--help") == 0) {
222		kprintf("Flushes the current QR buffer by encoding QR codes from\n"
223			"the data and then clears the QR buffer.\n");
224		return 1;
225	}
226
227	qrencode(0, NULL);
228	qrclear(0, NULL);
229	return 0;
230}
231
232
233static int
234qrappend(int argc, char* argv[])
235{
236	if (argc < 2 || (argc > 1 && strcmp(argv[1], "--help") == 0)) {
237		kprintf("%s <string>\n", argv[0]);
238		kprintf("Appends the given string to the QR buffer. Can be used as\n"
239			"the target of a pipe command to accumulate the output of other\n"
240			"commands in the QR buffer.\n"
241			"Note that this command will flush the QR buffer when it runs\n"
242			"full to make room for the new string. This will cause QR codes\n"
243			"to be generated while the append command still runs. As these\n"
244			"implicit flushes only happen to make room in the buffer, the\n"
245			"strings are afterwards appended aren't flushed, so make sure to\n"
246			"execute the qrflush command to generate codes for these as\n"
247			"well.\n");
248		return 1;
249	}
250
251	const char* source = argv[1];
252	int length = strlen(source) + 1;
253
254	while (length > 0) {
255		int copyCount = sizeof(sStringBuffer) - sBufferPosition - 1;
256		if (copyCount == 0) {
257			// The buffer is full, we need to flush it.
258			qrflush(0, NULL);
259		}
260
261		if (length < copyCount)
262			copyCount = length;
263
264		memcpy(sStringBuffer + sBufferPosition, source, copyCount);
265		sBufferPosition += copyCount;
266		source += copyCount;
267		length -= copyCount;
268	}
269
270	// Overwrite the 0 byte that was copied extra with a newline.
271	if (sBufferPosition > 0)
272		sStringBuffer[sBufferPosition - 1] = '\n';
273
274	return 0;
275}
276
277
278static int
279qrconfig(int argc, char* argv[])
280{
281	if (argc < 2 || (argc > 1 && strcmp(argv[1], "--help") == 0)) {
282		kprintf("%s <version>\n", argv[0]);
283		kprintf("Sets the QR version to be used. Valid versions range from 1\n"
284			"to 40 where each version determines the size of the QR code.\n"
285			"Version 1 is 21x21 in size and each version increments that size\n"
286			"by 4x4 up to 177x177 for version 40.\n"
287			"Bigger QR codes can hold more data, so ideally you use a version\n"
288			"that makes the QR code as big as possible while still fitting on\n"
289			"your screen.\n"
290			"You can test the new config by running qrencode\n"
291			"with a test string.\n"
292			"Note that due to memory constraints in the kernel debugger, some\n"
293			"of the higher versions may actually not work. The kernel\n"
294			"debugger heap size can be adjusted using the KDEBUG_HEAP define\n"
295			"in the kernel_debug_config.h header\n");
296		return 1;
297	}
298
299	int newVersion = atoi(argv[1]);
300	if (newVersion <= 0 || newVersion > 40) {
301		kprintf("Invalid QR code version supplied, "
302			"must be between 1 and 40.\n");
303		return 1;
304	}
305
306	sQRCodeVersion = newVersion;
307	return 0;
308}
309
310
311static int
312qrwebpost(int argc, char* argv[])
313{
314	if (argc >= 3 && strcmp(argv[1], "start") == 0) {
315		strlcpy(sWebPostId, argv[2], sizeof(sWebPostId));
316		sWebPostCounter = 0;
317
318		// Generate the clear code.
319		const char* args[2] = { "", "yes" };
320		qrencode(2, (char**)args);
321	} else if (argc >= 2 && strcmp(argv[1], "stop") == 0) {
322		sWebPostId[0] = 0;
323	} else {
324		kprintf("%s start <id>\n", argv[0]);
325		kprintf("%s stop\n", argv[0]);
326		kprintf("Causes the QR codes to be rendered as URLs that resolve to\n"
327			"a service that allows appending the data of multiple QR codes\n"
328			"for easier handling.\n"
329			"An initial QR code is generated that invokes a clear operation\n"
330			"to prepare the output file on the server.\n"
331			"Note that there is no logic behind the service, if you and\n"
332			"someone else use the same id at the same time, they will\n"
333			"overwrite eachother. Therefore use a reasonably unique name.\n");
334		return 1;
335	}
336
337	return 0;
338}
339
340
341static status_t
342std_ops(int32 op, ...)
343{
344	switch (op) {
345		case B_MODULE_INIT:
346			add_debugger_command("qrencode", qrencode,
347				"encodes a string / the current QR buffer");
348			add_debugger_command("qrappend", qrappend,
349				"appends a string to the QR buffer");
350			add_debugger_command("qrclear", qrclear,
351				"clears the current QR buffer");
352			add_debugger_command("qrflush", qrflush,
353				"encodes the current QR buffer and clears it");
354			add_debugger_command("qrconfig", qrconfig,
355				"sets the QR code version to be used");
356			add_debugger_command("qrwebpost", qrwebpost,
357				"sets up URL encoding for QR codes");
358			return B_OK;
359		case B_MODULE_UNINIT:
360			remove_debugger_command("qrencode", qrencode);
361			remove_debugger_command("qrappend", qrappend);
362			remove_debugger_command("qrclear", qrclear);
363			remove_debugger_command("qrflush", qrflush);
364			remove_debugger_command("qrconfig", qrconfig);
365			remove_debugger_command("qrwebpost", qrwebpost);
366			return B_OK;
367	}
368
369	return B_BAD_VALUE;
370}
371
372
373static struct debugger_module_info sModuleInfo = {
374	{
375		"debugger/qrencode/v1",
376		0,
377		&std_ops
378	},
379	NULL,
380	NULL,
381	NULL,
382	NULL
383};
384
385module_info *modules[] = {
386	(module_info *)&sModuleInfo,
387	NULL
388};
389
390