Subversion Repositories Okuu

Rev

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