Subversion Repositories Okuu

Rev

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