implement basic POP interface
itoomail now works as P2P with ~~no problems~~ a million problems Signed-off-by: AGentooCat <agentoocat@mail.i2p>
This commit is contained in:
173
src/pop.c
173
src/pop.c
@ -142,6 +142,10 @@ int parse_popline(struct POPCommand *com, char *line) {
|
||||
struct pop_conn_state {
|
||||
char *addr;
|
||||
char *authas;
|
||||
char *_authas;
|
||||
|
||||
struct SizedMsgid *msgids;
|
||||
long msgidl;
|
||||
};
|
||||
#define SENDFAIL(x) "[fd%d] Couldn't send " #x " line to client"
|
||||
int pop_connect(struct lineconn_t *hand, void *me) {
|
||||
@ -165,21 +169,184 @@ int pop_connect(struct lineconn_t *hand, void *me) {
|
||||
int pop_disconnect(struct lineconn_t *hand) {
|
||||
struct pop_conn_state *state = (struct pop_conn_state*)hand->data;
|
||||
log(DEBUG, "Closing POP connection at FD %d", hand->fd);
|
||||
if (state->authas)
|
||||
logout_user(state->authas);
|
||||
xfree(state->_authas);
|
||||
free(state);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int commit_pop_session(struct pop_conn_state *state) {
|
||||
if (!state->authas)
|
||||
return 1;
|
||||
int all_good = 1;
|
||||
for (long i = 0; i < state->msgidl; i++)
|
||||
if (state->msgids[i].deleted && wipemail(state->msgids[i].msgid) < 0)
|
||||
all_good = -1;
|
||||
return all_good;
|
||||
}
|
||||
|
||||
int pop_line(struct lineconn_t *hand) {
|
||||
struct pop_conn_state *state = (struct pop_conn_state*)hand->data;
|
||||
struct POPCommand com;
|
||||
if (parse_popline(&com, hand->buf) < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
|
||||
char *msg = NULL;
|
||||
#define CHKLOG \
|
||||
if (!state->authas) { msg = "-ERR Specify user to login as."; break; }
|
||||
#define CHKMID \
|
||||
if (com.arg1 < 1 || com.arg1 > state->msgidl) { \
|
||||
msg = "-ERR Invalid message ID."; break; \
|
||||
} else if (state->msgids[com.arg1 - 1].deleted) { \
|
||||
msg = "-ERR Message deleted."; break; \
|
||||
}
|
||||
#define SENDOK if (sendline(hand->fd, "+OK", 1) < 1) return -1;
|
||||
|
||||
switch (com.type) {
|
||||
case USER:
|
||||
if (state->authas) {
|
||||
msg = "-ERR Already logged in.";
|
||||
break;
|
||||
}
|
||||
int ret = login_user(com.strarg);
|
||||
if (ret == -1) return -1;
|
||||
else if (!ret) {
|
||||
msg = "-ERR User already logged in.";
|
||||
break;
|
||||
}
|
||||
if ((state->msgidl = getpendingids(&state->msgids, com.strarg)) < 0)
|
||||
return -1;
|
||||
state->_authas = com._backline;
|
||||
com._backline = NULL;
|
||||
state->authas = com.strarg;
|
||||
msg = "+OK Logged in; waiting for unneeded PASS.";
|
||||
break;
|
||||
case PASS:
|
||||
CHKLOG;
|
||||
msg = "+OK Password ignored.";
|
||||
break;
|
||||
|
||||
case STAT:
|
||||
CHKLOG;
|
||||
long len = 0, tlen = 0;
|
||||
for (long i = 0; i < state->msgidl; i++) {
|
||||
if (state->msgids[i].deleted) continue;
|
||||
len++;
|
||||
tlen += state->msgids[i].headlen + 2 + state->msgids[i].maillen;
|
||||
}
|
||||
if (sendfmtline(hand->fd, "+OK %ld %ld", len, tlen) < 1)
|
||||
return -1;
|
||||
break;
|
||||
|
||||
case LIST:
|
||||
CHKLOG;
|
||||
if (com.arg1 > -1) {
|
||||
CHKMID;
|
||||
long len = state->msgids[com.arg1 - 1].headlen + 2 + state->msgids[com.arg1 - 1].maillen;
|
||||
if (sendfmtline(hand->fd, "+OK %d %d", com.arg1, len) < 1)
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
SENDOK;
|
||||
for (long i = 0; i < state->msgidl; i++) {
|
||||
if (state->msgids[i].deleted) continue;
|
||||
long len = state->msgids[i].headlen + 2 + state->msgids[i].maillen;
|
||||
if (sendfmtline(hand->fd, "%d %d", i + 1, len) < 1)
|
||||
return -1;
|
||||
}
|
||||
msg = ".";
|
||||
break;
|
||||
|
||||
case TOP:
|
||||
case RETR:
|
||||
CHKLOG;
|
||||
CHKMID;
|
||||
if (com.type == TOP && com.arg2 < 1) {
|
||||
msg = "-ERR Invalid line count.";
|
||||
break;
|
||||
}
|
||||
|
||||
char *perr;
|
||||
struct Mail mail;
|
||||
if (getmaillines(&mail, state->msgids[com.arg1 - 1].msgid, com.type == TOP ? com.arg2 : 0, &perr) < 1) {
|
||||
log(ERROR, "couldn't pull mail %s of user %s: %s", state->msgids[com.arg1 - 1].msgid, state->authas, perr);
|
||||
msg = "-ERR Internal error.";
|
||||
break;
|
||||
}
|
||||
SENDOK;
|
||||
|
||||
for (long i = 0; i < mail.hdrl; i++)
|
||||
if (sendfmtline(hand->fd, "%s: %s", mail.hdrs[i].key, mail.hdrs[i].val) < 1) {
|
||||
freemail(&mail);
|
||||
return -1;
|
||||
}
|
||||
if (write(hand->fd, mail.lines, mail.size) < mail.size)
|
||||
return -1;
|
||||
// the ending dot-crlf comes from database
|
||||
freemail(&mail);
|
||||
break;
|
||||
|
||||
case P_NOOP:
|
||||
msg = "+OK";
|
||||
break;
|
||||
|
||||
case RSET:
|
||||
CHKLOG;
|
||||
for (long i = 0; i < state->msgidl; i++)
|
||||
state->msgids[i].deleted = 0;
|
||||
msg = "+OK";
|
||||
break;
|
||||
|
||||
case DELE:
|
||||
CHKLOG;
|
||||
CHKMID;
|
||||
state->msgids[com.arg1 - 1].deleted = 1;
|
||||
msg = "+OK";
|
||||
break;
|
||||
|
||||
case UIDL:
|
||||
CHKLOG;
|
||||
// breaks compat with pop3 spec: uidl ids must be 1-70 bytes long.
|
||||
// we send the message-id which can be longer.
|
||||
if (com.arg1 > -1) {
|
||||
CHKMID;
|
||||
char *mid = state->msgids[com.arg1 - 1].msgid;
|
||||
if (sendfmtline(hand->fd, "+OK %ld %s", com.arg1, mid) < 1)
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
|
||||
SENDOK;
|
||||
for (long i = 0; i < state->msgidl; i++) {
|
||||
if (state->msgids[i].deleted) continue;
|
||||
char *mid = state->msgids[i].msgid;
|
||||
if (sendfmtline(hand->fd, "%d %s", i + 1, mid) < 1)
|
||||
return -1;
|
||||
}
|
||||
msg = ".";
|
||||
break;
|
||||
|
||||
case P_QUIT:
|
||||
if (commit_pop_session(state) < 0)
|
||||
msg = "-ERR Couldn't commit POP session.";
|
||||
else
|
||||
msg = "+OK Done! See you later.";
|
||||
break;
|
||||
|
||||
default:
|
||||
msg = "-ERR Unrecognized command";
|
||||
break;
|
||||
}
|
||||
if (msg && sendline(hand->fd, msg, 1) < 1)
|
||||
return -1;
|
||||
if (com.type == P_QUIT)
|
||||
return -1;
|
||||
|
||||
return com._backline ? 0 : 1;
|
||||
}
|
||||
|
||||
void *popserv(void *me) {
|
||||
pthread_mutex_init(&login_lock, NULL);
|
||||
|
||||
struct linepoll_t hand = {
|
||||
.sockfd = pop_serv,
|
||||
.init_ptr = pop_connect,
|
||||
|
Reference in New Issue
Block a user