ZZMerge branch 'master' of ssh://git.citadel.org/appl/gitroot/citadel
[citadel.git] / citadel / modules / managesieve / serv_managesieve.c
index 121cad908c19f4a434feba284fe8952a6d0dde14..e085d5991a1691a7988e8df2f4c3f2c8afbef794 100644 (file)
@@ -1,12 +1,20 @@
-/**
- * $Id$
- *
+/*
  * This module is an managesieve implementation for the Citadel system.
  * It is compliant with all of the following:
  *
  * http://tools.ietf.org/html/draft-martin-managesieve-06
  * as this draft expires with this writing, you might need to search for
  * the new one.
+ *
+ * Copyright (c) 2007-2018 by the citadel.org team
+ *
+ * This program is open source software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
  */
 
 #include "sysdep.h"
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
+#include <libcitadel.h>
 #include "citadel.h"
 #include "server.h"
 #include "citserver.h"
 #include "support.h"
 #include "config.h"
 #include "control.h"
-#include "room_ops.h"
 #include "user_ops.h"
-#include "policy.h"
 #include "database.h"
 #include "msgbase.h"
-#include "tools.h"
 #include "internet_addressing.h"
-#include "imap_tools.h"        /* Needed for imap_parameterize */
 #include "genstamp.h"
 #include "domain.h"
 #include "clientsocket.h"
 #include "locate_host.h"
 #include "citadel_dirs.h"
-
-#ifndef HAVE_SNPRINTF
-#include "snprintf.h"
-#endif
-
-
 #include "ctdl_module.h"
-
-
-
-#ifdef HAVE_LIBSIEVE
-
 #include "serv_sieve.h"
 
 
-/**
+/*
  * http://tools.ietf.org/html/draft-martin-managesieve-06
  *
  * this is the draft this code tries to implement.
@@ -95,12 +89,73 @@ enum {      /** Command states for login authentication */
        mgsve_plain
 };
 
-#define MGSVE          CC->MGSVE
+#define MGSVE          ((struct citmgsve *)CC->session_specific_data)
+
+int old_imap_parameterize(char** args, char *in)
+{
+       char* out = in;
+       int num = 0;
+
+       for (;;)
+       {
+               /* Skip whitespace. */
+
+               while (isspace(*in))
+                       in++;
+               if (*in == 0)
+                       break;
+
+               /* Found the start of a token. */
+               
+               args[num++] = out;
+
+               /* Read in the token. */
+
+               for (;;)
+               {
+                       int c = *in++;
+                       if (isspace(c))
+                               break;
+                       
+                       if (c == '\"')
+                       {
+                               /* Found a quoted section. */
+
+                               for (;;)
+                               {
+                                       c = *in++;
+                                       if (c == '\"')
+                                               break;
+                                       else if (c == '\\')
+                                               c = *in++;
+
+                                       *out++ = c;
+                                       if (c == 0)
+                                               return num;
+                               }
+                       }
+                       else if (c == '\\')
+                       {
+                               c = *in++;
+                               *out++ = c;
+                       }
+                       else
+                               *out++ = c;
+
+                       if (c == 0)
+                               return num;
+               }
+               *out++ = '\0';
+       }
+
+       return num;
+}
 
 /*****************************************************************************/
 /*                      MANAGESIEVE Server                                   */
 /*****************************************************************************/
 
+
 void sieve_outbuf_append(char *str)
 {
         size_t newlen = strlen(str)+1;
@@ -124,7 +179,7 @@ void sieve_outbuf_append(char *str)
  */
 void cmd_mgsve_caps(void)
 { 
-       cprintf("\"IMPLEMENTATION\" \"CITADEL Sieve v6.84\"\r\n" /* TODO: put citversion here. */
+       cprintf("\"IMPLEMENTATION\" \"CITADEL Sieve " PACKAGE_VERSION "\"\r\n" 
                "\"SASL\" \"PLAIN\"\r\n" /*DIGEST-MD5 GSSAPI  SASL sucks.*/
 #ifdef HAVE_OPENSSL
 /* if TLS is already there, should we say that again? */
@@ -142,9 +197,9 @@ void managesieve_greeting(void) {
 
        strcpy(CC->cs_clientname, "Managesieve session");
 
-       CC->internal_pgm = 1;
+       CC->internal_pgm = 0;
        CC->cs_flags |= CS_STEALTH;
-       MGSVE = malloc(sizeof(struct citmgsve));
+       CC->session_specific_data = malloc(sizeof(struct citmgsve));
        memset(MGSVE, 0, sizeof(struct citmgsve));
        cmd_mgsve_caps();
 }
@@ -184,13 +239,13 @@ char *ReadString(long size, char *command)
        if (size < 1) {
                cprintf("NO %s: %ld BAD Message length must be at least 1.\r\n",
                        command, size);
-               CC->kill_me = 1;
+               CC->kill_me = KILLME_READSTRING_FAILED;
                return NULL;
        }
        MGSVE->transmitted_message = malloc(size + 2);
        if (MGSVE->transmitted_message == NULL) {
                cprintf("NO %s Cannot allocate memory.\r\n", command);
-               CC->kill_me = 1;
+               CC->kill_me = KILLME_MALLOC_FAILED;
                return NULL;
        }
        MGSVE->transmitted_length = size;
@@ -212,23 +267,32 @@ void cmd_mgsve_auth(int num_parms, char **parms, struct sdm_userdata *u)
                /* todo, check length*/
        {
                char auth[SIZ];
-               int retval;
-               char *message = ReadString(GetSizeToken(parms[2]), parms[0]);
+               char *message;
+               char *username;
+
+               message = NULL;
+               memset (auth, 0, SIZ);
+               if (parms[2][0] == '{')
+                       message = ReadString(GetSizeToken(parms[2]), parms[0]);
                
                if (message != NULL) {/**< do we have tokenized login? */
-                       retval = CtdlDecodeBase64(auth, MGSVE->transmitted_message, SIZ);
+                       CtdlDecodeBase64(auth, MGSVE->transmitted_message, strlen(MGSVE->transmitted_message));
                }
                else 
-                       retval = CtdlDecodeBase64(auth, parms[2], SIZ);
-
-               if (login_ok == CtdlLoginExistingUser(NULL, auth))
+                       CtdlDecodeBase64(auth, parms[2], strlen(parms[2]));
+               username = auth;
+               if ((*username == '\0') && (*(username + 1) != '\0'))
+                       username ++;
+               
+               if (login_ok == CtdlLoginExistingUser(username))
                {
                        char *pass;
+
                        pass = &(auth[strlen(auth)+1]);
                        /* for some reason the php script sends us the username twice. y? */
                        pass = &(pass[strlen(pass)+1]);
                        
-                       if (pass_ok == CtdlTryPassword(pass))
+                       if (pass_ok == CtdlTryPassword(pass, strlen(pass)))
                        {
                                MGSVE->command_state = mgsve_password;
                                cprintf("OK\r\n");
@@ -236,9 +300,8 @@ void cmd_mgsve_auth(int num_parms, char **parms, struct sdm_userdata *u)
                        }
                }
        }
-       
        cprintf("NO \"Authentication Failure.\"\r\n");/* we just support auth plain. */
-       CC->kill_me = 1;
+       CC->kill_me = KILLME_AUTHFAILED;
 }
 
 
@@ -254,18 +317,18 @@ void cmd_mgsve_starttls(void)
 
 
 
-/**
- *LOGOUT command, see chapter 2.3 
+/*
+ * LOGOUT command, see chapter 2.3 
  */
 void cmd_mgsve_logout(struct sdm_userdata *u)
 {
        cprintf("OK\r\n");
-       lprintf(CTDL_NOTICE, "MgSve bye.");
-       CC->kill_me = 1;
+       syslog(LOG_NOTICE, "MgSve bye.");
+       CC->kill_me = KILLME_CLIENT_LOGGED_OUT;
 }
 
 
-/**
+/*
  * HAVESPACE command. see chapter 2.5 
  */
 void cmd_mgsve_havespace(void)
@@ -276,7 +339,7 @@ void cmd_mgsve_havespace(void)
        if (MGSVE->command_state != mgsve_password)
        {
                cprintf("NO\r\n");
-               CC->kill_me = 1;
+               CC->kill_me = KILLME_QUOTA;
        }
        else
        {
@@ -286,7 +349,7 @@ void cmd_mgsve_havespace(void)
        }
 }
 
-/**
+/*
  * PUTSCRIPT command, see chapter 2.6 
  */
 void cmd_mgsve_putscript(int num_parms, char **parms, struct sdm_userdata *u)
@@ -320,7 +383,7 @@ void cmd_mgsve_putscript(int num_parms, char **parms, struct sdm_userdata *u)
        }
        else {
                cprintf("%s NO Read failed.\r\n", parms[0]);
-               CC->kill_me = 1;
+               CC->kill_me = KILLME_READ_FAILED;
                return;
        } 
 
@@ -388,7 +451,7 @@ void cmd_mgsve_getscript(int num_parms, char **parms, struct sdm_userdata *u)
                        slen = strlen(script_content);
                        outbuf = malloc (slen + 64);
                        snprintf(outbuf, slen + 64, "{%ld+}\r\n%s\r\nOK\r\n",slen, script_content);
-                       cprintf(outbuf);
+                       cprintf("%s", outbuf);
                }
                else
                        cprintf("No \"there is no script by that name %s \"\r\n", parms[1]);
@@ -444,7 +507,10 @@ void mgsve_auth(char *argbuf) {
                if (strlen(argbuf) >= 7) {
                }
                else {
-                       CtdlEncodeBase64(username_prompt, "Username:", 9);
+                       size_t len = CtdlEncodeBase64(username_prompt, "Username:", 9, 0);
+                       if (username_prompt[len - 1] == '\n') {
+                               username_prompt[len - 1] = '\0';
+                       }
                        cprintf("334 %s\r\n", username_prompt);
                }
                return;
@@ -504,16 +570,16 @@ void managesieve_command_loop(void) {
        memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */
        length = client_getln(cmdbuf, sizeof cmdbuf);
        if (length >= 1) {
-               num_parms = imap_parameterize(parms, cmdbuf);
+               num_parms = old_imap_parameterize(parms, cmdbuf);
                if (num_parms == 0) return;
                length = strlen(parms[0]);
        }
        if (length < 1) {
-               lprintf(CTDL_CRIT, "Client disconnected: ending session.\n");
-               CC->kill_me = 1;
+               syslog(LOG_CRIT, "managesieve: client disconnected: ending session.\n");
+               CC->kill_me = KILLME_CLIENT_DISCONNECTED;
                return;
        }
-       lprintf(CTDL_INFO, "MANAGESIEVE: %s\n", cmdbuf);
+       syslog(LOG_INFO, "MANAGESIEVE: %s\n", cmdbuf);
        if ((length>= 12) && (!strncasecmp(parms[0], "AUTHENTICATE", 12))){
                cmd_mgsve_auth(num_parms, parms, &u);
        }
@@ -530,7 +596,7 @@ void managesieve_command_loop(void) {
                cmd_mgsve_caps();
        } 
        /** these commands need to be authenticated. throw it out if it tries. */
-       else if (!CtdlAccessCheck(ac_logged_in))
+       else if (CC->logged_in != 0)
        {
                msiv_load(&u);
                if ((length>= 9) && (!strncasecmp(parms[0], "HAVESPACE", 9))){
@@ -557,36 +623,45 @@ void managesieve_command_loop(void) {
                msiv_store(&u, changes_made);
        }
        else {
-               cprintf("No\r\n");
-               lprintf(CTDL_INFO, "illegal Managesieve command: %s", parms[0]);
-               CC->kill_me = 1;
+               cprintf("No Invalid access or command.\r\n");
+               syslog(LOG_INFO, "illegal Managesieve command: %s", parms[0]);
+               CC->kill_me = KILLME_ILLEGAL_MANAGESIEVE_COMMAND;
        }
 
 
 }
 
+/*
+ * This cleanup function blows away the temporary memory and files used by
+ * the server.
+ */
+void managesieve_cleanup_function(void) {
 
-#endif /* HAVE_LIBSIEVE */
-
-CTDL_MODULE_INIT(managesieve)
-{
-
-#ifdef HAVE_LIBSIEVE
-
-       CtdlRegisterServiceHook(config.c_managesieve_port,      /* MGSVE */
-                               NULL,
-                               managesieve_greeting,
-                               managesieve_command_loop,
-                               NULL);
+       /* Don't do this stuff if this is not a managesieve session! */
+       if (CC->h_command_function != managesieve_command_loop) return;
 
-#else  /* HAVE_LIBSIEVE */
+       syslog(LOG_DEBUG, "Performing managesieve cleanup hook\n");
+       free(MGSVE);
+}
 
-       lprintf(CTDL_INFO, "This server is missing libsieve.  Managesieve protocol is disabled..\n");
 
-#endif /* HAVE_LIBSIEVE */
 
-       /* return our Subversion id for the Log */
-       return "$Id$";
+const char* CitadelServiceManageSieve = "ManageSieve";
+CTDL_MODULE_INIT(managesieve)
+{
+       if (!threading)
+       {
+               CtdlRegisterServiceHook(CtdlGetConfigInt("c_managesieve_port"),
+                                       NULL,
+                                       managesieve_greeting,
+                                       managesieve_command_loop,
+                                       NULL, 
+                                       CitadelServiceManageSieve);
+               CtdlRegisterSessionHook(managesieve_cleanup_function, EVT_STOP, PRIO_STOP + 30);
+       }
+       
+       /* return our module name for the log */
+       return "managesieve";
 }