"Fossies" - the Fresh Open Source Software Archive

Member "zorp-7.0.4/modules/smtp/smtpdata.cc" (28 Oct 2019, 10832 Bytes) of package /linux/privat/zorp-7.0.4.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "smtpdata.cc" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 7.0.1_vs_7.0.2.

    1 /***************************************************************************
    2  *
    3  * Copyright (c) 2000-2015 BalaBit IT Ltd, Budapest, Hungary
    4  * Copyright (c) 2015-2018 BalaSys IT Ltd, Budapest, Hungary
    5  *
    6  * This program is free software; you can redistribute it and/or modify
    7  * it under the terms of the GNU General Public License as published by
    8  * the Free Software Foundation; either version 2 of the License, or
    9  * (at your option) any later version.
   10  *
   11  * This program is distributed in the hope that it will be useful,
   12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   14  * GNU General Public License for more details.
   15  *
   16  * You should have received a copy of the GNU General Public License along
   17  * with this program; if not, write to the Free Software Foundation, Inc.,
   18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
   19  *
   20  *
   21  ***************************************************************************/
   22 
   23 #include "smtp.h"
   24 #include <zorp/proxy/transfer2.h>
   25 #include <zorpll/log.h>
   26 
   27 #define SMTP_SR_INITIAL      0
   28 #define SMTP_SR_DATA         1
   29 
   30 #define SMTP_DW_INITIAL      0
   31 #define SMTP_DW_TRANSFER     3
   32 #define SMTP_DW_TRANSFER_LF  4
   33 #define SMTP_DW_TRANSFER_DOT 5
   34 #define SMTP_DW_REJECTED     6
   35 
   36 /**
   37  * SmtpTransfer:
   38  *
   39  * An SMTP specific transfer class.
   40  **/
   41 struct _SmtpTransfer
   42 {
   43   ZTransfer2 super;
   44   /* destination write state */
   45   gint dst_write_state;
   46 
   47   gint src_read_state;
   48 
   49   GString *received_line;
   50   guint received_line_pos;
   51 
   52   /* The previous line was too long, it must be concatenated with the current line */
   53   gboolean previous_line_split;
   54 };
   55 
   56 extern ZClass SmtpTransfer__class;
   57 
   58 /**
   59  * smtp_transfer_src_read:
   60  * @s: ZTransfer instance
   61  * @stream: stream to read from
   62  * @buf: buffer to read into
   63  * @buf_len: size of the buffer
   64  * @bytes_read: the number of read bytes is returned here
   65  * @err: GLib error code
   66  *
   67  * Reads the incoming data stream, checks for EOF (a single '.' on its own),
   68  * removes '.' escaping and handles lines longer than the buffer size of ZStreamLine.
   69  *
   70  **/
   71 static GIOStatus
   72 smtp_transfer_src_read(ZTransfer2 *s, ZStream *stream, gchar *buf, gsize buf_len, gsize *bytes_read, GError **err)
   73 {
   74   SmtpTransfer *self = Z_CAST(s, SmtpTransfer);
   75   SmtpProxy *owner = Z_CAST(self->super.owner, SmtpProxy);
   76   GIOStatus res;
   77   gsize line_len = buf_len - 2; /* to make space to closing CR LF */
   78 
   79   if (G_UNLIKELY(self->src_read_state == SMTP_SR_INITIAL))
   80     {
   81       if (owner->add_received_header)
   82         {
   83           if (self->received_line == NULL)
   84             {
   85               if (!smtp_generate_received(owner, &self->received_line))
   86                 self->src_read_state = SMTP_SR_DATA;
   87             }
   88 
   89           if (self->received_line)
   90             {
   91               *bytes_read = MIN(buf_len, self->received_line->len - self->received_line_pos);
   92               memmove(buf, self->received_line->str + self->received_line_pos, *bytes_read);
   93               self->received_line_pos += *bytes_read;
   94 
   95               if (self->received_line_pos >= self->received_line->len)
   96                 {
   97                   self->src_read_state = SMTP_SR_DATA;
   98                 }
   99               return G_IO_STATUS_NORMAL;
  100             }
  101         }
  102       else
  103         {
  104           self->src_read_state = SMTP_SR_DATA;
  105         }
  106     }
  107 
  108   if (buf_len < 2)
  109     {
  110       return G_IO_STATUS_AGAIN;
  111     }
  112 
  113   res = z_stream_line_get_copy(stream, buf, &line_len, err);
  114   if (res == G_IO_STATUS_NORMAL)
  115     {
  116       if (!self->previous_line_split && line_len > 0 && buf[0] == '.')
  117         {
  118           if (line_len == 1)
  119             {
  120               return G_IO_STATUS_EOF;
  121             }
  122           else
  123             {
  124               /* strip off first dot */
  125               memmove(buf, &buf[1], line_len - 1);
  126               line_len = line_len - 1;
  127             }
  128         }
  129       buf[line_len] = '\r';
  130       buf[line_len+1] = '\n';
  131       *bytes_read = line_len + 2;
  132       self->previous_line_split = FALSE;
  133     }
  134   else if (res == G_IO_STATUS_AGAIN && line_len > 0)
  135     {
  136       /* streamline indicates that the line was too long, do not add EOL */
  137       *bytes_read = line_len;
  138       self->previous_line_split = TRUE;
  139       res = G_IO_STATUS_NORMAL;
  140     }
  141   return res;
  142 }
  143 
  144 /**
  145  * smtp_transfer_dst_write:
  146  * @s: ZTransfer instance
  147  * @stream: stream to write to
  148  * @buf: buffer to read into
  149  * @count: buffer size
  150  * @bytes_read: number of bytes returned
  151  * @err: GLib error
  152  *
  153  * This function handles the data stream as it comes out of the stacked
  154  * proxy. It takes care about prefixing the mail body with a "DATA" command,
  155  * and through complicated means also takes care about fetching the response
  156  * to it. When this is successful it takes care about sending the data
  157  * stream reescaping unescaped lines beginning with '.'.
  158  **/
  159 static GIOStatus
  160 smtp_transfer_dst_write(ZTransfer2 *s, ZStream *stream, const gchar *buf, gsize count, gsize *bytes_written, GError **err)
  161 {
  162   SmtpTransfer *self = Z_CAST(s, SmtpTransfer);
  163   GIOStatus res;
  164   GError *local_error = NULL;
  165   gsize bw;
  166   gsize i;
  167 
  168   *bytes_written = 0;
  169   if (self->dst_write_state == SMTP_DW_INITIAL)
  170     {
  171       z_transfer2_suspend(s, SMTP_TRANSFER_SUSPEND_DATA);
  172       self->dst_write_state = SMTP_DW_TRANSFER;
  173       return G_IO_STATUS_AGAIN;
  174     }
  175 
  176  transfer_state:
  177 
  178   if (self->dst_write_state == SMTP_DW_TRANSFER || self->dst_write_state == SMTP_DW_TRANSFER_LF)
  179     {
  180       for (i = *bytes_written; i < count; i++)
  181         {
  182           if (self->dst_write_state == SMTP_DW_TRANSFER)
  183             {
  184               if (buf[i] == '\n')
  185                 {
  186                   self->dst_write_state = SMTP_DW_TRANSFER_LF;
  187                 }
  188             }
  189           else if (self->dst_write_state == SMTP_DW_TRANSFER_LF)
  190             {
  191               if (buf[i] == '.')
  192                 {
  193                   /* we need to escape this '.' */
  194 
  195                   /* first, write buf up to this '.' */
  196                   res = z_stream_write(stream, buf + *bytes_written, i - *bytes_written, &bw, &local_error);
  197                   if (res == G_IO_STATUS_NORMAL && (i - *bytes_written) == bw)
  198                     {
  199                       *bytes_written += bw;
  200                       self->dst_write_state = SMTP_DW_TRANSFER_DOT;
  201                       break;
  202                     }
  203                   else
  204                     {
  205                       /* we wrote less bytes, go back to the original state */
  206                       self->dst_write_state = SMTP_DW_TRANSFER;
  207                       *bytes_written += bw;
  208                       if (local_error)
  209                         g_propagate_error(err, local_error);
  210                       return res;
  211                     }
  212                 }
  213               self->dst_write_state = SMTP_DW_TRANSFER;
  214             }
  215         }
  216       if (i == count)
  217         {
  218           /* no need to escape */
  219           res = z_stream_write(stream, buf + *bytes_written, count - *bytes_written, &bw, err);
  220           *bytes_written += bw;
  221           return res;
  222         }
  223     }
  224   if (self->dst_write_state == SMTP_DW_TRANSFER_DOT)
  225     {
  226       res = z_stream_write(stream, ".", 1, &bw, &local_error);
  227       if (res == G_IO_STATUS_NORMAL && bw == 1)
  228         {
  229           self->dst_write_state = SMTP_DW_TRANSFER;
  230           goto transfer_state;
  231         }
  232       if (local_error)
  233         g_propagate_error(err, local_error);
  234       return res;
  235     }
  236 
  237   /* server responded non-354 to the DATA command */
  238   return G_IO_STATUS_ERROR;
  239 }
  240 
  241 /**
  242  * smtp_transfer_dst_shutdown:
  243  * @s: ZTransfer instance
  244  * @stream: stream to shut down
  245  * @shutdown_mode: shutdown mode
  246  * @err: GLib error
  247  *
  248  * This function is called when the server side stream is to be shut down. It takes care
  249  * about ending the mail body with a '.', or
  250  **/
  251 static GIOStatus
  252 smtp_transfer_dst_shutdown(ZTransfer2 *s, ZStream *stream, GError **err)
  253 {
  254   gsize bytes_written;
  255   GError *local_error = NULL;
  256   GIOStatus res = G_IO_STATUS_NORMAL;
  257   SmtpTransfer *self = Z_CAST(s, SmtpTransfer);
  258 
  259   if (self->dst_write_state != SMTP_DW_INITIAL)
  260     {
  261       res = z_stream_write(stream, "\r\n.\r\n", 5, &bytes_written, &local_error);
  262     }
  263   if (local_error)
  264     g_propagate_error(err, local_error);
  265   return res;
  266 }
  267 
  268 static gboolean
  269 smtp_transfer_stack_proxy(ZTransfer2 *s, ZStackedProxy **stacked)
  270 {
  271   SmtpProxy *owner = Z_CAST(s->owner, SmtpProxy);
  272   ZPolicyObj *stacked_proxy;
  273   gboolean called;
  274   gboolean success = TRUE;
  275 
  276   z_policy_lock(owner->super.thread);
  277   stacked_proxy = z_policy_call(owner->super.handler,
  278                                 "requestStack",
  279                                 NULL,
  280                                 &called,
  281                                 owner->super.session_id);
  282   if (!stacked_proxy)
  283     success = FALSE;
  284   else if (stacked_proxy != z_policy_none)
  285     {
  286       success = z_proxy_stack_object(&owner->super, stacked_proxy, stacked, NULL);
  287     }
  288 
  289   if (!success)
  290     z_proxy_report_policy_abort(&owner->super);
  291 
  292   z_policy_var_unref(stacked_proxy);
  293   z_policy_unlock(owner->super.thread);
  294   return success;
  295 
  296 }
  297 
  298 static gboolean
  299 smtp_transfer_setup(ZTransfer2 *s)
  300 {
  301   z_stream_line_set_split(s->endpoints[EP_CLIENT], TRUE);
  302   z_stream_line_set_truncate(s->endpoints[EP_CLIENT], FALSE);
  303   return TRUE;
  304 }
  305 
  306 static gboolean
  307 smtp_transfer_progress(ZTransfer2 *s)
  308 {
  309   SmtpTransfer *self = Z_CAST(s, SmtpTransfer);
  310 
  311   if (self->dst_write_state == SMTP_DW_INITIAL)
  312     z_transfer2_suspend(s, SMTP_TRANSFER_SUSPEND_NOOP);
  313   return TRUE;
  314 }
  315 
  316 static void
  317 smtp_transfer_free_method(ZObject *s)
  318 {
  319   SmtpTransfer *self = Z_CAST(s, SmtpTransfer);
  320 
  321   if (self->received_line)
  322     g_string_free(self->received_line, TRUE);
  323   z_transfer2_free_method(s);
  324 }
  325 
  326 ZTransfer2Funcs smtp_transfer_funcs =
  327 {
  328   {
  329     Z_FUNCS_COUNT(ZTransfer2),
  330     smtp_transfer_free_method,
  331   },
  332   smtp_transfer_src_read,
  333   smtp_transfer_dst_write,
  334   NULL,
  335   smtp_transfer_dst_shutdown,
  336   smtp_transfer_stack_proxy,
  337   /* .setup = */ smtp_transfer_setup,
  338   /* .run = */ NULL,
  339   /* .progress = */ smtp_transfer_progress
  340 };
  341 
  342 Z_CLASS_DEF(SmtpTransfer, ZTransfer2, smtp_transfer_funcs);
  343 
  344 /**
  345  * smtp_transfer_new:
  346  * @self: SmtpProxy instance
  347  *
  348  * This function is an Smtp specific constructor for the ZTransfer2 class.
  349  *
  350  * Returns: ZTransfer2 instance
  351  **/
  352 ZTransfer2 *
  353 smtp_transfer_new(SmtpProxy *owner)
  354 {
  355   SmtpTransfer *self;
  356 
  357   self = Z_CAST(z_transfer2_new(Z_CLASS(SmtpTransfer), &owner->super, owner->poll,
  358                                 owner->super.endpoints[EP_CLIENT], owner->super.endpoints[EP_SERVER],
  359                                 owner->buffer_size, owner->timeout,
  360                                 ZT2F_COMPLETE_COPY),
  361                 SmtpTransfer);
  362   z_transfer2_set_content_format(&self->super, "email");
  363   z_transfer2_enable_progress(&self->super, owner->interval_transfer_noop);
  364   return &self->super;
  365 }