first commit
This commit is contained in:
commit
3d07f7beaf
10 changed files with 473 additions and 0 deletions
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
*.core
|
||||||
|
*.o
|
||||||
|
posta
|
||||||
|
config.h
|
||||||
29
COPYING
Normal file
29
COPYING
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2023, Lorenzo Torres
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
* Neither the name of the copyright holder nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
51
Makefile
Normal file
51
Makefile
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
# posta - terminal based email client
|
||||||
|
# See LICENSE file for copyright and license details.
|
||||||
|
|
||||||
|
include config.mk
|
||||||
|
|
||||||
|
SRC = posta.c utils.c imap.c
|
||||||
|
OBJ = ${SRC:.c=.o}
|
||||||
|
|
||||||
|
all: options posta
|
||||||
|
|
||||||
|
options:
|
||||||
|
@echo posta build options:
|
||||||
|
@echo "CFLAGS = ${CFLAGS}"
|
||||||
|
@echo "LDFLAGS = ${LDFLAGS}"
|
||||||
|
@echo "CC = ${CC}"
|
||||||
|
|
||||||
|
.c.o:
|
||||||
|
${CC} -c ${CFLAGS} $<
|
||||||
|
|
||||||
|
${OBJ}: config.h config.mk
|
||||||
|
|
||||||
|
config.h:
|
||||||
|
cp config.def.h $@
|
||||||
|
|
||||||
|
posta: ${OBJ}
|
||||||
|
${CC} -o $@ ${OBJ} ${LDFLAGS}
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f posta ${OBJ} posta-${VERSION}.tar.gz
|
||||||
|
|
||||||
|
dist: clean
|
||||||
|
mkdir -p posta-${VERSION}
|
||||||
|
cp -R LICENSE Makefile README config.def.h config.mk\
|
||||||
|
posta.1 util.h ${SRC} posta.png transient.c posta-${VERSION}
|
||||||
|
tar -cf posta-${VERSION}.tar posta-${VERSION}
|
||||||
|
gzip posta-${VERSION}.tar
|
||||||
|
rm -rf posta-${VERSION}
|
||||||
|
|
||||||
|
install: all
|
||||||
|
mkdir -p ${DESTDIR}${PREFIX}/bin
|
||||||
|
cp -f posta ${DESTDIR}${PREFIX}/bin
|
||||||
|
chmod 755 ${DESTDIR}${PREFIX}/bin/posta
|
||||||
|
mkdir -p ${DESTDIR}${MANPREFIX}/man1
|
||||||
|
sed "s/VERSION/${VERSION}/g" < posta.1 > ${DESTDIR}${MANPREFIX}/man1/posta.1
|
||||||
|
chmod 644 ${DESTDIR}${MANPREFIX}/man1/posta.1
|
||||||
|
|
||||||
|
uninstall:
|
||||||
|
rm -f ${DESTDIR}${PREFIX}/bin/posta\
|
||||||
|
${DESTDIR}${MANPREFIX}/man1/posta.1
|
||||||
|
|
||||||
|
.PHONY: all options clean dist install uninstall
|
||||||
5
config.def.h
Normal file
5
config.def.h
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
#ifndef CONFIG_H
|
||||||
|
#define CONFIG_H
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
28
config.mk
Normal file
28
config.mk
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
# posta version
|
||||||
|
VERSION = 1.0
|
||||||
|
|
||||||
|
# Customize below to fit your system
|
||||||
|
|
||||||
|
# paths
|
||||||
|
PREFIX = /usr
|
||||||
|
MANPREFIX = ${PREFIX}/share/man
|
||||||
|
|
||||||
|
# OpenBSD (uncomment)
|
||||||
|
MANPREFIX = ${PREFIX}/man
|
||||||
|
|
||||||
|
# includes and libs
|
||||||
|
INCS =
|
||||||
|
LIBS = -lssl -lcrypto
|
||||||
|
|
||||||
|
# flags
|
||||||
|
CPPFLAGS = -g -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS}
|
||||||
|
#CFLAGS = -g -std=c99 -pedantic -Wall -O0 ${INCS} ${CPPFLAGS}
|
||||||
|
CFLAGS = -g -std=c99 -pedantic -Wall -Wno-deprecated-declarations -Os ${INCS} ${CPPFLAGS}
|
||||||
|
LDFLAGS = ${LIBS}
|
||||||
|
|
||||||
|
# Solaris
|
||||||
|
#CFLAGS = -fast ${INCS} -DVERSION=\"${VERSION}\"
|
||||||
|
#LDFLAGS = ${LIBS}
|
||||||
|
|
||||||
|
# compiler and linker
|
||||||
|
CC = cc
|
||||||
218
imap.c
Normal file
218
imap.c
Normal file
|
|
@ -0,0 +1,218 @@
|
||||||
|
#include "imap.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <openssl/err.h>
|
||||||
|
|
||||||
|
imap_t *imap_connect(const char *addr, const char *port)
|
||||||
|
{
|
||||||
|
int sock;
|
||||||
|
char *res;
|
||||||
|
FILE *sockfile;
|
||||||
|
imap_t *state;
|
||||||
|
struct addrinfo* address = NULL;
|
||||||
|
|
||||||
|
getaddrinfo(addr, port, 0, &address);
|
||||||
|
struct sockaddr_in *p = (struct sockaddr_in *)address->ai_addr;
|
||||||
|
|
||||||
|
if ((sock = create_connection(p)) == -1)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if ((sockfile = fdopen(sock, "r")) == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
state = (imap_t *) malloc(sizeof(imap_t));
|
||||||
|
state->sock = sock;
|
||||||
|
state->sockfile = sockfile;
|
||||||
|
|
||||||
|
if (strcmp(port, "993") == 0) {
|
||||||
|
state->tls = 1;
|
||||||
|
imap_starttls(state);
|
||||||
|
} else {
|
||||||
|
state->tls = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = imap_response(state);
|
||||||
|
|
||||||
|
if (res[2] != 'O' || res[3] != 'K')
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
free(res);
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
void imap_close(imap_t *state)
|
||||||
|
{
|
||||||
|
close_connection(state->sock);
|
||||||
|
fclose(state->sockfile);
|
||||||
|
|
||||||
|
free(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *imap_response(imap_t *state)
|
||||||
|
{
|
||||||
|
size_t len;
|
||||||
|
char *res;
|
||||||
|
char c;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
len = 15;
|
||||||
|
res = (char *) calloc(len, sizeof(char));
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
|
||||||
|
if (!state->tls) {
|
||||||
|
c = fgetc(state->sockfile);
|
||||||
|
} else {
|
||||||
|
SSL_read(state->ssl, &c, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (c != '\n') {
|
||||||
|
res[i] = c;
|
||||||
|
i++;
|
||||||
|
|
||||||
|
if (i > len) {
|
||||||
|
len = i+5;
|
||||||
|
res = (char *) reallocarray(res, len, sizeof(char));
|
||||||
|
}
|
||||||
|
if (!state->tls) {
|
||||||
|
c = fgetc(state->sockfile);
|
||||||
|
} else {
|
||||||
|
SSL_read(state->ssl, &c, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i < len)
|
||||||
|
res = (char *) reallocarray(res, i+1, sizeof(char));
|
||||||
|
|
||||||
|
res[i] = '\0';
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int imap_send(imap_t *state, char *cmd)
|
||||||
|
{
|
||||||
|
if (!state->tls) {
|
||||||
|
return send(state->sock, (const void *)cmd, strlen(cmd), 0);
|
||||||
|
} else {
|
||||||
|
return SSL_write(state->ssl, (const void *)cmd, strlen(cmd)) > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int imap_capability(imap_t *state)
|
||||||
|
{
|
||||||
|
state->capabilities = 0;
|
||||||
|
char *cmd, *res, *token;
|
||||||
|
|
||||||
|
cmd = "A0 CAPABILITY\n";
|
||||||
|
imap_send(state, cmd);
|
||||||
|
res = imap_response(state);
|
||||||
|
token = get_token(res);
|
||||||
|
|
||||||
|
if (!imap_match_token(token, "*"))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
token = get_token(NULL);
|
||||||
|
|
||||||
|
if (!imap_match_token(token, TOK_CAPABILITY))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
while ((token = get_token(NULL)) != NULL) {
|
||||||
|
if (imap_match_token(token, TOK_IMAP4))
|
||||||
|
state->capabilities |= IMAP4;
|
||||||
|
else if (imap_match_token(token, TOK_STARTTLS))
|
||||||
|
state->capabilities |= STARTTLS;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(res);
|
||||||
|
res = imap_response(state);
|
||||||
|
free(res);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int imap_match_token(char *t1, const char *t2)
|
||||||
|
{
|
||||||
|
return strcmp(t1, t2) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int imap_starttls(imap_t* state)
|
||||||
|
{
|
||||||
|
if (!state->tls) {
|
||||||
|
char *cmd, *res, *token;
|
||||||
|
|
||||||
|
cmd = "A1 STARTTLS\n";
|
||||||
|
imap_send(state, cmd);
|
||||||
|
res = imap_response(state);
|
||||||
|
token = get_token(res);
|
||||||
|
|
||||||
|
if (!imap_match_token(token, "A1"))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
token = get_token(NULL);
|
||||||
|
|
||||||
|
if (!imap_match_token(token, "OK"))
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
state->ssl_ctx = SSL_CTX_new(TLSv1_2_client_method());
|
||||||
|
state->ssl = SSL_new(state->ssl_ctx);
|
||||||
|
SSL_set_fd(state->ssl, state->sock);
|
||||||
|
if (SSL_connect(state->ssl) == 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
state->tls = 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int imap_login(imap_t* state, char *username, char *password)
|
||||||
|
{
|
||||||
|
char *cmd, *res, *token;
|
||||||
|
|
||||||
|
size_t datalen = strlen(username) + strlen(password) + 12;
|
||||||
|
cmd = calloc(datalen, sizeof(char));
|
||||||
|
snprintf(cmd, datalen, "A2 LOGIN %s %s\n", username, password);
|
||||||
|
imap_send(state, cmd);
|
||||||
|
res = imap_response(state);
|
||||||
|
token = get_token(res);
|
||||||
|
|
||||||
|
if (!imap_match_token(token, "*"))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
token = get_token(NULL);
|
||||||
|
|
||||||
|
if (!imap_match_token(token, TOK_CAPABILITY))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
while ((token = get_token(NULL)) != NULL) {
|
||||||
|
if (imap_match_token(token, TOK_IMAP4))
|
||||||
|
state->capabilities |= IMAP4;
|
||||||
|
else if (imap_match_token(token, TOK_STARTTLS))
|
||||||
|
state->capabilities |= STARTTLS;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(res);
|
||||||
|
|
||||||
|
|
||||||
|
res = imap_response(state);
|
||||||
|
token = get_token(res);
|
||||||
|
|
||||||
|
if (!imap_match_token(token, "A2"))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
token = get_token(NULL);
|
||||||
|
|
||||||
|
if (!imap_match_token(token, TOK_OK))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
51
imap.h
Normal file
51
imap.h
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
#ifndef IMAP_H
|
||||||
|
#define IMAP_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <openssl/ssl.h>
|
||||||
|
|
||||||
|
#define TOK_OK "OK"
|
||||||
|
#define TOK_BAD "BAD"
|
||||||
|
#define TOK_CAPABILITY "CAPABILITY"
|
||||||
|
#define TOK_IMAP4 "IMAP4rev1"
|
||||||
|
#define TOK_STARTTLS "STARTTLS"
|
||||||
|
|
||||||
|
enum imap_caps {
|
||||||
|
IMAP4 = 1 << 0,
|
||||||
|
STARTTLS = 1 << 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* IMAP connection */
|
||||||
|
typedef struct {
|
||||||
|
int sock;
|
||||||
|
int capabilities;
|
||||||
|
int tls;
|
||||||
|
FILE *sockfile;
|
||||||
|
SSL *ssl;
|
||||||
|
SSL_CTX *ssl_ctx;
|
||||||
|
} imap_t;
|
||||||
|
|
||||||
|
/* Connect to an IMAP server */
|
||||||
|
imap_t *imap_connect(const char *addr, const char *port);
|
||||||
|
|
||||||
|
/* Close the connection to the IMAP server, automatically free used memory */
|
||||||
|
void imap_close(imap_t *state);
|
||||||
|
|
||||||
|
/* Get the server response as a string */
|
||||||
|
char *imap_response(imap_t *state);
|
||||||
|
|
||||||
|
int imap_send(imap_t *state, char *cmd);
|
||||||
|
|
||||||
|
/* Query for server capabilities */
|
||||||
|
int imap_capability(imap_t *state);
|
||||||
|
|
||||||
|
/* Check for token matching */
|
||||||
|
int imap_match_token(char *t1, const char *t2);
|
||||||
|
|
||||||
|
/* Init TLS connection */
|
||||||
|
int imap_starttls(imap_t* state);
|
||||||
|
|
||||||
|
/* Login to the IMAP server */
|
||||||
|
int imap_login(imap_t* state, char *username, char *password);
|
||||||
|
|
||||||
|
#endif
|
||||||
39
posta.c
Normal file
39
posta.c
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <openssl/ssl.h>
|
||||||
|
#include "utils.h"
|
||||||
|
#include "imap.h"
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
SSL_library_init();
|
||||||
|
|
||||||
|
imap_t *state = imap_connect("sagittarius-a.org", "993");
|
||||||
|
|
||||||
|
/*
|
||||||
|
if (imap_starttls(state) == 0) {
|
||||||
|
printf("TLS connection enstablished!\n");
|
||||||
|
} else {
|
||||||
|
printf("Can't enstablish TLS connection\n");
|
||||||
|
exit(-1);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
if (imap_capability(state) != 0) {
|
||||||
|
printf("Can't get server capabilities!\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state->capabilities & IMAP4) {
|
||||||
|
printf("IMAP4rev1 found\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (imap_login(state, "", "") != 0) {
|
||||||
|
printf("Login failed!\n");
|
||||||
|
} else {
|
||||||
|
printf("Logged successfully!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
imap_close(state);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
35
utils.c
Normal file
35
utils.c
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
#include "utils.h"
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
int create_connection(struct sockaddr_in *endpoint)
|
||||||
|
{
|
||||||
|
int fd, status;
|
||||||
|
|
||||||
|
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((status = connect(fd, (struct sockaddr*)endpoint, sizeof(*endpoint))) < 0) {
|
||||||
|
printf("Failed to connect!\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
void close_connection(int socket)
|
||||||
|
{
|
||||||
|
close(socket);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *get_token(char *s)
|
||||||
|
{
|
||||||
|
const char sep[4] = " ";
|
||||||
|
return strtok(s, sep);
|
||||||
|
}
|
||||||
13
utils.h
Normal file
13
utils.h
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
#ifndef UTILS_H
|
||||||
|
#define UTILS_H
|
||||||
|
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
int create_connection(struct sockaddr_in *addr);
|
||||||
|
void close_connection(int socket);
|
||||||
|
|
||||||
|
char *get_token(char *s);
|
||||||
|
|
||||||
|
#endif
|
||||||
Loading…
Add table
Add a link
Reference in a new issue