Subversion Repositories Okuu

Rev

Rev 13 | Details | Compare with Previous | Last modification | View Log | RSS feed

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