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