[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: RFC4978 (COMPRESS=DEFLATE feature)
At Sun, 14 Feb 2010 12:55:51 +0100, Herbert J. Skuhra wrote:
> > zlib.el is not ready yet, so all deflate/inflate work is done by
> > external utility. If you (or anybody else) want to value, how good
> > compress feature is, I can post sources.
>
> Vitaly, please post the sources. I've already checked out gnutls from
> git.
Disclaimer: this is not production quality code ;)
ssl-program-name "/path/to/deflater"
ssl-program-arguments '("/path/to/gnutls-cli" "-p" service host)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <zlib.h>
#include <assert.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <signal.h>
#define MAX(a,b) (a > b ? a : b)
int stdin_fd[2], stdout_fd[2], stderr_fd[2];
int do_deflate = 0;
int fd_log = 0;
int pid;
#define CHUNK 4096
#define DEBUG 0
z_stream strm_def;
z_stream strm_inf;
unsigned char in[CHUNK], out[CHUNK], inf_buf[CHUNK * 16];
int bytes, ret, inf_sz;
char *ptr;
unsigned char* inf_ptr = inf_buf;
void make_pipes()
{
assert(pipe2(stdin_fd, O_NONBLOCK) == 0);
assert(pipe2(stdout_fd, O_NONBLOCK) == 0);
assert(pipe2(stderr_fd, O_NONBLOCK) == 0);
}
void prepare()
{
close(0);
dup(stdin_fd[0]);
close(1);
dup(stdout_fd[1]);
close(2);
dup(stderr_fd[1]);
close(stdin_fd[1]);
close(stdout_fd[0]);
close(stderr_fd[0]);
}
void z_init()
{
int ret;
strm_def.zalloc = Z_NULL;
strm_def.zfree = Z_NULL;
strm_def.opaque = Z_NULL;
ret = deflateInit2(&strm_def,
Z_DEFAULT_COMPRESSION,
Z_DEFLATED,
-MAX_WBITS,
MAX_MEM_LEVEL,
Z_DEFAULT_STRATEGY);
assert(ret == Z_OK);
strm_inf.zalloc = Z_NULL;
strm_inf.zfree = Z_NULL;
strm_inf.opaque = Z_NULL;
ret = inflateInit2(&strm_inf, -MAX_WBITS);
assert(ret == Z_OK);
}
void write_all(int fd, char* buf, int count)
{
int ret;
fd_set set;
struct timeval tv;
do {
FD_ZERO(&set);
FD_SET(fd, &set);
tv.tv_sec = 0;
tv.tv_usec = 250;
ret = select (fd + 1, NULL, &set, NULL, &tv);
if (ret > 0 && FD_ISSET (fd, &set)) {
ret = write(fd, buf, count);
if (ret > 0) {
buf += ret;
count -= ret;
} else if (ret < 0 && errno != EAGAIN)
exit(1);
}
} while (count);
}
void logger(int io, char* str, int bytes)
{
if (fd_log) {
if (io)
write_all(fd_log, "ans: ", 5);
else
write_all(fd_log, "req: ", 5);
write_all(fd_log, str, bytes);
fsync(fd_log);
}
}
void process_stdin()
{
char *b;
if (do_deflate != 1) { /* do_deflate == 1 means we're waiting
* for response to COMPRESS*/
memset(in, 0, CHUNK);
memset(out, 0, CHUNK);
bytes = read(0, in, CHUNK);
if (bytes > 0) {
char *b = strchr (in, '\n');
if (b != NULL && *(b - 1) != '\r') {
strcpy (b, "\r\n");
bytes++;
}
logger(0, in, bytes);
ptr = (char*)(long)strcasestr(in, "compress deflate");
if (ptr)
do_deflate = 1;
if (do_deflate == 2) {
int have;
strm_def.avail_in = bytes;
strm_def.next_in = in;
if (DEBUG) {
int i;
printf("\nA (%d) > ", bytes);
for (i = 0; i < bytes; i++)
printf("%02x ", in[i]);
printf("\n");
}
do {
strm_def.avail_out = CHUNK;
strm_def.next_out = out;
ret = deflate(&strm_def, Z_SYNC_FLUSH);
assert(ret != Z_STREAM_ERROR);
have = CHUNK - strm_def.avail_out;
if (DEBUG) {
int i;
printf("Z-in (%d) > ", have);
for (i = 0; i < have; i++)
printf("%02x ", out[i]);
printf("\n");
}
write_all(stdin_fd[1], out, have);
} while (strm_def.avail_out == 0);
assert(strm_def.avail_in == 0);
} else {
write_all(stdin_fd[1], in, bytes);
}
}
}
}
void process_stdout()
{
memset(in, 0, CHUNK);
bytes = read(stdout_fd[0], in, CHUNK);
if (bytes > 0) {
if (do_deflate == 1) {
ptr = (char *)(long)strcasestr(in, "ok");
if (ptr) {
z_init();
do_deflate = 2;
write_all(1, in, bytes);
logger(1, in, bytes);
}
} else if (do_deflate == 2) {
int have;
memcpy(inf_ptr, in, bytes);
strm_inf.avail_in += bytes;
strm_inf.next_in = inf_buf;
if (DEBUG) {
int i;
printf("\nbytes = %d\n", bytes);
printf("inf_ptr delta = %d\n",
inf_ptr - inf_buf);
printf("avail_in = %d\n", strm_inf.avail_in);
printf("Z-out (%d) > ", bytes);
for (i = 0; i < bytes; i++)
printf("%02x ", in[i]);
printf("\n");
}
do {
strm_inf.avail_out = CHUNK;
strm_inf.next_out = out;
ret = inflate(&strm_inf, Z_SYNC_FLUSH);
assert(ret != Z_STREAM_ERROR);
have = CHUNK - strm_inf.avail_out;
if (DEBUG) {
printf("\nhave = %d\n", have);
printf("avail_in = %d\n",
strm_inf.avail_in);
printf("next_in = %d\n",
strm_inf.next_in - inf_buf);
}
write_all(1, out, have);
logger(1, out, have);
} while (strm_inf.avail_out == 0);
memmove(inf_buf, strm_inf.next_in, strm_inf.avail_in);
inf_ptr = inf_buf + strm_inf.avail_in;
} else {
write_all(1, in, bytes);
logger(1, in, bytes);
}
}
}
void process_stderr()
{
memset(in, 0, CHUNK);
bytes = read(stderr_fd[0], in, CHUNK);
if (bytes > 0) {
write(1, in, bytes);
}
}
void do_job()
{
fd_set set;
struct timeval tv;
FD_ZERO(&set);
FD_SET(0, &set);
FD_SET(stdout_fd[0], &set);
FD_SET(stderr_fd[0], &set);
tv.tv_sec = 0;
tv.tv_usec = 250;
ret = select (MAX(stdout_fd[0], stderr_fd[0]) + 1,
&set,
NULL,
NULL,
&tv);
if (ret < 0)
return;
if (FD_ISSET(0, &set))
process_stdin();
if (FD_ISSET(stdout_fd[0], &set))
process_stdout();
if (FD_ISSET(stderr_fd[0], &set))
process_stderr();
}
void sighandler(int sig, siginfo_t* si, void* re)
{
if (pid > 0)
kill(pid, SIGKILL);
}
int main(int argc, char* argv[])
{
char *args[32];
char *env[] = { NULL };
struct sigaction sa;
int i;
for (i = 1; i <= argc; i++) {
args[i - 1] = argv[i];
}
int ret;
make_pipes();
int flags;
flags = fcntl(0, F_GETFL, 0);
assert(flags != -1);
fcntl(0, F_SETFL, flags | O_NONBLOCK);
pid = fork();
if (pid == 0) {
prepare();
ret = execve(args[0], args, env);
printf("error = %d (%s)\n", ret, strerror(errno));
} else if (pid > 0) {
close(stdin_fd[0]);
close(stdout_fd[1]);
close(stderr_fd[1]);
sa.sa_sigaction = sighandler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART | SA_SIGINFO;
sigaction(SIGSEGV, &sa, NULL);
sigaction(SIGINT, &sa, NULL);
sigaction(SIGABRT, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
if (DEBUG) {
fd_log = open("/tmp/log", O_WRONLY | O_CREAT | O_APPEND,
0666);
assert(fd_log > 0);
}
for (;;) {
do_job();
if (waitpid(pid, 0, WNOHANG) > 0) {
pid = 0;
break;
}
}
if (fd_log)
close(fd_log);
}
}
Index: elmo/elmo-imap4.el
===================================================================
RCS file: /cvs/root/wanderlust/elmo/elmo-imap4.el,v
retrieving revision 1.182
diff -p -u -w -r1.182 elmo-imap4.el
--- elmo/elmo-imap4.el 22 Jan 2010 15:42:07 -0000 1.182
+++ elmo/elmo-imap4.el 14 Feb 2010 10:29:56 -0000
@@ -90,6 +90,9 @@
(defvar elmo-imap4-use-cache t
"Use cache in imap4 folder.")
+(defvar elmo-imap4-use-compression nil
+ "Use compression in imap4 proto, when possible.")
+
(defvar elmo-imap4-extra-namespace-alist
'(("^\\({.*/nntp}\\).*$" . ".")) ; Default is for UW's remote nntp mailbox...
"Extra namespace alist.
@@ -1145,7 +1148,7 @@ If CHOP-LENGTH is not specified, message
(if (sasl-step-data step)
(elmo-base64-encode-string (sasl-step-data step)
'no-line-break)
- "")))))))
+ ""))))))))
;; Some servers return reduced capabilities when client asks for them
;; before login. It might be a good idea to ask them again, otherwise
;; we can miss some useful feature.
@@ -1153,7 +1156,13 @@ If CHOP-LENGTH is not specified, message
session
(elmo-imap4-response-value
(elmo-imap4-send-command-wait session "capability")
- 'capability)))))))
+ 'capability))
+ (when (and (elmo-imap4-session-capable-p session 'compress=deflate)
+ elmo-imap4-use-compression)
+ (elmo-imap4-response-ok-p
+ (elmo-imap4-send-command-wait
+ session
+ "compress deflate")))))))
(luna-define-method elmo-network-setup-session ((session
elmo-imap4-session))
--
wbr, Vitaly