1 /* $OpenBSD: pop_trans.c,v 1.4 2003/05/12 19:28:22 camield Exp $ */
2 
3 /*
4  * TRANSACTION state handling.
5  */
6 
7 #include <stdio.h>
8 #include <syslog.h>
9 
10 #include "params.h"
11 #include "protocol.h"
12 #include "database.h"
13 #include "mailbox.h"
14 
pop_trans_quit(char * params)15 static int pop_trans_quit(char *params)
16 {
17 	if (params) return POP_ERROR;
18 	return POP_STATE;
19 }
20 
pop_trans_noop(char * params)21 static int pop_trans_noop(char *params)
22 {
23 	if (params) return POP_ERROR;
24 	return POP_OK;
25 }
26 
pop_trans_stat(char * params)27 static int pop_trans_stat(char *params)
28 {
29 	if (params) return POP_ERROR;
30 	if (pop_reply("+OK %u %lu", db.visible_count, db.visible_size))
31 		return POP_CRASH_NETFAIL;
32 	return POP_QUIET;
33 }
34 
pop_trans_list_or_uidl_all(int uidl)35 static int pop_trans_list_or_uidl_all(int uidl)
36 {
37 	unsigned int number;
38 	struct db_message *msg;
39 
40 	if (pop_reply_ok()) return POP_CRASH_NETFAIL;
41 	for (number = 1; number <= db.total_count; number++) {
42 		msg = db.array[number - 1];
43 		if (msg->flags & MSG_DELETED) continue;
44 		if (uidl) {
45 			if (pop_reply("%u "
46 			    "%02x%02x%02x%02x%02x%02x%02x%02x",
47 			    number,
48 			    msg->hash[3], msg->hash[2],
49 			    msg->hash[1], msg->hash[0],
50 			    msg->hash[7], msg->hash[6],
51 			    msg->hash[5], msg->hash[4]))
52 				return POP_CRASH_NETFAIL;
53 		} else
54 			if (pop_reply("%u %lu", number, msg->size))
55 				return POP_CRASH_NETFAIL;
56 	}
57 	if (pop_reply_terminate()) return POP_CRASH_NETFAIL;
58 	return POP_QUIET;
59 }
60 
pop_trans_list_or_uidl(char * params,int uidl)61 static int pop_trans_list_or_uidl(char *params, int uidl)
62 {
63 	int number;
64 	struct db_message *msg;
65 
66 	if (!params)
67 		return pop_trans_list_or_uidl_all(uidl);
68 
69 	number = pop_get_int(&params);
70 	if (number < 1 || number > db.total_count || params)
71 		return POP_ERROR;
72 	msg = db.array[number - 1];
73 	if (msg->flags & MSG_DELETED) return POP_ERROR;
74 	if (uidl) {
75 		if (pop_reply("+OK %d "
76 		    "%02x%02x%02x%02x%02x%02x%02x%02x",
77 		    number,
78 		    msg->hash[3], msg->hash[2],
79 		    msg->hash[1], msg->hash[0],
80 		    msg->hash[7], msg->hash[6],
81 		    msg->hash[5], msg->hash[4]))
82 			return POP_CRASH_NETFAIL;
83 	} else
84 		if (pop_reply("+OK %d %lu", number, msg->size))
85 			return POP_CRASH_NETFAIL;
86 	return POP_QUIET;
87 }
88 
pop_trans_list(char * params)89 static int pop_trans_list(char *params)
90 {
91 	return pop_trans_list_or_uidl(params, 0);
92 }
93 
pop_trans_uidl(char * params)94 static int pop_trans_uidl(char *params)
95 {
96 	return pop_trans_list_or_uidl(params, 1);
97 }
98 
pop_trans_retr(char * params)99 static int pop_trans_retr(char *params)
100 {
101 	int number;
102 	struct db_message *msg;
103 	int event;
104 
105 	number = pop_get_int(&params);
106 	if (number < 1 || number > db.total_count || params) return POP_ERROR;
107 	msg = db.array[number - 1];
108 	if (msg->flags & MSG_DELETED) return POP_ERROR;
109 	if ((event = mailbox_get(msg, -1)) != POP_OK) return event;
110 #if POP_SUPPORT_LAST
111 	if (number > db.last) db.last = number;
112 #endif
113 	return POP_QUIET;
114 }
115 
pop_trans_top(char * params)116 static int pop_trans_top(char *params)
117 {
118 	int number, lines;
119 	struct db_message *msg;
120 	int event;
121 
122 	number = pop_get_int(&params);
123 	if (number < 1 || number > db.total_count) return POP_ERROR;
124 	lines = pop_get_int(&params);
125 	if (lines < 0 || params) return POP_ERROR;
126 	msg = db.array[number - 1];
127 	if (msg->flags & MSG_DELETED) return POP_ERROR;
128 	if ((event = mailbox_get(msg, lines)) != POP_OK) return event;
129 	return POP_QUIET;
130 }
131 
pop_trans_dele(char * params)132 static int pop_trans_dele(char *params)
133 {
134 	int number;
135 	struct db_message *msg;
136 
137 	number = pop_get_int(&params);
138 	if (number < 1 || number > db.total_count || params) return POP_ERROR;
139 	msg = db.array[number - 1];
140 	if (db_delete(msg)) return POP_ERROR;
141 #if POP_SUPPORT_LAST
142 	if (number > db.last) db.last = number;
143 #endif
144 	return POP_OK;
145 }
146 
pop_trans_rset(char * params)147 static int pop_trans_rset(char *params)
148 {
149 	struct db_message *msg;
150 
151 	if (params) return POP_ERROR;
152 
153 	if ((msg = db.head))
154 	do {
155 		msg->flags &= ~MSG_DELETED;
156 	} while ((msg = msg->next));
157 
158 	db.visible_count = db.total_count;
159 	db.visible_size = db.total_size;
160 	db.flags &= ~DB_DIRTY;
161 #if POP_SUPPORT_LAST
162 	db.last = 0;
163 #endif
164 
165 	return POP_OK;
166 }
167 
168 #if POP_SUPPORT_LAST
pop_trans_last(char * params)169 static int pop_trans_last(char *params)
170 {
171 	if (params) return POP_ERROR;
172 	if (pop_reply("+OK %u", db.last)) return POP_CRASH_NETFAIL;
173 	return POP_QUIET;
174 }
175 #endif
176 
177 static struct pop_command pop_trans_commands[] = {
178 	{"QUIT", pop_trans_quit},
179 	{"NOOP", pop_trans_noop},
180 	{"STAT", pop_trans_stat},
181 	{"LIST", pop_trans_list},
182 	{"UIDL", pop_trans_uidl},
183 	{"RETR", pop_trans_retr},
184 	{"TOP", pop_trans_top},
185 	{"DELE", pop_trans_dele},
186 	{"RSET", pop_trans_rset},
187 #if POP_SUPPORT_LAST
188 	{"LAST", pop_trans_last},
189 #endif
190 	{NULL, NULL}
191 };
192 
db_load(char * spool,char * mailbox)193 static int db_load(char *spool, char *mailbox)
194 {
195 	db_init();
196 
197 	if (mailbox_open(spool, mailbox)) return 1;
198 
199 	if (db_fix()) {
200 		mailbox_close();
201 		return 1;
202 	}
203 
204 	return 0;
205 }
206 
do_pop_trans(char * spool,char * mailbox)207 int do_pop_trans(char *spool, char *mailbox)
208 {
209 	int event;
210 
211 	if (!pop_sane()) return 1;
212 
213 	if (db_load(spool, mailbox)) {
214 		syslog(SYSLOG_PRI_HI,
215 			"Failed or refused to load %s/%s",
216 			spool, mailbox);
217 		pop_reply_error();
218 		return 0;
219 	}
220 
221 	syslog(SYSLOG_PRI_LO, "%u message%s (%lu byte%s) loaded",
222 		db.total_count, db.total_count == 1 ? "" : "s",
223 		db.total_size, db.total_size == 1 ? "" : "s");
224 
225 	if (pop_reply_ok())
226 		event = POP_CRASH_NETFAIL;
227 	else
228 	switch ((event = pop_handle_state(pop_trans_commands))) {
229 	case POP_STATE:
230 		if (mailbox_update()) {
231 			if (db.flags & DB_STALE) break;
232 			syslog(SYSLOG_PRI_ERROR,
233 				"Failed to update %s/%s",
234 				spool, mailbox);
235 			pop_reply_error();
236 			break;
237 		}
238 
239 		syslog(SYSLOG_PRI_LO, "%u (%lu) deleted, %u (%lu) left",
240 			db.total_count - db.visible_count,
241 			db.total_size - db.visible_size,
242 			db.visible_count,
243 			db.visible_size);
244 		pop_reply_ok();
245 		break;
246 
247 	case POP_CRASH_NETFAIL:
248 		syslog(SYSLOG_PRI_LO, "Premature disconnect");
249 		break;
250 
251 	case POP_CRASH_NETTIME:
252 		syslog(SYSLOG_PRI_LO, "Connection timed out");
253 	}
254 
255 	if (db.flags & DB_STALE)
256 		syslog(SYSLOG_PRI_LO, "Another MUA active, giving up");
257 	else
258 	if (event == POP_CRASH_SERVER)
259 		syslog(SYSLOG_PRI_ERROR,
260 			"Server failure accessing %s/%s",
261 			spool, mailbox);
262 
263 	mailbox_close();
264 
265 	return 0;
266 }
267