Subversion Repositories IRC-Archiver

Rev

Rev 18 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
11 nishi 1
/* $Id: html.c 21 2024-08-30 11:16:04Z nishi $ */
2
 
3
#include "web_html.h"
4
 
13 nishi 5
#include "web_db.h"
6
 
11 nishi 7
#include "ia_util.h"
8
 
9
#include <time.h>
10
#include <stdio.h>
11
#include <stdlib.h>
12
#include <string.h>
13 nishi 13
#include <stdbool.h>
14 nishi 14
#include <sys/stat.h>
15
#include <dirent.h>
11 nishi 16
 
17
extern char* webroot;
13 nishi 18
extern const char* ircarc_version;
11 nishi 19
 
20
char* web_html_escape(const char* html) {
21
	char* str = malloc(strlen(html) * 5 + 1);
22
	int i;
23
	int incr = 0;
24
	for(i = 0; html[i] != 0; i++) {
25
		if(html[i] == '&') {
26
			str[incr++] = '&';
27
			str[incr++] = 'a';
28
			str[incr++] = 'm';
29
			str[incr++] = 'p';
30
			str[incr++] = ';';
31
		} else if(html[i] == '<') {
32
			str[incr++] = '&';
33
			str[incr++] = 'l';
34
			str[incr++] = 't';
35
			str[incr++] = ';';
36
		} else if(html[i] == '>') {
37
			str[incr++] = '&';
38
			str[incr++] = 'g';
39
			str[incr++] = 't';
40
			str[incr++] = ';';
41
		} else {
42
			str[incr++] = html[i];
43
			;
44
		}
45
	}
46
	str[incr] = 0;
47
	return str;
48
}
49
 
13 nishi 50
#define TAG(tagname, el, chr) \
51
	bool attr = true; \
52
	if(bufincr > 0) { \
53
		int k; \
54
		for(k = bufincr - 1; k >= 0; k--) { \
55
			if(buffer[k] == chr) { \
56
				attr = false; \
57
				int l; \
58
				buffer[k] = 0; \
59
				for(l = k; l < bufincr; l++) { \
60
					buffer[l] = buffer[l + 1]; \
61
				} \
62
				break; \
63
			} \
64
		} \
65
		if(!attr) bufincr--; \
66
	} \
67
	if(attr) { \
68
		buffer[bufincr++] = chr; \
69
		char* tmp = fmtmsg; \
70
		fmtmsg = ia_strcat4(tmp, "<" tagname, el, ">"); \
71
		free(tmp); \
72
	} else { \
73
		char* tmp = fmtmsg; \
74
		fmtmsg = ia_strcat(tmp, "</" tagname ">"); \
75
		free(tmp); \
76
	}
77
 
18 nishi 78
int mtimesort(const struct dirent** d1_, const struct dirent** d2_) {
14 nishi 79
	struct dirent* d1 = (struct dirent*)d1_;
80
	struct dirent* d2 = (struct dirent*)d2_;
81
	char* d1_path = ia_strcat3(webroot, "/", d1->d_name);
82
	char* d2_path = ia_strcat3(webroot, "/", d2->d_name);
83
	struct stat s1;
84
	struct stat s2;
85
	stat(d1_path, &s1);
86
	stat(d2_path, &s2);
87
	free(d1_path);
88
	free(d2_path);
89
	return s1.st_mtime - s2.st_mtime;
90
}
91
 
11 nishi 92
int web_html_generate(const char* name, web_range_t range) {
13 nishi 93
	time_t t = time(NULL);
11 nishi 94
	char* path = ia_strcat4(webroot, "/", name, ".html");
95
	FILE* f = fopen(path, "w");
96
	if(f != NULL) {
13 nishi 97
		entry_t** e = web_db_query(range);
98
 
99
		char date[512];
100
		struct tm* tm = gmtime(&t);
101
		strftime(date, sizeof(date), "%Y/%m/%d %H:%M:%S UTC", tm);
11 nishi 102
		char* htmlesc = web_html_escape(name);
103
		char* title = ia_strcat("Archive: ", htmlesc);
13 nishi 104
		free(htmlesc);
12 nishi 105
		fprintf(f, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n");
11 nishi 106
		fprintf(f, "<html>\n");
107
		fprintf(f, "	<head>\n");
108
		fprintf(f, "		<meta http-equiv=\"Content-Type\" content=\"text/html;charset=UTF-8\">\n");
109
		fprintf(f, "		<title>%s</title>\n", title);
13 nishi 110
		fprintf(f, "		<style type=\"text/css\">\n");
111
		fprintf(f, "		</style>\n");
11 nishi 112
		fprintf(f, "	</head>\n");
21 nishi 113
		fprintf(f, "	<body style=\"padding: 15px; margin: 0 auto; width: 910px;\">\n");
13 nishi 114
		fprintf(f, "		<div style=\"padding: 1px 0; margin: 0; text-align: center; background-color: #8080ff;\">\n");
12 nishi 115
		fprintf(f, "			<h1>%s</h1>\n", title);
116
		fprintf(f, "		</div>\n");
13 nishi 117
		fprintf(f, "		Archived at %s.<br>\n", date);
118
		htmlesc = web_html_escape(range.channel);
119
		fprintf(f, "		Channel: <code>%s</code><br>\n", htmlesc);
120
		free(htmlesc);
121
		fprintf(f, "		<hr>\n");
122
		fprintf(f, "		<a href=\"./\">Go back to index</a>\n");
123
		fprintf(f, "		<hr>\n");
124
		fprintf(f, "<div>\n");
125
		fprintf(f, "		<pre style=\"margin: 0; border: solid 1px black; padding: 10px; overflow: scroll; width: 678px; min-height: 900px; background-color: #d0d0ff; float: left;\"><code>");
126
		int i;
127
		int bgcolor = 0xd0d0ff;
128
		int fgcolor = 0x000000;
129
		for(i = 0; e[i] != NULL; i++) {
130
			time_t t = e[i]->time;
131
			struct tm* tm = gmtime(&t);
132
			char* escusr = web_html_escape(e[i]->username);
133
			char* escmsg = web_html_escape(e[i]->message);
134
			char date[512];
135
			strftime(date, 512, "%Y/%m/%d %H:%M:%S UTC", tm);
136
			char cbuf[2];
137
			cbuf[1] = 0;
138
			char* fmtmsg = ia_strdup("");
139
			int j;
140
			char buffer[512];
141
			memset(buffer, 0, 512);
142
			int bufincr = 0;
143
			for(j = 0; escmsg[j] != 0; j++) {
144
				if(escmsg[j] == 2) {
145
					TAG("b", "", 'B');
146
				} else if(escmsg[j] == 0x1d) {
147
					TAG("i", "", 'I');
148
				} else if(escmsg[j] == 0x1f) {
149
					TAG("u", "", 'U');
150
				} else if(escmsg[j] == 0x1e) {
151
					TAG("s", "", 'S');
152
				} else if(escmsg[j] == 0x16) {
153
					char fgt[32];
154
					char bgt[32];
155
					int _c = fgcolor;
156
					fgcolor = bgcolor;
157
					bgcolor = _c;
158
					sprintf(fgt, "#%06X", fgcolor);
159
					sprintf(bgt, "#%06X", bgcolor);
160
					char* _ = ia_strcat4(" style=\"background-color: ", bgt, "; color: ", fgt);
161
					char* style = ia_strcat(_, ";\"");
162
					free(_);
163
					TAG("span", style, 'R');
164
				} else if(escmsg[j] == 0x03) {
165
					j++;
166
					int k = j;
167
					for(; escmsg[j] != 0 && j < k + 2; j++) {
168
						if(!('0' <= escmsg[j] && escmsg[j] <= '9')) break;
169
					}
170
					if(escmsg[j] == ',') {
171
						j++;
172
						k = j;
173
						for(; escmsg[j] != 0 && j < k + 2; j++) {
174
							if(!('0' <= escmsg[j] && escmsg[j] <= '9')) break;
175
						}
176
					}
177
					j--;
178
				} else if(escmsg[j] == 0x0f) {
179
					int k;
180
					for(k = bufincr - 1; k >= 0; k--) {
181
						char c = buffer[k];
182
						if(c == 'B') {
183
							char* tmp = fmtmsg;
184
							fmtmsg = ia_strcat(tmp, "</b>");
185
							free(tmp);
186
						} else if(c == 'I') {
187
							char* tmp = fmtmsg;
188
							fmtmsg = ia_strcat(tmp, "</i>");
189
							free(tmp);
190
						} else if(c == 'U') {
191
							char* tmp = fmtmsg;
192
							fmtmsg = ia_strcat(tmp, "</u>");
193
							free(tmp);
194
						} else if(c == 'S') {
195
							char* tmp = fmtmsg;
196
							fmtmsg = ia_strcat(tmp, "</s>");
197
							free(tmp);
198
						} else if(c == 'R') {
199
							char* tmp = fmtmsg;
200
							fmtmsg = ia_strcat(tmp, "</span>");
201
							free(tmp);
202
						}
203
					}
204
					bufincr = 0;
205
					bgcolor = 0xd0d0ff;
206
					fgcolor = 0x000000;
207
				} else {
208
					cbuf[0] = escmsg[j];
209
					char* tmp;
210
					tmp = fmtmsg;
211
					fmtmsg = ia_strcat(tmp, cbuf);
212
					free(tmp);
213
				}
214
			}
215
			for(j = bufincr - 1; j >= 0; j--) {
216
				char c = buffer[j];
217
				if(c == 'B') {
218
					char* tmp = fmtmsg;
219
					fmtmsg = ia_strcat(tmp, "</b>");
220
					free(tmp);
221
				} else if(c == 'I') {
222
					char* tmp = fmtmsg;
223
					fmtmsg = ia_strcat(tmp, "</i>");
224
					free(tmp);
225
				} else if(c == 'U') {
226
					char* tmp = fmtmsg;
227
					fmtmsg = ia_strcat(tmp, "</u>");
228
					free(tmp);
229
				} else if(c == 'S') {
230
					char* tmp = fmtmsg;
231
					fmtmsg = ia_strcat(tmp, "</s>");
232
					free(tmp);
233
				} else if(c == 'R') {
234
					char* tmp = fmtmsg;
235
					fmtmsg = ia_strcat(tmp, "</span>");
236
					free(tmp);
237
				}
238
			}
239
			fprintf(f, "<span class=\"line\">[%s] &lt;%s&gt; %s</span>\n", date, escusr, fmtmsg);
240
			free(fmtmsg);
241
			free(escusr);
242
			free(escmsg);
243
			free(e[i]->username);
244
			free(e[i]->message);
245
			free(e[i]);
246
		}
247
		free(e);
248
		fprintf(f, "</code></pre>\n");
249
		fprintf(f, "		<div style=\"margin: 0; border: solid 1px black; width: 178px; min-height: 900px; padding: 10px; background-color: #d0d0ff; float: right; overflow: scroll\">Statistics<hr>%d messages</div>\n", i);
250
		fprintf(f, "		</div>\n");
251
		fprintf(f, "		<div style=\"clear: both\"></div>\n");
252
		fprintf(f, "		<hr>\n");
253
		fprintf(f, "		<a href=\"./\">Go back to index</a>\n");
254
		fprintf(f, "		<hr>\n");
255
		fprintf(f, "		<i>Generated by <a href=\"http://nishi.boats/ircarc\">IRC-Archiver</a> %s</i>\n", ircarc_version);
11 nishi 256
		fprintf(f, "	</body>\n");
257
		fprintf(f, "</html>\n");
258
		fclose(f);
259
		free(title);
260
	}
261
	free(path);
14 nishi 262
	path = ia_strcat(webroot, "/index.html");
263
	f = fopen(path, "w");
18 nishi 264
	if(f != NULL) {
14 nishi 265
		char* title = ia_strdup("Index");
18 nishi 266
		struct dirent** namelist;
14 nishi 267
		int n = scandir(webroot, &namelist, NULL, mtimesort);
268
		fprintf(f, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n");
269
		fprintf(f, "<html>\n");
270
		fprintf(f, "	<head>\n");
271
		fprintf(f, "		<meta http-equiv=\"Content-Type\" content=\"text/html;charset=UTF-8\">\n");
272
		fprintf(f, "		<title>%s</title>\n", title);
273
		fprintf(f, "		<style type=\"text/css\">\n");
274
		fprintf(f, "		</style>\n");
275
		fprintf(f, "	</head>\n");
276
		fprintf(f, "	<body style=\"padding: 15px; margin: 0 auto; width: 900px;\">\n");
277
		fprintf(f, "		<div style=\"padding: 1px 0; margin: 0; text-align: center; background-color: #8080ff;\">\n");
278
		fprintf(f, "			<h1>%s</h1>\n", title);
279
		fprintf(f, "		</div>\n");
280
		fprintf(f, "		<hr>\n");
281
		fprintf(f, "		<table border=\"1\" style=\"width: 100%%;\">\n");
282
		fprintf(f, "			<tr>\n");
283
		fprintf(f, "				<th>Name</th><th style=\"width: 250px;\">Archived at</th><th style=\"width: 100px;\">Link</th>");
284
		fprintf(f, "			</tr>\n");
285
		int i;
18 nishi 286
		for(i = 0; i < n; i++) {
287
			if(strcmp(namelist[i]->d_name, "..") != 0 && strcmp(namelist[i]->d_name, ".") != 0 && strcmp(namelist[i]->d_name, "index.html") != 0) {
14 nishi 288
				struct stat s;
289
				char* np = ia_strcat3(webroot, "/", namelist[i]->d_name);
290
				stat(np, &s);
291
				struct tm* tm = gmtime(&s.st_mtime);
292
				char date[512];
293
				strftime(date, 512, "%Y/%m/%d %H:%M:%S UTC", tm);
294
				char* name = ia_strdup(namelist[i]->d_name);
295
				int j;
18 nishi 296
				for(j = strlen(name) - 1; j >= 0; j--) {
297
					if(name[j] == '.') {
14 nishi 298
						name[j] = 0;
299
						break;
300
					}
301
				}
302
				char* esc = web_html_escape(name);
303
				free(name);
304
				fprintf(f, "			<tr>\n");
305
				fprintf(f, "				<td>%s</td>", esc);
306
				fprintf(f, "				<td>%s</td>", date);
307
				fprintf(f, "				<td><a href=\"%s\">Link</a></td>", namelist[i]->d_name);
308
				fprintf(f, "			</tr>\n");
309
				free(esc);
310
			}
311
			free(namelist[i]);
312
		}
313
		free(namelist);
314
		fprintf(f, "		</table>\n");
315
		fprintf(f, "		<hr>\n");
316
		fprintf(f, "		<i>Generated by <a href=\"http://nishi.boats/ircarc\">IRC-Archiver</a> %s</i>\n", ircarc_version);
317
		fprintf(f, "	</body>\n");
318
		fprintf(f, "</html>\n");
319
		fclose(f);
320
		free(title);
321
	}
322
	free(path);
11 nishi 323
}