Subversion Repositories Okuu

Rev

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

Rev Author Line No. Line
3 nishi 1
/* $Id: news.c 13 2024-09-12 15:29:22Z nishi $ */
2
 
3
#define OK_NEWS_SRC
4
#include "ok_news.h"
5
 
6
#include "ok_util.h"
7
 
8
#include <stdio.h>
9
#include <stdbool.h>
10
#include <string.h>
11
#include <stdlib.h>
12
#include <stdint.h>
13
#include <sys/stat.h>
13 nishi 14
#include <errno.h>
3 nishi 15
 
5 nishi 16
#include <sys/socket.h>
17
#include <netinet/tcp.h>
18
#include <arpa/inet.h>
19
 
20
#ifdef __FreeBSD__
21
#include <netinet/in.h>
22
#endif
23
 
24
extern char* nntpserver;
25
extern char* nntpuser;
26
extern char* nntppass;
27
extern char* nntppath;
28
extern char* nntpfrom;
29
extern char* nntpgroup;
30
extern char* nntpcount;
31
extern int nntpport;
32
 
3 nishi 33
struct news_entry news_entry;
34
 
5 nishi 35
void ok_close(int sock);
36
 
11 nishi 37
void ok_news_init(void) {
3 nishi 38
	news_entry.from = NULL;
39
	news_entry.content = NULL;
40
}
41
 
11 nishi 42
int ok_news_read(const char* path) {
43
	if(news_entry.from != NULL) {
3 nishi 44
		free(news_entry.from);
45
		news_entry.from = NULL;
46
	}
11 nishi 47
	if(news_entry.content != NULL) {
3 nishi 48
		free(news_entry.content);
49
		news_entry.content = NULL;
50
	}
51
 
52
	struct stat s;
11 nishi 53
	if(stat(path, &s) == 0) {
3 nishi 54
		char* boundary = NULL;
55
		char* buffer = malloc(s.st_size + 1);
56
		buffer[s.st_size] = 0;
57
		FILE* f = fopen(path, "r");
58
		fread(buffer, s.st_size, 1, f);
59
 
60
		uint64_t i;
61
		bool newline = false;
62
		int incr = 0;
63
		char* l = malloc(1);
64
		l[0] = 0;
65
		bool header = true;
66
		bool ignore = false;
67
		bool bheader = false;
11 nishi 68
		for(i = 0; i < s.st_size; i++) {
69
			if(buffer[i] == '\r') {
70
				if(buffer[i + 1] == '\n') {
3 nishi 71
					/* newline */
72
					i++;
11 nishi 73
					if(!header) {
3 nishi 74
						char* line = malloc(i - 1 - incr + 1);
75
						line[i - 1 - incr] = 0;
76
						memcpy(line, buffer + incr, i - 1 - incr);
77
 
11 nishi 78
						if(strcmp(line, ".") == 0) {
3 nishi 79
							free(line);
80
							break;
11 nishi 81
						} else {
3 nishi 82
							char* ln = line;
11 nishi 83
							if(line[0] == '.') {
3 nishi 84
								ln++;
85
							}
86
 
11 nishi 87
							if(news_entry.content == NULL) {
3 nishi 88
								news_entry.content = malloc(1);
89
								news_entry.content[0] = 0;
90
							}
91
 
11 nishi 92
							if(boundary != NULL && strcmp(ln, boundary) == 0) {
3 nishi 93
								bheader = true;
94
								ignore = true;
11 nishi 95
							} else if(boundary != NULL && bheader && strlen(ln) == 0) {
3 nishi 96
								bheader = false;
97
								free(line);
98
								incr = i + 1;
99
								newline = true;
100
								continue;
11 nishi 101
							} else if(boundary != NULL && bheader) {
3 nishi 102
								int j;
11 nishi 103
								for(j = 0; j < strlen(ln); j++) {
104
									if(ln[j] == ':') {
3 nishi 105
										ln[j] = 0;
11 nishi 106
										if(strcasecmp(ln, "Content-Type") == 0) {
3 nishi 107
											ignore = false;
108
											j++;
11 nishi 109
											for(; ln[j] != 0 && (ln[j] == ' ' || ln[j] == '\t'); j++)
110
												;
111
											if(ln[j] != 0) {
3 nishi 112
												char* v = ln + j;
113
												int k;
11 nishi 114
												for(k = 0; v[k] != 0; k++) {
115
													if(v[k] == ';') {
3 nishi 116
														v[k] = 0;
117
														break;
118
													}
119
												}
11 nishi 120
												if(strcasecmp(v, "text/plain") == 0) {
121
												} else {
3 nishi 122
													ignore = true;
123
												}
124
											}
125
										}
126
										break;
127
									}
128
								}
129
							}
130
 
11 nishi 131
							if(!ignore && !bheader) {
3 nishi 132
								char* tmp = news_entry.content;
133
								news_entry.content = ok_strcat3(tmp, ln, "\n");
134
								free(tmp);
135
							}
136
						}
137
 
138
						free(line);
11 nishi 139
					} else if(newline) {
3 nishi 140
						header = false;
11 nishi 141
					} else {
3 nishi 142
						char* line = malloc(i - 1 - incr + 1);
143
						line[i - 1 - incr] = 0;
144
						memcpy(line, buffer + incr, i - 1 - incr);
145
 
146
						char* last = ok_strdup(l);
147
						char* tmp = l;
148
						l = ok_strcat(tmp, line);
149
						free(tmp);
150
						bool al = false;
11 nishi 151
						if(('a' <= line[0] && line[0] <= 'z') || ('A' <= line[0] && line[0] <= 'Z')) {
3 nishi 152
							free(l);
153
							l = ok_strdup(line);
154
							al = true;
155
						}
11 nishi 156
						if(al) {
3 nishi 157
							char* ln = ok_strdup(l);
158
							int j;
11 nishi 159
							for(j = 0; ln[j] != 0; j++) {
160
								if(ln[j] == ':') {
3 nishi 161
									char* key = ln;
162
									char* value = "";
163
									ln[j] = 0;
164
									j++;
11 nishi 165
									for(; ln[j] != 0 && (ln[j] == '\t' || ln[j] == ' '); j++)
166
										;
3 nishi 167
									if(ln[j] != 0) value = ln + j;
11 nishi 168
									if(strcasecmp(key, "From") == 0) {
3 nishi 169
										if(news_entry.from != NULL) free(news_entry.from);
170
										news_entry.from = ok_strdup(value);
11 nishi 171
									} else if(strcasecmp(key, "Content-Type") == 0) {
3 nishi 172
										int k = 0;
173
										int incr2 = 0;
11 nishi 174
										for(k = 0; k <= strlen(value); k++) {
175
											if(value[k] == ';' || value[k] == 0) {
3 nishi 176
												char* attr = malloc(k - incr2 + 1);
177
												attr[k - incr2] = 0;
178
												memcpy(attr, value + incr2, k - incr2);
179
 
180
												int in;
11 nishi 181
												for(in = 0; attr[in] != 0; in++) {
182
													if(attr[in] == '=') {
3 nishi 183
														attr[in] = 0;
184
 
11 nishi 185
														if(strcasecmp(attr, "boundary") == 0) {
3 nishi 186
															boundary = ok_strcat("--", attr + in + 1 + 1);
187
															boundary[strlen(attr + in + 1 + 1) - 1 + 2] = 0;
188
															ignore = true;
189
														}
190
 
191
														break;
192
													}
193
												}
194
 
195
												free(attr);
196
												k++;
11 nishi 197
												for(; value[k] != 0 && (value[k] == ' ' || value[k] == '\t'); k++)
198
													;
3 nishi 199
												incr2 = k;
200
											}
201
										}
202
									}
203
								}
204
							}
205
							free(ln);
206
						}
207
						free(last);
208
						free(line);
209
					}
210
					incr = i + 1;
211
					newline = true;
11 nishi 212
				} else {
3 nishi 213
					newline = false;
214
				}
11 nishi 215
			} else {
3 nishi 216
				newline = false;
217
			}
218
		}
219
		free(l);
220
 
221
		free(buffer);
222
		if(boundary != NULL) free(boundary);
223
		return 0;
11 nishi 224
	} else {
3 nishi 225
		return 1;
226
	}
227
}
5 nishi 228
 
11 nishi 229
int ok_news_parse(int sock) {
5 nishi 230
	char c;
231
	int sta = 0;
232
	bool st = false;
11 nishi 233
	while(1) {
234
		if(recv(sock, &c, 1, 0) <= 0) {
5 nishi 235
			return -1;
236
		}
237
		if(c == '\n') break;
11 nishi 238
		if(!st) {
239
			if('0' <= c && c <= '9') {
5 nishi 240
				sta *= 10;
241
				sta += c - '0';
11 nishi 242
			} else if(c == ' ') {
5 nishi 243
				st = true;
244
			}
245
		}
246
	}
247
	return sta == 0 ? -1 : sta;
248
}
249
 
11 nishi 250
int ok_news_write(const char* nick, const char* message) {
5 nishi 251
	int nt_sock;
252
	struct sockaddr_in nt_addr;
11 nishi 253
	if((nt_sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
13 nishi 254
		fprintf(stderr, "Socket creation failure: %s\n", strerror(errno));
5 nishi 255
		return 1;
256
	}
257
 
258
	bzero((char*)&nt_addr, sizeof(nt_addr));
259
	nt_addr.sin_family = PF_INET;
260
	nt_addr.sin_addr.s_addr = inet_addr(nntpserver);
261
	nt_addr.sin_port = htons(nntpport);
262
 
263
	int yes = 1;
264
 
265
	if(setsockopt(nt_sock, IPPROTO_TCP, TCP_NODELAY, (char*)&yes, sizeof(yes)) < 0) {
266
		fprintf(stderr, "setsockopt failure");
267
		ok_close(nt_sock);
268
		return 1;
269
	}
270
 
11 nishi 271
	if(connect(nt_sock, (struct sockaddr*)&nt_addr, sizeof(nt_addr)) < 0) {
5 nishi 272
		fprintf(stderr, "Connection failure\n");
273
		ok_close(nt_sock);
274
		return 1;
275
	}
276
 
277
	int sta;
278
 
279
	sta = ok_news_parse(nt_sock);
11 nishi 280
	if(sta == 200 || sta == 201) {
5 nishi 281
		char construct[1024];
11 nishi 282
		if(nntpuser != NULL) {
5 nishi 283
			sprintf(construct, "AUTHINFO USER %s\r\n", nntpuser);
284
			send(nt_sock, construct, strlen(construct), 0);
285
			sta = ok_news_parse(nt_sock);
11 nishi 286
			if(sta != 381) {
5 nishi 287
				goto cleanup;
288
			}
289
		}
11 nishi 290
		if(nntppass != NULL) {
5 nishi 291
			sprintf(construct, "AUTHINFO PASS %s\r\n", nntppass);
292
			send(nt_sock, construct, strlen(construct), 0);
7 nishi 293
			sta = ok_news_parse(nt_sock);
11 nishi 294
			if(sta != 281) {
5 nishi 295
				goto cleanup;
296
			}
297
		}
11 nishi 298
		send(nt_sock, "MODE READER\r\n", 4 + 1 + 6 + 2, 0);
5 nishi 299
		sta = ok_news_parse(nt_sock);
11 nishi 300
		if(sta == 200 || sta == 201) {
5 nishi 301
			send(nt_sock, "POST\r\n", 4 + 2, 0);
302
			sta = ok_news_parse(nt_sock);
11 nishi 303
			if(sta == 340) {
5 nishi 304
				sprintf(construct, "From: %s\r\n", nntpfrom);
305
				send(nt_sock, construct, strlen(construct), 0);
306
				sprintf(construct, "Newsgroups: %s\r\n", nntpgroup);
307
				send(nt_sock, construct, strlen(construct), 0);
7 nishi 308
				sprintf(construct, "Subject: [IRC] Message from %s\r\n", nick);
5 nishi 309
				send(nt_sock, construct, strlen(construct), 0);
9 nishi 310
				sprintf(construct, "Content-Type: text/plain; charset=UTF-8\r\n", nick);
311
				send(nt_sock, construct, strlen(construct), 0);
5 nishi 312
				send(nt_sock, "\r\n", 2, 0);
313
				char c;
314
				int i;
315
				bool first = true;
11 nishi 316
				for(i = 0; message[i] != 0; i++) {
317
					if(message[i] == '\n') {
5 nishi 318
						send(nt_sock, "\r\n", 2, 0);
319
						first = true;
11 nishi 320
					} else {
321
						if(first && message[i] == '.') {
5 nishi 322
							send(nt_sock, message + i, 1, 0);
323
						}
324
						send(nt_sock, message + i, 1, 0);
325
						first = false;
326
					}
327
				}
328
				if(!first) send(nt_sock, "\r\n", 2, 0);
329
				send(nt_sock, ".\r\n", 3, 0);
330
				sta = ok_news_parse(nt_sock);
331
				if(sta != 240) goto cleanup;
10 nishi 332
				send(nt_sock, "QUIT\r\n", 6, 0);
333
				sta = ok_news_parse(nt_sock);
334
				if(sta != 205) goto cleanup;
11 nishi 335
			} else {
5 nishi 336
				goto cleanup;
337
			}
11 nishi 338
		} else {
5 nishi 339
			goto cleanup;
340
		}
11 nishi 341
	} else {
5 nishi 342
		goto cleanup;
343
	}
344
 
345
	ok_close(nt_sock);
346
	return 0;
347
cleanup:
348
	ok_close(nt_sock);
349
	return 1;
350
}