コード例 #1
0
ファイル: rebuild.c プロジェクト: brong/cyrus-imapd
EXPORTED int sieve_rebuild(const char *script_fname, const char *bc_fname,
                           int force, char **out_parse_errors)
{
    char new_bc_fname[MAX_MAILBOX_PATH + 1] = {0};
    char *freeme = NULL;
    FILE *script_file = NULL;
    char *parse_errors = NULL;
    sieve_script_t *script = NULL;
    bytecode_info_t *bc = NULL;
    int script_fd = -1, bc_fd = -1;
    int r;
    size_t len;

    if (!script_fname && !bc_fname)
        return SIEVE_FAIL; /* XXX assert? */

    if (!script_fname)
        script_fname = freeme = sieve_getscriptfname(bc_fname);

    if (!bc_fname)
        bc_fname = freeme = sieve_getbcfname(script_fname);

    if (!script_fname || !bc_fname)
        return SIEVE_FAIL;

    /* open and lock the script file */
    script_fd = open(script_fname, O_RDWR);
    if (script_fd == -1) {
        syslog(LOG_ERR, "IOERROR: unable to open %s for reading: %m",
                        script_fname);
        r = IMAP_IOERROR;
        goto done;
    }

    r = lock_setlock(script_fd, /* exclusive */ 1, /* nonblocking */ 0,
                     script_fname);
    if (r) {
        syslog(LOG_ERR, "IOERROR: unable to obtain lock on %s: %m",
                        script_fname);
        r = IMAP_IOERROR;
        goto done;
    }

    /* exit early if bc is up to date */
    if (!force) {
        struct stat script_stat, bc_stat;

        r = fstat(script_fd, &script_stat);
        if (r) {
            syslog(LOG_DEBUG, "%s: stat %s: %m", __func__, script_fname);
            r = SIEVE_FAIL;
            goto done;
        }

        r = stat(bc_fname, &bc_stat);
        if (r && errno != ENOENT) {
            syslog(LOG_DEBUG, "%s: stat %s: %m", __func__, bc_fname);
            r = SIEVE_FAIL;
            goto done;
        }

        if (!r && bc_stat.st_mtime >= script_stat.st_mtime) {
            sieve_execute_t *exe = NULL;
            r = sieve_script_load(bc_fname, &exe);

            if (!r) {
                int version;

                bc_header_parse((bytecode_input_t *) exe->bc_cur->data,
                                &version, NULL);
                if (version == BYTECODE_VERSION) {
                    syslog(LOG_DEBUG,
                           "%s: %s is up to date\n", __func__, bc_fname);
                    r = SIEVE_OK;
                    sieve_script_unload(&exe);
                    goto done;
                }
            }

            sieve_script_unload(&exe);
        }
    }

    len = strlcpy(new_bc_fname, bc_fname, sizeof(new_bc_fname));
    if (len >= sizeof(new_bc_fname)) {
        syslog(LOG_DEBUG, "%s: filename too long: %s", __func__, bc_fname);
        r = SIEVE_FAIL;
        goto done;
    }
    len = strlcat(new_bc_fname, ".NEW", sizeof(new_bc_fname));
    if (len >= sizeof(new_bc_fname)) {
        syslog(LOG_DEBUG, "%s: filename too long: %s", __func__, bc_fname);
        r = SIEVE_FAIL;
        goto done;
    }

    /* make sure no stray hardlink is lying around */
    unlink(new_bc_fname);

    bc_fd = open(new_bc_fname, O_CREAT|O_TRUNC|O_WRONLY,
                               S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
    if (bc_fd < 0) {
        syslog(LOG_ERR, "IOERROR: unable to open %s for writing: %m",
                        new_bc_fname);
        r = IMAP_IOERROR;
        goto done;
    }

    /* if an error occurs after this point, we need to unlink new_bc_fname */

    script_file = fdopen(script_fd, "r");
    if (!script_file) {
        syslog(LOG_ERR, "IOERROR: unable to fdopen %s for reading: %m",
                          script_fname);
        r = IMAP_IOERROR;
        goto done;
    }

    r = sieve_script_parse_only(script_file, &parse_errors, &script);
    if (r != SIEVE_OK) {
        syslog(LOG_DEBUG, "%s: %s parse failed: %s",
                          __func__, script_fname, parse_errors);
        goto done;
    }

    if (sieve_generate_bytecode(&bc, script) == -1) {
        syslog(LOG_DEBUG, "%s: %s bytecode generation failed: %s",
                          __func__, script_fname, "unknown error");
        r = SIEVE_FAIL;
        goto done;
    }

    if (sieve_emit_bytecode(bc_fd, bc) == -1) {
        syslog(LOG_DEBUG, "%s: unable to emit bytecode to %s: %s",
                          __func__, bc_fname, "unknown error");
        r = SIEVE_FAIL;
        goto done;
    }

    if (fsync(bc_fd) < 0) {
        r = errno;
        syslog(LOG_ERR, "IOERROR: fsync %s: %m", new_bc_fname);
        goto done;
    }

    if (rename(new_bc_fname, bc_fname) < 0) {
        r = errno;
        syslog(LOG_ERR, "IOERROR: rename %s -> %s: %m",
                        new_bc_fname, bc_fname);
        goto done;
    }

    syslog(LOG_DEBUG, "%s: %s rebuilt from %s",
                      __func__, bc_fname, script_fname);

done:
    if (r && new_bc_fname[0] != '\0') unlink(new_bc_fname);

    if (bc_fd >= 0) close(bc_fd);

    lock_unlock(script_fd, script_fname);
    if (script_file) {
        fclose(script_file); /* also closes underlying fd */
    }
    else if (script_fd >= 0) {
        close(script_fd);
    }

    if (parse_errors) {
        if (out_parse_errors)
            *out_parse_errors = parse_errors;
        else
            free(parse_errors);
    }

    if (bc) sieve_free_bytecode(&bc);
    if (script) sieve_script_free(&script);

    if (freeme) free(freeme);

    return r;
}
コード例 #2
0
ファイル: autocreate.c プロジェクト: JensErat/cyrus-imapd
static int autocreate_sieve(const char *userid, const char *source_script)
{
    /* XXX - this is really ugly, but too much work to tidy up right now -- Bron */
    sieve_script_t *s = NULL;
    bytecode_info_t *bc = NULL;
    char *err = NULL;
    FILE *in_stream, *out_fp;
    int out_fd, in_fd, r, k;
    int do_compile = 0;
    const char *compiled_source_script = NULL;
    const char *sievename = get_script_name(source_script);
    const char *sieve_script_dir = NULL;
    char sieve_script_name[MAX_FILENAME];
    char sieve_bcscript_name[MAX_FILENAME];
    char sieve_default[MAX_FILENAME];
    char sieve_tmpname[MAX_FILENAME];
    char sieve_bctmpname[MAX_FILENAME];
    char sieve_bclink_name[MAX_FILENAME];
    char buf[4096];
    mode_t oldmask;
    struct stat statbuf;

    /* We don't support using the homedirectory, like timsieved */
    if (config_getswitch(IMAPOPT_SIEVEUSEHOMEDIR)) {
        syslog(LOG_WARNING,"autocreate_sieve: autocreate_sieve does not work with sieveusehomedir option in imapd.conf");
        return 1;
    }

    /* Check if sievedir is defined in imapd.conf */
    if(!config_getstring(IMAPOPT_SIEVEDIR)) {
        syslog(LOG_WARNING, "autocreate_sieve: sievedir option is not defined. Check imapd.conf");
        return 1;
    }

    /* Check if autocreate_sieve_compiledscript is defined in imapd.conf */
    if(!(compiled_source_script = config_getstring(IMAPOPT_AUTOCREATE_SIEVE_SCRIPT_COMPILED))) {
        syslog(LOG_WARNING, "autocreate_sieve: autocreate_sieve_compiledscript option is not defined. Compiling it");
        do_compile = 1;
    }

    if (!(sieve_script_dir = user_sieve_path(userid))) {
        syslog(LOG_WARNING, "autocreate_sieve: unable to determine sieve directory for user %s", userid);
        return 1;
    }

    if(snprintf(sieve_tmpname, MAX_FILENAME, "%s/%s.script.NEW",sieve_script_dir, sievename) >= MAX_FILENAME) {
        syslog(LOG_WARNING, "autocreate_sieve: Invalid sieve path %s, %s, %s", sieve_script_dir, sievename, userid);
        return 1;
    }
    if(snprintf(sieve_bctmpname, MAX_FILENAME, "%s/%s.bc.NEW",sieve_script_dir, sievename) >= MAX_FILENAME) {
        syslog(LOG_WARNING, "autocreate_sieve: Invalid sieve path %s, %s, %s", sieve_script_dir, sievename, userid);
        return 1;
    }
    if(snprintf(sieve_script_name, MAX_FILENAME, "%s/%s.script",sieve_script_dir, sievename) >= MAX_FILENAME) {
        syslog(LOG_WARNING, "autocreate_sieve: Invalid sieve path %s, %s, %s", sieve_script_dir, sievename, userid);
        return 1;
    }
    if(snprintf(sieve_bcscript_name, MAX_FILENAME, "%s/%s.bc",sieve_script_dir, sievename) >= MAX_FILENAME) {
        syslog(LOG_WARNING, "autocreate_sieve: Invalid sieve path %s, %s, %s", sieve_script_dir, sievename, userid);
        return 1;
    }
    if(snprintf(sieve_default, MAX_FILENAME, "%s/%s",sieve_script_dir,"defaultbc") >= MAX_FILENAME) {
        syslog(LOG_WARNING, "autocreate_sieve: Invalid sieve path %s, %s, %s", sieve_script_dir, sievename, userid);
        return 1;
    }
    /* XXX no directory? umm */
    if(snprintf(sieve_bclink_name, MAX_FILENAME, "%s.bc", sievename) >= MAX_FILENAME) {
        syslog(LOG_WARNING, "autocreate_sieve: Invalid sieve path %s, %s, %s", sieve_script_dir, sievename, userid);
        return 1;
    }

    /* Check if a default sieve filter alrady exists */
    if(!stat(sieve_default,&statbuf)) {
        syslog(LOG_WARNING,"autocreate_sieve: Default sieve script already exists");
        return 1;
    }

    /* Open the source script. if there is a problem with that exit */
    in_stream = fopen(source_script, "r");
    if(!in_stream) {
        syslog(LOG_WARNING,"autocreate_sieve: Unable to open sieve script %s. Check permissions",source_script);
        return 1;
    }

    /*
     * At this point we start the modifications of the filesystem
     */

    /* Create the directory where the sieve scripts will reside */
    r = cyrus_mkdir(sieve_bctmpname, 0755);
    if(r == -1) {
        /* If this fails we just leave */
        fclose(in_stream);
        return 1;
    }

    /*
     * We open the file that will be used as the bc file. If this file exists, overwrite it
     * since something bad has happened. We open the file here so that this error checking is
     * done before we try to open the rest of the files to start copying etc.
     */
    out_fd = open(sieve_bctmpname, O_CREAT|O_TRUNC|O_WRONLY, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
    if(out_fd < 0) {
        if(errno == EEXIST) {
            syslog(LOG_WARNING,"autocreate_sieve: File %s already exists. Probaly left over. Ignoring",sieve_bctmpname);
        } else if (errno == EACCES) {
            syslog(LOG_WARNING,"autocreate_sieve: No access to create file %s. Check permissions",sieve_bctmpname);
            fclose(in_stream);
            return 1;
        } else {
            syslog(LOG_WARNING,"autocreate_sieve: Unable to create %s: %m",sieve_bctmpname);
            fclose(in_stream);
            return 1;
        }
    }

    if(!do_compile && compiled_source_script && (in_fd = open(compiled_source_script, O_RDONLY)) != -1) {
        while((r = read(in_fd, buf, sizeof(buf))) > 0) {
            if((k=write(out_fd, buf,r)) < 0) {
                syslog(LOG_WARNING, "autocreate_sieve: Error writing to file %s: %m", sieve_bctmpname);
                close(out_fd);
                close(in_fd);
                fclose(in_stream);
                unlink(sieve_bctmpname);
                return 1;
           }
        }

        if(r == 0) { /* EOF */
            xclose(out_fd);
            xclose(in_fd);
        } else if (r < 0) {
            syslog(LOG_WARNING, "autocreate_sieve: Error reading compiled script file %s: %m. Will try to compile it",
                           compiled_source_script);
            xclose(in_fd);
            do_compile = 1;
            if(lseek(out_fd, 0, SEEK_SET)) {
                syslog(LOG_WARNING, "autocreate_sieve: Major IO problem (lseek: %m). Aborting");
                xclose(out_fd);
                return 1;
            }
        }
        xclose(in_fd);
    } else {
        if(compiled_source_script)
              syslog(LOG_WARNING,"autocreate_sieve: Problem opening compiled script file: %s. Compiling it", compiled_source_script);
        do_compile = 1;
    }


    /* Because we failed to open a precompiled bc sieve script, we compile one */
    if(do_compile) {
       if(is_script_parsable(in_stream,&err, &s) == TIMSIEVE_FAIL) {
            if(err && *err) {
               syslog(LOG_WARNING,"autocreate_sieve: Error while parsing script %s.",err);
               free(err);
            } else
                syslog(LOG_WARNING,"autocreate_sieve: Error while parsing script");

            unlink(sieve_bctmpname);
            fclose(in_stream);
            close(out_fd);
            return 1;
        }

        /* generate the bytecode */
        if(sieve_generate_bytecode(&bc, s) == TIMSIEVE_FAIL) {
            syslog(LOG_WARNING,"autocreate_sieve: problem compiling sieve script");
            /* removing the copied script and cleaning up memory */
            unlink(sieve_bctmpname);
            sieve_script_free(&s);
            fclose(in_stream);
            close(out_fd);
            return 1;
        }

        if(sieve_emit_bytecode(out_fd, bc) == TIMSIEVE_FAIL) {
            syslog(LOG_WARNING,"autocreate_sieve: problem emiting sieve script");
            /* removing the copied script and cleaning up memory */
            unlink(sieve_bctmpname);
            sieve_free_bytecode(&bc);
            sieve_script_free(&s);
            fclose(in_stream);
            close(out_fd);
            return 1;
        }

        /* clean up the memory */
        sieve_free_bytecode(&bc);
        sieve_script_free(&s);
    }

    xclose(out_fd);
    rewind(in_stream);

    /* Copy the initial script */
    oldmask = umask(077);
    if((out_fp = fopen(sieve_tmpname, "w")) == NULL) {
        syslog(LOG_WARNING,"autocreate_sieve: Unable to open destination sieve script %s: %m", sieve_tmpname);
        unlink(sieve_bctmpname);
        umask(oldmask);
        fclose(in_stream);
        return 1;
    }
    umask(oldmask);

    while((r = fread(buf,sizeof(char), sizeof(buf), in_stream)) > 0) {
        if( fwrite(buf,sizeof(char), r, out_fp) != (unsigned)r) {
            syslog(LOG_WARNING,"autocreate_sieve: Problem writing to sieve script file %s: %m",sieve_tmpname);
            fclose(out_fp);
            unlink(sieve_tmpname);
            unlink(sieve_bctmpname);
            fclose(in_stream);
            return 1;
        }
    }

    if(feof(in_stream)) {
        fclose(out_fp);
        fclose(in_stream);
    } else { /* ferror */
        fclose(out_fp);
        unlink(sieve_tmpname);
        unlink(sieve_bctmpname);
        fclose(in_stream);
        return 1;
    }

    /* Renaming the necessary stuff */
    if(rename(sieve_tmpname, sieve_script_name)) {
        unlink(sieve_tmpname);
        unlink(sieve_bctmpname);
        return 1;
    }

    if(rename(sieve_bctmpname, sieve_bcscript_name)) {
        unlink(sieve_bctmpname);
        unlink(sieve_bcscript_name);
        return 1;
    }

    /* end now with the symlink */
    if(symlink(sieve_bclink_name, sieve_default)) {
        if(errno != EEXIST) {
            syslog(LOG_WARNING, "autocreate_sieve: problem making the default link (symlink: %m).");
            /* Lets delete the files */
            unlink(sieve_script_name);
            unlink(sieve_bcscript_name);
        }
    }

    /*
     * If everything has succeeded AND we have compiled the script AND we have requested
     * to generate the global script so that it is not compiled each time then we create it.
     */
    if(do_compile &&
          config_getswitch(IMAPOPT_AUTOCREATE_SIEVE_SCRIPT_COMPILE)) {

        if(!compiled_source_script) {
            syslog(LOG_WARNING, "autocreate_sieve: To save a compiled sieve script, autocreate_sieve_compiledscript must have been defined in imapd.conf");
            return 0;
        }

        if(snprintf(sieve_tmpname, MAX_FILENAME, "%s.NEW", compiled_source_script) >= MAX_FILENAME)
            return 0;

        /*
         * Copy everything from the newly created bc sieve sieve script.
         */
        if((in_fd = open(sieve_bcscript_name, O_RDONLY))<0) {
            return 0;
        }

        if((out_fd = open(sieve_tmpname, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) < 0) {
            if(errno == EEXIST) {
               /* Someone is already doing this so just bail out. */
               syslog(LOG_WARNING, "autocreate_sieve: %s already exists. Some other instance processing it, or it is left over", sieve_tmpname);
                close(in_fd);
                return 0;
            } else if (errno == EACCES) {
                syslog(LOG_WARNING,"autocreate_sieve: No access to create file %s. Check permissions",sieve_tmpname);
                close(in_fd);
                return 0;
            } else {
                syslog(LOG_WARNING,"autocreate_sieve: Unable to create %s: %m",sieve_tmpname);
                close(in_fd);
                return 0;
            }
        }

        while((r = read(in_fd, buf, sizeof(buf))) > 0) {
            if((k = write(out_fd,buf,r)) < 0) {
                syslog(LOG_WARNING, "autocreate_sieve: Error writing to file: %s: %m", sieve_tmpname);
                close(out_fd);
                close(in_fd);
                unlink(sieve_tmpname);
                return 0;
           }
        }

        if(r == 0 ) { /*EOF */
            xclose(out_fd);
            xclose(in_fd);
        } else if (r < 0) {
                syslog(LOG_WARNING, "autocreate_sieve: Error reading file: %s: %m", sieve_bcscript_name);
                xclose(out_fd);
                xclose(in_fd);
                unlink(sieve_tmpname);
                return 0;
        }

        /* Rename the temporary created sieve script to its final name. */
        if(rename(sieve_tmpname, compiled_source_script)) {
            if(errno != EEXIST) {
               unlink(sieve_tmpname);
               unlink(compiled_source_script);
        }
            return 0;
        }

        syslog(LOG_NOTICE, "autocreate_sieve: Compiled sieve script was successfully saved in %s", compiled_source_script);
    }

    return 0;
}
コード例 #3
0
ファイル: actions.c プロジェクト: ajwans/cyrus-imapd
/* save name as a sieve script */
int putscript(struct protstream *conn, mystring_t *name, mystring_t *data,
	      int verify_only)
{
  FILE *stream;
  char *dataptr;
  char *errstr;
  int lup;
  int result;
  char path[1024], p2[1024];
  char bc_path[1024], bc_p2[1024];
  int maxscripts;
  sieve_script_t *s;

  result = scriptname_valid(name);
  if (result!=TIMSIEVE_OK)
  {
      prot_printf(conn,"NO \"Invalid script name\"\r\n");
      return result;
  }

  if (verify_only)
      stream = tmpfile();

  else {
      /* see if this would put the user over quota */
      maxscripts = config_getint(IMAPOPT_SIEVE_MAXSCRIPTS);

      if (countscripts(string_DATAPTR(name))+1 > maxscripts)
      {
	  prot_printf(conn,
		      "NO (\"QUOTA\") \"You are only allowed %d scripts on this server\"\r\n",
		      maxscripts);
	  return TIMSIEVE_FAIL;
      }

      snprintf(path, 1023, "%s.script.NEW", string_DATAPTR(name));

      stream = fopen(path, "w+");
  }


  if (stream == NULL) {
      prot_printf(conn, "NO \"Unable to open script for writing (%s)\"\r\n",
		  path);
      return TIMSIEVE_NOEXIST;
  }

  dataptr = string_DATAPTR(data);

  for (lup=0;lup<= data->len / BLOCKSIZE; lup++) {
      int amount = BLOCKSIZE;

      if (lup*BLOCKSIZE+BLOCKSIZE > data->len)
	  amount=data->len % BLOCKSIZE;

      fwrite(dataptr, 1, amount, stream);
      
      dataptr += amount;
  }

  /* let's make sure this is a valid script
     (no parse errors)
  */
  result = is_script_parsable(stream, &errstr, &s);

  if (result != TIMSIEVE_OK) {
      if (errstr && *errstr) { 
	  prot_printf(conn, "NO {" SIZE_T_FMT "}\r\n%s\r\n",
		      strlen(errstr), errstr);
	  free(errstr);
      } else {
	  if (errstr) free(errstr);
	  prot_printf(conn, "NO \"parse failed\"\r\n");
      }
      fclose(stream);
      unlink(path);
      return result;
  }

  fflush(stream);
  fclose(stream);
  
  if (!verify_only) {
      int fd;
      bytecode_info_t *bc;
      
      /* Now, generate the bytecode */
      if(sieve_generate_bytecode(&bc, s) == -1) {
	  unlink(path);
	  sieve_script_free(&s);
	  prot_printf(conn, "NO \"bytecode generate failed\"\r\n");
	  return TIMSIEVE_FAIL;
      }

      /* Now, open the new file */
      snprintf(bc_path, 1023, "%s.bc.NEW", string_DATAPTR(name));
      fd = open(bc_path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
      if(fd < 0) {
	  unlink(path);
	  sieve_free_bytecode(&bc);
	  sieve_script_free(&s);
	  prot_printf(conn, "NO \"couldn't open bytecode file\"\r\n");
	  return TIMSIEVE_FAIL;
      }
	  
      /* Now, emit the bytecode */
      if(sieve_emit_bytecode(fd, bc) == -1) {
	  close(fd);
	  unlink(path);
	  unlink(bc_path);
	  sieve_free_bytecode(&bc);
	  sieve_script_free(&s);
	  prot_printf(conn, "NO \"bytecode emit failed\"\r\n");
	  return TIMSIEVE_FAIL;
      }

      sieve_free_bytecode(&bc);
      sieve_script_free(&s);

      close(fd);

      /* Now, rename! */
      snprintf(p2, 1023, "%s.script", string_DATAPTR(name));
      snprintf(bc_p2, 1023, "%s.bc", string_DATAPTR(name));
      rename(path, p2);
      rename(bc_path, bc_p2);

  }

  prot_printf(conn, "OK\r\n");
  sync_log_sieve(sieved_userid);

  return TIMSIEVE_OK;
}
コード例 #4
0
ファイル: actions.c プロジェクト: JensErat/cyrus-imapd
/* save name as a sieve script */
int putscript(struct protstream *conn, const struct buf *name,
              const struct buf *data, int verify_only)
{
  FILE *stream;
  const char *dataptr;
  char *errstr;
  unsigned int i;
  int last_was_r = 0;
  int result;
  char path[1024], p2[1024];
  char bc_path[1024], bc_p2[1024];
  int maxscripts;
  sieve_script_t *s;

  result = scriptname_valid(name);
  if (result!=TIMSIEVE_OK)
  {
      prot_printf(conn,"NO \"Invalid script name\"\r\n");
      return result;
  }

  if (verify_only)
      stream = tmpfile();

  else {
      /* see if this would put the user over quota */
      maxscripts = config_getint(IMAPOPT_SIEVE_MAXSCRIPTS);

      if (countscripts(name->s)+1 > maxscripts)
      {
          prot_printf(conn,
                      "NO (QUOTA/MAXSCRIPTS) \"You are only allowed %d scripts on this server\"\r\n",
                      maxscripts);
          return TIMSIEVE_FAIL;
      }

      snprintf(path, 1023, "%s.script.NEW", name->s);

      stream = fopen(path, "w+");
  }


  if (stream == NULL) {
      prot_printf(conn, "NO \"Unable to open script for writing (%s)\"\r\n",
                  path);
      return TIMSIEVE_NOEXIST;
  }

  dataptr = data->s;

  /* copy data to file - replacing any lone \r or \n with the
   * \r\n pair so notify messages are SMTP compatible */
  for (i = 0; i < data->len; i++) {
      if (last_was_r) {
          if (dataptr[i] != '\n')
              putc('\n', stream);
      }
      else {
          if (dataptr[i] == '\n')
              putc('\r', stream);
      }
      putc(dataptr[i], stream);
      last_was_r = (dataptr[i] == '\r');
  }
  if (last_was_r)
      putc('\n', stream);


  /* let's make sure this is a valid script
     (no parse errors)
  */
  result = is_script_parsable(stream, &errstr, &s);

  if (result != TIMSIEVE_OK) {
      if (errstr && *errstr) {
          prot_printf(conn, "NO ");
          prot_printstring(conn, errstr);
          prot_printf(conn, "\r\n");
      } else {
          prot_printf(conn, "NO \"parse failed\"\r\n");
      }
      free(errstr);
      fclose(stream);
      unlink(path);
      return result;
  }

  fflush(stream);
  fclose(stream);

  if (!verify_only) {
      int fd;
      bytecode_info_t *bc;

      /* Now, generate the bytecode */
      if(sieve_generate_bytecode(&bc, s) == -1) {
          unlink(path);
          sieve_script_free(&s);
          prot_printf(conn, "NO \"bytecode generate failed\"\r\n");
          return TIMSIEVE_FAIL;
      }

      /* Now, open the new file */
      snprintf(bc_path, 1023, "%s.bc.NEW", name->s);
      fd = open(bc_path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
      if(fd < 0) {
          unlink(path);
          sieve_free_bytecode(&bc);
          sieve_script_free(&s);
          prot_printf(conn, "NO \"couldn't open bytecode file\"\r\n");
          return TIMSIEVE_FAIL;
      }

      /* Now, emit the bytecode */
      if(sieve_emit_bytecode(fd, bc) == -1) {
          close(fd);
          unlink(path);
          unlink(bc_path);
          sieve_free_bytecode(&bc);
          sieve_script_free(&s);
          prot_printf(conn, "NO \"bytecode emit failed\"\r\n");
          return TIMSIEVE_FAIL;
      }

      sieve_free_bytecode(&bc);
      sieve_script_free(&s);

      close(fd);

      /* Now, rename! */
      snprintf(p2, 1023, "%s.script", name->s);
      snprintf(bc_p2, 1023, "%s.bc", name->s);
      rename(path, p2);
      rename(bc_path, bc_p2);

  }

  prot_printf(conn, "OK\r\n");
  sync_log_sieve(sieved_userid);

  return TIMSIEVE_OK;
}